Netty源码分析:服务端启动全过程(篇幅很长)

标签: netty 源码 服务端启动 全过程 服务端开发
2078人阅读 评论(0) 收藏 举报
分类:

Netty源码分析:服务端启动全过程

先说结论,Netty 服务端启动和交互的逻辑的底层实现是借助于Java NIO ServerSocketChannel来实现,Java NIO ServerSocketChannel作为服务端的绑定端口、接受客户端的连接的样式代码如下:

        /*
         * 既然是服务器端,肯定需要一个ServerSocketChannel来监听新进来的TCP连接。
         * */
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //监听指定的端口号
        serverSocketChannel.socket().bind(new InetSocketAddress(9999));

        //检测是否有客户端连接进来
        while(true){
            SocketChannel socketChannel = serverSocketChannel.accept();
            //do  something....
        }
        //在使用完毕后,会进行关闭
        serverSocketChannel.close();

结论已经说完了,或许看完这篇博文,你才会明白这个结论哈,不急,慢慢看。

一般Netty服务端的代码如下所示:

    public final class SimpleServer {

        public static void main(String[] args) throws Exception {
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();

            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .handler(new SimpleServerHandler())
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                            }
                        });

                ChannelFuture f = b.bind(8888).sync();

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

        private static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                System.out.println("channelActive");
            }

            @Override
            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                System.out.println("channelRegistered");
            }

            @Override
            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                System.out.println("handlerAdded");
            }
        }
    }

在前面两篇博文中从源码的角度分析了如下几行代码主要做了哪些工作。

            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup();

            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .handler(new SimpleServerHandler())
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                            }
                        });

本篇博文将从源码的角度分析ChannelFuture f = b.bind(8888).sync() 的内部实现。

这样就完成了Netty服务器端启动过程的源码分析。

源码分析ChannelFuture f = b.bind(8888).sync()

通过b.bind(8888)来启动服务,下面将来进行分析。

AbstractBootstrap.java

    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

其中,new InetSocketAddress(inetPort)对象如下所示:

这个函数没什么好说的,调用了参数为InetSocketAddress的bind方法,该方法代码如下。

    public ChannelFuture bind(SocketAddress localAddress) {
        validate();//相关参数的检查
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);//下面将分析
    } 

该函数主要看两点:validate()和doBind(localAddress)

1、先看validate()方法

    //函数功能:检查相关参数是否设置了
    @SuppressWarnings("unchecked")
    public B validate() {
        if (group == null) {//这里的group指的是:b.group(bossGroup, workerGroup)代码中的bossGroup
            throw new IllegalStateException("group not set");
        }

        if (channelFactory == null) {
            throw new IllegalStateException("channel or channelFactory not set");
        }
        return (B) this;
    } 

该方法主要检查了两个参数,一个是group,一个是channelFactory,在这里可以想一想这两个参数是在哪里以及何时被赋值的?答案是在如下代码块中被赋值的,其中是将bossGroup赋值给了group,将BootstrapChannelFactory赋值给了channelFactory.

     ServerBootstrap b = new ServerBootstrap();
                    b.group(bossGroup, workerGroup)
                            .channel(NioServerSocketChannel.class)

2、接下来看bind方法中的doBind(localAddress)方法

doBind方法的源代码如下:

    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();//1
        final Channel channel = regFuture.channel();//2
        if (regFuture.cause() != null) {
            return regFuture;
        }

        final ChannelPromise promise;
        if (regFuture.isDone()) {
            promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    doBind0(regFuture, channel, localAddress, promise);
                }
            });
        }

        return promise;
    } 

doBind这个函数是我们要分析的重点,这个函数的主要工作有如下几点:

1、通过initAndRegister()方法得到一个ChannelFuture的实例regFuture。

2、通过regFuture.cause()方法判断是否在执行initAndRegister方法时产生来异常。如果产生来异常,则直接返回,如果没有产生异常则进行第3步。

3、通过regFuture.isDone()来判断initAndRegister方法是否执行完毕,如果执行完毕来返回true,然后调用doBind0进行socket绑定。如果没有执行完毕则返回false进行第4步。

