Netty源码之服务端启动

上个学期自己也读过Netty的源码,但那个时候每想到通过博客的方式把它给记录下来,以至于,几个月过去后自己也忘得差不多了,所以打算重新读一遍Netty的源码,并把读的过程给记录下来,下面就开始吧:服务端启动流程:

public static void main(String[] args) throws Exception {
		//一组boss线程,用来监听客户端的连接请求
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //worker线程,服务端用来处理客户端的逻辑
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
        	//新建一个启动器
            ServerBootstrap b = new ServerBootstrap();
            //添加boss线程和worker线程
            b.group(bossGroup, workerGroup)
            		//设定channel的类型
                    .channel(NioServerSocketChannel.class)
                    //设定新连接的属性
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    //添加一个处理器
                    .handler(new ServerHandler())
                    //添加一组channel的处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new AuthHandler());
                            //..

                        }
                    });
			//绑定端口并启动
            ChannelFuture f = b.bind(8888).sync();

            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

上面的主程序就是启动一个简单的Netty服务端的流程,我们把Netty的服务端启动分为一下四步:
1、创建服务端Channel
2、初始化服务端Channel
3、注册selector
4、端口绑定
接下来,从主程序的bind方法开始,我们一步一步的跟踪:

 public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }

这个方法最终调用了doBind方法:

private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
       ...
    }

我们首先看第一步,创建服务端Channel,就是上面initAndRegister方法,我们进入这个方法深入看一下实现的过程:

 final ChannelFuture initAndRegister() {
        Channel channel = null;
        //创建一个服务端channel
        channel = channelFactory.newChannel();
        //初始化channel
         init(channel);
  		...
  		//注册channel
        ChannelFuture regFuture = config().group().register(channel);
        return regFuture;
    }

这个方法一共做了三件事:
1、创建一个服务端channel
2、初始化channel
3、注册channel
先看一下创建服务端channel:

 channel = channelFactory.newChannel();

我们发现这行代码是用一个工厂来实例化了一个channel,那这个channelFactory是在哪里初始化的呢?既然我们程序没跟踪到,那可能就是在构造方法中创建的,于是我们回到main方法找到了这一行:

channel(NioServerSocketChannel.class)

可能就和这行代码有关,我们走进去看一下

public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }

果然,根据我们传进去的使用的channel的类型创建了一个工厂,作为参数传到了另一个方法中:

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        ...
        this.channelFactory = channelFactory;
        return (B) this;
    }

在这个方法中就将我们创建的channelFactory存到了启动类的属性中。我们继续回到这行代码,开始创建一个channel:

 channel = channelFactory.newChannel();

进入newChannel方法:

 public T newChannel() {
        try {
        	//通过反射实例化channel
            return clazz.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }

这个方法使用我们传入的channel类使用默认的构造方法实例化了channel,难道channel就这么简单的被创建了么?我们进入channel类的默认构造方法中看一下:

public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

默认的构造方法又调用了其他的构造方法,并且传入了参数,我们看一下newSocket方法执行后返回了什么:

SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
 private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
           
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }

这个方法就是通过provider打开了一个channel并返回,那么这个provider是怎么被创建的呢?我们带着好奇心继续进入SelectorProvider.provider()方法:

public static SelectorProvider provider() {
		//加锁
        synchronized (lock) {
        	//如果selector provider不为空直接返回
            if (provider != null)
                return provider;
              //否则就创建一个
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

这个方法就是返回返回此调用的系统范围默认选择器。
现在selectorProvider创建了一个java底层的channel并且传入了另外一个构造方法:

public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

我们继续看它父类的构造方法,并且记住传入的参数:null, channel, SelectionKey.OP_ACCEPT:

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        //继续调用父类的构造方法
        super(parent);
        //保存已创建的java底层channel
        this.ch = ch;
        //保存感兴趣的事件
        this.readInterestOp = readInterestOp;
        try {
        	//设置channel为非阻塞
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }

这个方法保存了我们传入的一些参数,并且继续调用父类的构造方法:

 protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        //创建unsafe类
        unsafe = newUnsafe();
        //创建channelPipeline
        pipeline = newChannelPipeline();
    }

这个构造方法做了三件事:
1、创建了一个id
2、创建了unsafe对象
3、创建了channelPipeline类
我们再继续看一下channelPipeline是如何被创建的:

protected DefaultChannelPipeline(Channel channel) {
		//保存我们常见的服务端channel
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);
		//创建一个头节点和尾节点
        tail = new TailContext(this);
        head = new HeadContext(this);
		//双向链表
        head.next = tail;
        tail.prev = head;
    }