4、regFuture会添加一个ChannelFutureListener监听,当initAndRegister执行完成时,调用operationComplete方法并执行doBind0进行socket绑定。

第3、4点想干的事就是一个:调用doBind0方法进行socket绑定。

下面将分成4部分对每行代码具体做了哪些工作进行详细分析。

第1部分: final ChannelFuture regFuture = initAndRegister()

下面将先分析这行代码做了些什么,该方法的具体代码如下:

    final ChannelFuture initAndRegister() {
        //结论:这里的channel为一个NioServerSocketChannel对象,具体分析见后面
        final Channel channel = channelFactory().newChannel();//1
        try {
            init(channel);//2
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = group().register(channel);//3
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        // If we are here and the promise is not failed, it's one of the following cases:
        // 1) If we attempted registration from the event loop, the registration has been completed at this point.
        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
        // 2) If we attempted registration from the other thread, the registration request has been successfully
        //    added to the event loop's task queue for later execution.
        //    i.e. It's safe to attempt bind() or connect() now:
        //         because bind() or connect() will be executed *after* the scheduled registration task is executed
        //         because register(), bind(), and connect() are all bound to the same thread.

        return regFuture;
    } 

通过函数名以及内部调用的函数可以猜测该函数干了两件事情:

1、初始化一个Channel,要想初始化,肯定要先得到一个Channel,是吧。

    final Channel channel = channelFactory().newChannel();//1
    init(channel);//2

2、将Channel进行注册,至于注册到哪里,目前不知道。

    ChannelFuture regFuture = group().register(channel);//3

下面我们将分析这几行代码内部干来些什么。

1.1部分 final Channel channel = channelFactory().newChannel();

在博文Netty源码分析:ServerBootstrap分析中,我们知道b.channel(NioServerSocketChannel.class)的功能为:设置父类属性channelFactory 为: BootstrapChannelFactory类的对象。其中这里BootstrapChannelFactory对象中包括一个clazz属性为:NioServerSocketChannel.class

因此,final Channel channel = channelFactory().newChannel();就是调用的BootstrapChannelFactory类中的newChannel()方法,该方法的具体内容为:

            private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {
                private final Class<? extends T> clazz;

                BootstrapChannelFactory(Class<? extends T> clazz) {
                    this.clazz = clazz;
                }

                @Override
                public T newChannel() {
                    try {
                        return clazz.newInstance();
                    } catch (Throwable t) {
                        throw new ChannelException("Unable to create Channel from class " + clazz, t);
                    }
                }

                @Override
                public String toString() {
                    return StringUtil.simpleClassName(clazz) + ".class";
                }
            }

看到这个类,我们可以得到的结论:final Channel channel = channelFactory().newChannel();这行代码的作用为通过反射产生来一个NioServerSocketChannel类的实例。

下面将看下NioServerSocketChannel类的构造函数做了哪些工作。

NioServerSocketChannel类的继承体系结构如下:

其无参构造函数如下:

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

无参构造函数中SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider()

函数newSocket的功能为:利用SelectorProvider产生一个SocketChannelImpl对象。

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

    public SocketChannel openSocketChannel() throws IOException {
        return new SocketChannelImpl(this);
    }

无参构造函数通过newSocket函数产生了一个SocketChannelImpl对象,然后调用了如下构造函数,我们继续看

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    } 
    //父类AbstractNioMessageChannel的构造函数
    protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent, ch, readInterestOp);
    }   
    //父类 AbstractNioChannel的构造函数
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;//SelectionKey.OP_ACCEPT
        try {
            ch.configureBlocking(false);//设置当前的ServerSocketChannel为非阻塞的
        } 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);
        }
    } 
    //父类AbstractChannel的构造函数
    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        unsafe = newUnsafe();
        pipeline = new DefaultChannelPipeline(this);
    }  

new NioServerSocketChannel()产生一个实例对象时,调用上面这么多构造函数主要干了两件事情:

1、产生来一个SocketChannelImpl类的实例,并设置为非阻塞的。

2、设置了config属性

    config = new NioServerSocketChannelConfig(this, javaChannel().socket()

3、设置SelectionKey.OP_ACCEPT事件

    this.readInterestOp = readInterestOp;//SelectionKey.OP_ACCEPT

4、设置unsafe属性

    @Override
    protected AbstractNioUnsafe newUnsafe() {
        return new NioMessageUnsafe();
    }

主要作用为:用来负责底层的connect、register、read和write等操作。

5、设置pipeline属性

    pipeline = new DefaultChannelPipeline(this);

每个Channel都有自己的pipeline,当有请求事件发生时,pipeline负责调用相应的hander进行处理。

这些属性在后面都会用到,至于NioServerSocketChannel 对象中的unsafe、pipeline属性的具体实现后面进行分析。

结论:final Channel channel = channelFactory().newChannel();这行代码的作用为通过反射产生来一个NioServerSocketChannel类的实例,其中这个NioServerSocketChannel类对象有这样几个属性:SocketChannel、NioServerSocketChannelConfig 、SelectionKey.OP_ACCEPT事件、NioMessageUnsafe、DefaultChannelPipeline

1.2 init(channel)

init方法的具体代码如下:

    @Override
    void init(Channel channel) throws Exception {
        //1、设置新接入channel的option
        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            channel.config().setOptions(options);//NioServerSocketChannelConfig
        }
        //2、设置新接入channel的attr
        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
        //3、设置handler到pipeline上
        ChannelPipeline p = channel.pipeline();
        if (handler() != null) {//这里的handler()返回的就是第二部分.handler(new SimpleServerHandler())所设置的SimpleServerHandler
            p.addLast(handler());
        }

        final EventLoopGroup currentChildGroup = childGroup;
        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()));
        }
        /*
        p.addLast()向serverChannel的流水线处理器中加入了一个 ServerBootstrapAcceptor,
        从名字上就可以看出来,这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器
        */
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new ServerBootstrapAcceptor(
                        currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }

该函数的功能为:

1、设置channel的options

如果没有设置,则options为空,该属性在ServerBootstrap类中的定义如下

    Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

options可能如下:

    public <T> boolean setOption(ChannelOption<T> option, T value) {
        validate(option, value);

        if (option == CONNECT_TIMEOUT_MILLIS) {
            setConnectTimeoutMillis((Integer) value);
        } else if (option == MAX_MESSAGES_PER_READ) {
            setMaxMessagesPerRead((Integer) value);
        } else if (option == WRITE_SPIN_COUNT) {
            setWriteSpinCount((Integer) value);
        } else if (option == ALLOCATOR) {
            setAllocator((ByteBufAllocator) value);
        } else if (option == RCVBUF_ALLOCATOR) {
            setRecvByteBufAllocator((RecvByteBufAllocator) value);
        } else if (option == AUTO_READ) {
            setAutoRead((Boolean) value);
        } else if (option == AUTO_CLOSE) {
            setAutoClose((Boolean) value);
        } else if (option == WRITE_BUFFER_HIGH_WATER_MARK) {
            setWriteBufferHighWaterMark((Integer) value);
        } else if (option == WRITE_BUFFER_LOW_WATER_MARK) {
            setWriteBufferLowWaterMark((Integer) value);
        } else if (option == MESSAGE_SIZE_ESTIMATOR) {
            setMessageSizeEstimator((MessageSizeEstimator) value);
        } else {
            return false;
        }

        return true;
    }

2、设置channel的attrs

如果没有设置,则attrs为空,该属性在ServerBootstrap类中的定义如下

    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();

3、设置handler到channel的pipeline上

其中,这里的handler为:在博文Netty源码分析:ServerBootstrap分析中分析的通过b.handler(new SimpleServerHandler())所设置的SimpleServerHandler对象

4、在pipeline上添加来一个ChannelInitializer对象,其中重写来initChannel方法。该方法通过p.addLast()向serverChannel的流水线处理器中加入了一个 ServerBootstrapAcceptor,
从名字上就可以看出来,这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器

看到这里,我们发现其实init只是初始化了一些基本的配置和属性,以及在pipeline上加入了一个接入器,用来专门接受新连接,并没有启动服务.

1.3 ChannelFuture regFuture = group().register(channel)