pipeline里面保存了一个双向链表,那些处理器在之后的初始化channel的过程中就会被封装成一个节点被放到这个双向链表中,这些在之后讲这个组件的时候会讲到。我们回到最初的那个构造方法:

public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

分析完父类的构造方法了,接着看第二行代码,应该是创建了当前channel的配置对象,我们就不深究了,继续回到我们分析的进度上,现在分析完了实例化当前channel的构造方法,我们发现服务端的channel实际上是创建了一个Java底层的channel然后封装成了服务端的channel。
继续看下面的流程:

init(channel);

接下来就要对我们创建的channel进行初始化了:

void init(Channel channel) throws Exception {
		//获得channel的配置
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            channel.config().setOptions(options);
        }
		//获得channel的属性
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
		//获得channel的pipeline
        ChannelPipeline p = channel.pipeline();
		//获得worker线程
        final EventLoopGroup currentChildGroup = childGroup;
       //获得我们添加的handler
        final ChannelHandler currentChildHandler = childHandler;
        //获得我们设置的配置和属性
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }
		//为pipeline添加我们在主程序中添加的handler,并且在自动添加一个用来处理accept的handler
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
            	//获得channel的pipeline
                final ChannelPipeline pipeline = ch.pipeline();
                //获得我们配置的handler
                ChannelHandler handler = config.handler();
                //添加到pipeline上
                if (handler != null) {
                    pipeline.addLast(handler);
                }
				//这个handler是当又新的channel注册的时候为他们添加上我们的配置属性
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

这个初始化方法为我们创建的服务端channel设置了配置和属性信息,并且添加了我们配置的handler和一个默认的handler。
这样初始化channel就结束了,接下来是注册selector:

ChannelFuture regFuture = config().group().register(channel);

我们一部分一部分的看,先看一下config()这个方法返回了什么:

public final ServerBootstrapConfig config() {
        return config;
    }

返回的config封装了一个bootStrap,里面的方法都是返回bootStrap各种属性的,比如接下来的:

public final EventLoopGroup group() {
        return bootstrap.group();
    }

返回的是bossGroup,调用他的register方法

public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }

next方法是做什么的呢?我们看一下:

 public EventExecutor next() {
        return chooser.next();
    }

他最终调用了chooser的next方法:

public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }

原来他是按照id轮询来选择一个eventLoop来工作,好,现在又工作的线程了,我们看看注册是怎么实现的:

public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }

他又将我们要注册的channel和选中的工作线程封装成了promise对象传进去:

public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

最终通过unsafe类的方法注册:

  public final void register(EventLoop eventLoop, final ChannelPromise promise) {
           ...
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

            AbstractChannel.this.eventLoop = eventLoop;
			//如果当前的eventLoop绑定的线程就是当前线程,那么就直接调用注册方法
            if (eventLoop.inEventLoop()) {
                register0(promise);
            }
            //否则放到任务队列里
             else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

这个方法最终调用了register0进行注册:

private void register0(ChannelPromise promise) {
          	    ...
          	    //注册
                doRegister();
                ...
                //触发添加channel事件
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                //触发channel注册完成事件
                pipeline.fireChannelRegistered();
                ...
        }

这个方法做了三件事:
1、最终调用了注册方法
2、触发添加channel事件
3、触发channel注册完成事件
我们分别来看一下注册是怎么实现的:

protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
            	//将我们创建的服务端channel注册到eventLoop的selector,注册的感兴趣的事件事无
                selectionKey = javaChannel().register(eventLoop().selector, 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

最后一步就是要绑定端口了

protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

最中调用java底层来绑定,绑定结束后会触发事件并且执行方法:

private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }

最终会调用:

public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }

我们进去看一下:

protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

为我们的服务端channel设置上对读感兴趣,也就是对accept感兴趣的事件。
这样服务端就启动起来了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值