前面的分析我们知道group为:NioEvenLoopGroup,其继承MultithreadEventLoopGroup,该类中的register方法如下:

     @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);//调用了NioEvenLoop对象中的register方法,NioEventLoop extends SingleThreadEventLoop
    }   

next()方法的代码如下,其功能为选择下一个NioEventLoop对象。

    @Override
    public EventExecutor next() {
        return chooser.next();//调用MultithreadEventExecutorGroup中的next方法
    } 

在博文Netty源码分析:NioEventLoopGroup分析的分析中,我们在MultithreadEventExecutorGroup类的构造函数中看到:根据线程个数nThreads是否为2的幂次方来选择chooser,其中这两个chooser为: PowerOfTwoEventExecutorChooser、GenericEventExecutorChooser

这两个chooser功能都是一样,这是求余的方式不一样。

next方法返回的是一个NioEvenLoop对象,至于children何时被初始化的,是在MultithreadEventExecutorGroup的构造函数中被初始化的,即 执行EventLoopGroup bossGroup = new NioEventLoopGroup(1);
时被初始化的, 具体可以看博文Netty源码分析:NioEventLoopGroup分析的分析。

    private final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
        @Override
        public EventExecutor next() {
            return children[childIndex.getAndIncrement() & children.length - 1];//利用2的N次方法的特点,使用&求余更快。
        }
    }

    private final class GenericEventExecutorChooser implements EventExecutorChooser {
        @Override
        public EventExecutor next() {
            return children[Math.abs(childIndex.getAndIncrement() % children.length)];
        }
    } 

结论:由于NioEventLoopGroup中维护着多个NioEventLoop,next方法回调用chooser策略找到下一个NioEventLoop,并执行该对象的register方法进行注册。

由于NioEventLoop extends SingleThreadEventLoop,NioEventLoop没有重写该方法,因此看 SingleThreadEventLoop类中的register方法

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

    @Override
    public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        if (promise == null) {
            throw new NullPointerException("promise");
        }

        channel.unsafe().register(this, promise);
        return promise;
    }

在本博文第1部分的NioServerSocketChannel实例化中设置来unsafe属性,具体是调用如下的方法来设置的,因此这里的channel.unsafe()就是NioMessageUnsafe实例。

    @Override
    protected AbstractNioUnsafe newUnsafe() {
        return new NioMessageUnsafe();
    }

NioMessageUnsafe没有重写register方法,NioMessageUnsafe extends AbstractNioUnsafe,AbstractNioUnsafe extends AbstractUnsafe

因此,channel.unsafe().register(this, promise)这行代码调用的是AbstractUnsafe类中的register方法,具体代码如下:

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            //判断该channel是否已经被注册到EventLoop中
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

                //1 将eventLoop设置在NioServerSocketChannel上
            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {//判断当前线程是否为该EventLoop中拥有的线程,如果是,则直接注册,如果不是,则添加一个任务到该线程中
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new OneTimeTask() { //重点
                        @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(promise)方法。基本逻辑为:

1、通过调用eventLoop.inEventLoop()方法判断当前线程是否为该EventLoop中拥有的线程,如果是,则直接注册,如果不是,说明该EventLoop在等待并没有执行权,则进行第二步。

                AbstractEventExecutor.java    
                @Override
                public boolean inEventLoop() {
                    return inEventLoop(Thread.currentThread());
                }

            SingleThreadEventExecutor.java

                @Override
                public boolean inEventLoop(Thread thread) {
                    return thread == this.thread;
                } 

2、既然该EventLoop中的线程此时没有执行权,但是我们可以提交一个任务到该线程中,等该EventLoop的线程有执行权的时候就自然而然的会执行此任务,而该任务负责调用register0方法,这样也就达到了调用register0方法的目的。具体为:任务OneTimeTask子类被提交到NioEventLoop线程中执行,然后调用此任务的run方法,进而调用register0方法,其中promise = new DefaultChannelPromise(channel, this)。

下面看register0这个方法,具体代码如下:

        private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                doRegister();
                registered = true;
                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();//执行完,控制台输出:channelRegistered
                if (isActive()) { //分析
                    pipeline.fireChannelActive();
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

在上面的代码中,是通过调用doRegister()方法完成NioServerSocketChannel的注册,该方法的具体代码如下:

     @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                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 SelectableChannel javaChannel() {
        return ch;
    } 

在本博文的第1部分的NioServerSocketChannel的实例化分析中,我们知道这里的javaChannel()方法返回的ch为实例化NioServerSocketChannel时产生的一个SocketChannelImpl类的实例,并设置为非阻塞的,具体见本博文的第1部分。

selectionKey = javaChannel().register(eventLoop().selector, 0, this);就完成了ServerSocketChannel注册到Selector中。

回顾下,这里的eventLoop().selector是什么?答案是:KQueueSelectorImpl对象。

    NioEventLoop(NioEventLoopGroup parent, ThreadFactory threadFactory, SelectorProvider selectorProvider) {
        super(parent, threadFactory, false);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        provider = selectorProvider;
        selector = openSelector();
    }

    private Selector openSelector() {
        final Selector selector;
        try {
            selector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }
        //...省略了一部分代码
        return selector;
    } 

通过回溯NioEventLoop到NioEventLoopGroup构造函数中,我们知道这里的provider是通过调用SelectorProvider.provider()产生的new KQueueSelectorProvider() 对象,该类KQueueSelectorProvider中的openSelector()方法的代码如下:

    public AbstractSelector openSelector() throws IOException {
        return new KQueueSelectorImpl(this);
    }  

ServerSocketChannel注册完之后,接着执行pipeline.fireChannelRegistered方法。

    @Override
    public ChannelHandlerContext fireChannelRegistered() {
        final AbstractChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRegistered();
        } else {
            executor.execute(new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeChannelRegistered();
                }
            });
        }
        return this;
    }

    private void invokeChannelRegistered() {
        try {
            ((ChannelInboundHandler) handler()).channelRegistered(this);//调用我们自己写的Handler然后通过.handler设置后Handler
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }  

pipeline中维护了handler链表,还记得之前.handler(new SimpleServerHandler())初始化的handler在本博文的第1.2部分的分析中介绍了此handler被添加到此pipeline中了,通过遍历链表,执行InBound类型handler的channelRegistered方法,最终执行init中添加的ChannelInitializer handler。

因此执行到这里,我们的控制台就回输出:channelRegistered,这行信息。

到这里,我们就将doBind方法final ChannelFuture regFuture = initAndRegister();给分析完了,得到的结论如下:

1、通过反射产生了一个NioServerSocketChannle对象。

2、完成了初始化

3、将NioServerSocketChannel进行了注册。

接下来我们分析doBind方法的剩余部分代码主要做了什么,

源代码如下:

    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();//1
        final Channel channel = regFuture.channel();//2
        if (regFuture.cause() != null) {
            return regFuture;
        }

        final ChannelPromise promise;
        if (regFuture.isDone()) {
            promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    doBind0(regFuture, channel, localAddress, promise);
                }
            });
        }

        return promise;
    } 

第二部分:doBind0(regFuture, channel, localAddress, promise);

其中regFuture为:DefaultChannelPromise,channel:NioServerSocketChannel

    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

该函数主要是提交了一个Runnable任务到NioEventLoop线程中来进行处理。,这里先看一下NioEventLoop类的execute方法

    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }

        boolean inEventLoop = inEventLoop();//判断当前线程是否为该NioEventLoop所关联的线程,如果是,则添加任务到任务队列中,如果不是,则先启动线程,然后添加任务到任务队列中去
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            //如果
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

当提交的任务被线程执行后,则会执行channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE)这行代码,这行代码完成的功能为:实现channel与端口的绑定。

具体如下:

    AbstractChannel.java    

    @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }

在该方法中直接调用了pipeline的bind方法,这里的pipeline时DefaultChannelPipeline的实例。

 DefaultChannelPipeline.java 

    @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }

在上面方法中直接调用了TailContext实例tail的bind方法,tail在博文 Netty源码分析:ChannelPipeline中有详细的介绍。

继续看tail实例的bind方法

    AbstractChannelHandlerContext.java   

    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        //...省略有效性检查

        final AbstractChannelHandlerContext next = findContextOutbound();//
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }

        return promise;
    }

此上面bind函数中的这行代码:final AbstractChannelHandlerContext next = findContextOutbound();所完成的任务就是在pipeline所持有的以AbstractChannelHandlerContext为节点的双向链表中从尾节点tail开始向前寻找第一个outbound=true的handler节点。

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

阅读博文 Netty源码分析:ChannelPipeline中我们知道:在 DefaultChannelPipeline 的构造器中, 会实例化两个对象: head 和 tail, 并形成了双向链表的头和尾。 head 是 HeadContext 的实例, 它实现了 ChannelOutboundHandler 接口, 并且它的 outbound 字段为 true.而tail 是 TailContext 的实例,它实现了ChannelInboundHandler 接口,并且其outbound 字段为 false,inbound 字段为true。 基于此在如上的bind函数中调用了 findContextOutbound方法 找到的 AbstractChannelHandlerContext 对象其实就是 head.

继续看,在pipelie的双向链表中找到第一个outbound=true的AbstractChannelHandlerContext节点head后,然后调用此节点的invokeConnect方法,该方法的代码如下:

    private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

HeadContext类中的handler()方法代码如下:

        @Override
        public ChannelHandler handler() {
            return this;
        }

该方法返回的是其本身,这是因为HeadContext由于其继承AbstractChannelHandlerContext以及实现了ChannelHandler接口使其具有Context和Handler双重特性。

继续看,看HeadContext类中的bind方法,代码如下:

        @Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }

unsafe这个字段是在HeadContext构造函数中被初始化的,如下:

         HeadContext(DefaultChannelPipeline pipeline) {
            super(pipeline, null, HEAD_NAME, false, true);
            unsafe = pipeline.channel().unsafe();
        }

而此构造函数中的pipeline.channel().unsafe()这行代码返回的就是在本博文前面研究NioServerSocketChannel这个类的构造函数中所初始化的一个实例,如下:

    unsafe = newUnsafe();//newUnsafe()方法返回的是NioMessageUnsafe对象。  

接下来看NioMessageUnsafe类中的bind方法(准确来说:该方法在AbstractUnsafe中),该类bind具体方法代码如下:

        @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
                //...省略了部分代码
            boolean wasActive = isActive();
            try {
                doBind(localAddress);//核心代码
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }

            if (!wasActive && isActive()) {
                invokeLater(new OneTimeTask() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

上面的核心代码就是:doBind(localAddress);需要注意的是,此doBind方法是在NioServerSocketChannel类中的doBind方法,不是其他类中的。

NioServerSocketChannel类中的doBind方法代码如下:

    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }

看到这里,我们就很亲切了哈,

上面方法中javaChannel()方法返回的是NioServerSocketChannel实例初始化时所产生的Java NIO ServerSocketChannel实例(更具体点为ServerSocketChannelImple实例)。 等价于语句serverSocketChannel.socket().bind(localAddress)完成了指定端口的绑定,这样就开始监听此端口。

如果对Java NIO层面的服务端绑定端口对端口的监听、客户端的连接以及交互不太清晰,可以看博文Java NIO 之 ServerSocketChannel/SocketChannel ,这里介绍了 Java NIO 层面的服务端对端口的监听、客户端与服务端之间的连接以及客户端于服务端之间的交互。

说到这里本来应该就结束了,但是还有如下的一点需要说明:即绑定端口成功后,是这里调用了我们自定义handler的channelActive方法。更多可以结合博文:

这样就真正的完成了端口的绑定。在绑定之前,isActive()方法返回false,绑定之后返回true。

    @Override
    public boolean isActive() {
        return javaChannel().socket().isBound();
    }

这样,就进入了如下的if条件的代码块中

            if (!wasActive && isActive()) {
                invokeLater(new OneTimeTask() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }    

        private void invokeLater(Runnable task) {
            try {
                    //省略了部分代码
                eventLoop().execute(task);
            } catch (RejectedExecutionException e) {
                logger.warn("Can't invoke task later as EventLoop rejected it", e);
            }
        }

进而开始执行 pipeline.fireChannelActive();这行代码 ,这行代码的具体调用链如下所示:

DefaultChannelPipeline.java

     @Override
    public ChannelPipeline fireChannelActive() {
        head.fireChannelActive();

        if (channel.config().isAutoRead()) {
            channel.read();
        }

        return this;
    }

    @Override
    public ChannelHandlerContext fireChannelActive() {
        final AbstractChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
            executor.execute(new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeChannelActive();
                }
            });
        }
        return this;
    }

    private void invokeChannelActive() {
        try {
            ((ChannelInboundHandler) handler()).channelActive(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }

上面3个函数的具体解析在博文Netty源码分析:ChannelPipeline有详细介绍,这里不在介绍。

总结

到这里就这样大致的稀里糊涂的从源码的角度给梳理了一下,在跟源码的过程中,确实由于其复杂性,有时候跟到某一个节点时就想撤出来,但撤出来后发现后面又牵扯着前面很多东西,因此就这样反反复复的看、反反复复的记录,特别是在记录成文章时,又感觉很多东西都没有彻底给弄清楚说明白。

目前就这样了,在以后自己的学习中,再逐渐以其他博文中来完善吧,如有错误,请批评指正哈。

总结2

在梳理完Netty源码分析:ChannelPipeline和博文Netty源码分析:客户端连接这两块知识点之后,又重新梳理了一下整个过程,发现又理清了很多东西。

所以在我们在源码的过程中,如果遇到此时不懂的东西,就跳过,这是因为一个复杂的系统牵扯的东西实在太多太多了,当你研究其他模块的时候或许就懂了。

查看评论

netty源码分析(十七)Netty线程模型深度解读与架构设计原则

上次分析到:public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements Event...
  • wzq6578702
  • wzq6578702
  • 2017-11-05 15:19:09
  • 832

netty源码解析

背景 netty 是一个异步事件驱动的网络通信层框架,其官方文档的解释为 Netty is a NIO client server framework which enables quick ...
  • lu930124
  • lu930124
  • 2017-07-30 18:20:55
  • 334

Netty4.x源码分析详解

  • 2016年06月08日 11:31
  • 387KB
  • 下载

Netty 4源码解析:请求处理

Netty 4源码解析:请求处理通过之前《Netty 4源码解析:服务端启动》的分析,我们知道在最前端“扛压力”的是NioEventLoop.run()方法。我们指定创建出的NioServerSock...
  • dc_726
  • dc_726
  • 2015-08-29 08:14:51
  • 7112

netty4.0.x源码分析—channel

备注:本文的分析基于netty 4.0.9final版本,仅对Nio进行分析,因为本人对Socket编程比较感兴趣。 1、channel总体机构图 nio channel的总体结构图如下: 2、关键...
  • pingnanlee
  • pingnanlee
  • 2013-09-23 15:33:27
  • 12918

Netty 源码分析(一)

Netty 实现通信的步骤: 1.创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写。 2.创建一个ServerBootstrap对象,配置Netty的一...
  • lm324114
  • lm324114
  • 2017-10-14 12:39:35
  • 729

一起学Netty(十八)netty源码学习之netty server端源码初读(上)

1)Netty的server端代码一开始初始化了两个EventLoopGroup,其实就是初始化EventLoop,每一个EventLoop的具体实现就是维护了一个任务队列,一个延迟任务队列,一个th...
  • linuu
  • linuu
  • 2016-07-27 19:03:37
  • 3880

Netty5.0架构剖析和源码解读

  • 2016年05月14日 23:15
  • 3.41MB
  • 下载

netty 源码分析一

netty 源码
  • zhuyijian135757
  • zhuyijian135757
  • 2014-07-27 23:31:27
  • 2645

Netty源码分析之DelimiterBasedFrameDecoder

DelimiterBasedFrameDecoder可以接受多个分隔符,比LineBasedFrameDecoder功能强大。实际上,DelimiterBasedFrameDecoder有一个成员变量...
  • shifeng2400
  • shifeng2400
  • 2015-08-26 16:49:19
  • 888
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 83万+
    积分: 1万+
    排名: 1211
    联系方式
    有问题欢迎留言哈
    最新评论