Netty4.x源码分析:服务端绑定端口

本文会详细解析Netty绑定端口源码的每一个流程,不过整个过程涉及到许多的异步调用,整个逻辑过程有点绕,非战斗人员谨慎阅读。。

引包

 		<dependency>
		    <groupId>io.netty</groupId>
		    <artifactId>netty-all</artifactId>
		    <version>4.1.6.Final</version>
		</dependency>

绑定端口模板代码

			NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
			NioEventLoopGroup workGroup= new NioEventLoopGroup(1);
			ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup)
                           .handler(new LoggingHandler())
                           .channel(NioServerSocketChannel.class)
                           .option(ChannelOption.SO_BACKLOG, 128)
                           .childHandler(new ChannelInitializer<Channel>() {
    
                            @Override
                            protected void initChannel(Channel ch) throws Exception {
                                // 这里添加自己的业务处理Handler
                                ch.pipeline().addLast(new TestHandler());
                            }
                        });
             ChannelFuture f = serverBootstrap.bind(8080).sync();

源码分析

1、创建ServerBoostrap对象,进行一些简单的配置

1、设置group

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }
@SuppressWarnings("unchecked")
    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return (B) this;
    }

这里我们记一下用于接受客户端Channel连接的Group变量称为group,另一个用于处理客户端读写的Group称为childGroup,以防后面混淆
2、设置handler

 @SuppressWarnings("unchecked")
    public B handler(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
        return (B) this;
    }

3、设置channel

public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }
 public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return (B) this;
    }

4、设置option

    @SuppressWarnings("unchecked")
    public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
                options.put(option, value);
            }
        }
        return (B) this;
    }

5、设置childHandler

 public ServerBootstrap childHandler(ChannelHandler childHandler) {
        if (childHandler == null) {
            throw new NullPointerException("childHandler");
        }
        this.childHandler = childHandler;
        return this;
    }

2、调用bind()进行端口绑定

ChannelFuture f = serverBootstrap.bind(8080).sync();
AbstractBootstrap.java
public ChannelFuture bind(int inetPort) {
	return bind(new InetSocketAddress(inetPort));
}
AbstractBootstrap.java
public ChannelFuture bind(SocketAddress localAddress) {
	validate();
	if (localAddress == null) {
	    throw new NullPointerException("localAddress");
	}
	return doBind(localAddress);
}
  • 首先调用validate()进行一些常规的校验
 	 public ServerBootstrap validate() {
        super.validate();
        if (childHandler == null) {
            throw new IllegalStateException("childHandler not set");
        }
        if (childGroup == null) {
            logger.warn("childGroup is not set. Using parentGroup instead.");
            childGroup = config.group();
        }
        return this;
    }
public B validate() {
        if (group == null) {
            throw new IllegalStateException("group not set");
        }
        if (channelFactory == null) {
            throw new IllegalStateException("channel or channelFactory not set");
        }
        return (B) this;
    }
  • 然后调用doBind(),doBind()是整个绑定端口流程的核心方法
   private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the 
                        // ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

这里我们可以拆分为几个代码片段,然后依次分析

  • 调用initAndRegister(),初始化channel,并且注册channel到NioEventLoop中的Selector上(NioEventLoopGroup源码分析中我们说过,每个NioEventLoop中都含有一个Selector,用于监听客户端的Channel)
final ChannelFuture regFuture = initAndRegister();
  • 判断regFuture是否已经完成,如果已经完成直接调用doBind0(),如果没有完成,则往regFuture添加一个监听器
 		if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
						romise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }

1、首先我们看下initAndRegister()方法

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                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 = config().group().register(channel);
        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;
    }

这里我们也可以把initAndRegister()方法拆分为几个代码块,进行依次分析

  • 通过channelFactory.newChannel(),去实例化Channel(实际上是NioServerSocketChannel),就是之前我们调用ServerBootstrap.channel(NioServerSocketChannel.class)进行配置的
  • 调用init(channel),去初始化channel
  • config().group().register(channel),往NioEventLoop上注册channel

首先我们看channelFactory.newChannel(),很显然这个channelFactory就是ReflectiveChannelFactory

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

通过反射去构建NioServerSocketChannel对象,紧接着我们来看NioServerSocketChannel的构造函数

 	private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
	/**
     * Create a new instance
     */
    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SEL	ECTOR_PROVIDER));
    }
private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }

这里我们很熟悉的Nio构建ServerSocketChannel的过程,通过SelectorProvider.openServerSocketChannel()去构建。

这里可能有些人会混淆ServerSocketChannel和NioServerSocketChannel的关系,ServerSocketChannel是Java Nio包下的类,NioServerSocketChannel是Netty包下的类,NioServerSocketChannel包装了ServerSocketChannel,并且增加了pipeline等功能。

接下来是一些NioServerSocketChannel构造函数调用和NioServerSocketChannel父类的构造函数调用

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

创建一个NioServerSocketChannelConfig,主要用于向外展示NioServerSocketChannel的一些配置信息

protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
	super(parent, ch, readInterestOp);
}

这个readInterestOp变量是SelectionKey.OP_ACCEPT枚举的值,这里我们要加深下印象,后面会用到这个变量

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            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);
        }
    }

OK,这里又遇到我们很熟悉的NIO的API,调用SelectableChannel.configureBlocking(false),把ServerSocketChannel设置为非阻塞

    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

这里是NioServerSocketChannel的最终构造,主要可以分为3点

  • 调用newId(),生成一个全局Id,这个我们不讲解
  • 调用newUnsafe(),生成NioMessageUnsafe对象,这个对象很关键,包括后面的NioServerSocketChannel往Selector注册ACCEPT事件、NioServerSocketChannel绑定端口都需要用到这个对象。
  • 调用newChannelPipeline(),生成DefaultChannelPipeline对象,这个ChannelPipeline我们很熟悉,就是Netty非常出名的调用链对象。

首先我们看一下newUnsafe()方法,在AbstractNioMessageChannel类下

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

NioMessageUnsafe对象是一个空构造,所以我们就不往里面看了,我们继续往下看newChannelPipeline()方法

    protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }
	protected DefaultChannelPipeline(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;
    }

由上面代码我们可以了解到ChannelPipeline的构造过程,总结为以下几点

  • 每个channelpipeline都和一个channel绑定,不管是NioServerSocketChannel还是NioSocketChannel。当然我们这里的这个channel是NioServerSocketChannel
  • ChannelPipeline它是一个双向循环链表结构,链表中的元素为AbstractChannelHandlerContext对象,那为什么我们往ChannelPipeline中添加的是ChannelHandler对象?因为每次用户添加ChannelHandler对象的时候,Netty会自动把ChannelHandler包装成AbstractChannelHandlerContext对象,在插入链表中。
  • 初始化过程中,会自动创建头结点和尾结点,分别为HeadContext和TailContext

此时channelpipeline的双向链表结构如下:
Netty4.x源码分析:服务端绑定端口_第1张图片

到此为止NioServerSocketChannel的对象构造完毕。

回看下initAndRegister()方法拆分的代码块
Netty4.x源码分析:服务端绑定端口_第2张图片

channel实例化后,紧接着调用init(channel),进行channel初始化,以下提到的channel,基本都是NioServerSocketChannel

void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            channel.config().setOptions(options);
        }

        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());
            }
        }

        ChannelPipeline p = channel.pipeline();

        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(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
                // In this case the initChannel(...) method will only be called after this method returns. Because
                // of this we need to ensure we add our handler in a delayed fashion so all the users handler are
                // placed in front of the ServerBootstrapAcceptor.
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }
  • 进行一些option的配置,也就是调用ServerBootstrap.option()设置的属性
  • 进行一些attr的配置,也就是调用ServerBootstrap.attr()设置的属性
  • 获取channel的pipeline,调用pipeline.addLast()往pipeline添加一个ChannelInitializer对象,ChannelInitializer对象的initChannel()并不会马上执行,什么时候执行,后面会说。

我们看一下pipeline的addLast()方法

@Override
    public final ChannelPipeline addLast(ChannelHandler... handlers) {
        return addLast(null, handlers);
    }
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers");
        }

        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, null, h);
        }

        return this;
    }
@Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

我们可以把addLast()拆分成几个关键的代码块,然后依次分析

  • 调用newCtx = newContext(group, filterName(name, handler), handler),把handler包装成AbstractChannelHandlerContext对象
  • 调用addLast0(newCtx),把上面生成的AbstractChannelHandlerContext对象添加到双向链表中
 private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }
  • 如果该channel还没有注册,那么调用callHandlerCallbackLater(newCtx,true),延迟执行该AbstractChannelHandlerContext的handlerAdded()方法,即把该AbstractChannelHandlerContext加入一个单链表中,那么一旦该channel注册到NioEventLoop中,就会马上执行该单链表上的AbstractChannelHandlerContext的handlerAdded方法(很显然,我们现在channel还没有注册,所以会加入到单链表中)
if (!registered) {
	newCtx.setAddPending();
	 callHandlerCallbackLater(newCtx, true);
	 return this;
}
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
        assert !registered;
        PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
        // pendingHandlerCallbackHead是单链表结构
        PendingHandlerCallback pending = pendingHandlerCallbackHead;
        if (pending == null) {
            pendingHandlerCallbackHead = task;
        } else {
            // Find the tail of the linked-list.
            while (pending.next != null) {
                pending = pending.next;
            }
            pending.next = task;
        }
    }
  • 调用callHandlerAdded0(newCtx),即执行AbstractChannelHandlerContext的handlerAdded()方法
    – 为什么上面说延迟执行handlerAdded(),等到注册完后在执行,这里又说要执行handlerAdded()?因为有些AbstractChannelHandlerContext的handlerAdded()是不需要注册后才执行的(也就是不需要延迟执行)。因为我们这里添加的是ChannelInitializer对象,我们看下ChannelInitializer源码便清楚。
callHandlerAdded0(newCtx);
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
        try {
            ctx.handler().handlerAdded(ctx);
            ctx.setAddComplete();
        } catch (Throwable t) {
           ...省略
        }
    }

ChannelInitializer类的handlerAdded()

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // This should always be true with our current DefaultChannelPipeline implementation.
            // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
            // suprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
            // will be added in the expected order.
            initChannel(ctx);
        }
    }

这里就很显然了,这里首先获取了channel,并且判断了该channel是否注册到NioEventLoop,很显然,目前来说还没有,所以initChannel(ctx)方法并没有执行。

那么此时pipeline中的数据结构如下图:
Netty4.x源码分析:服务端绑定端口_第3张图片

到此为止channelpipe.addLast()方法就分析完毕了,而且init(channel)也走完了

回看下initAndRegister()方法拆分的代码块
Netty4.x源码分析:服务端绑定端口_第4张图片

init(channel)也走完后,那么紧接着是分析config().group().register(channel),config().group()获取到的是group(与childGroup区分,忘记了,往前面看调用ServerBootstrap.group()方法有说)

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

首先调用了next(),获取一个NioEventLoop(讲NioEventLoopGroup源码分析的时候,说到一个选择器,这时候就要用上它了,如果忘了,在回去看)

MultithreadEventLoopGroup.java
    public EventLoop next() {
        return (EventLoop) super.next();
    }
MultithreadEventExecutorGroup.java
    public EventExecutor next() {
        return chooser.next();
    }
SingleThreadEventLoop.java
    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }

这里创建了DefaultChannelPromise对象,很显然等一下会使用到异步调用

SingleThreadEventLoop.java
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("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;
            }

            AbstractChannel.this.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);
                }
            }
        }
  • 首先调用AbstractChannel.this.eventLoop = eventLoop;在channel设置NioEventLoop,标记该Channel是由那个NioEventLoop来管理的
  • if(eventLoop.inEventLoop()){…},判断当前线程是否在NioEventLoop线程中,如果在,那么直接在同步调用register0(promise)方法,如果不在,那么把register0(promise)方法包装一个Task放入NioEventLoop的队列中,让NioEventLoop线程池异步执行。【很显然,当前我们还在Main线程中,不在NioEventLoop这个线程中,所以它会先返回结果,在异步执行register0(promise)】

因为是异步执行的register0(promise)方法,所以我们暂且先不跟这个方法,我们先回到调用的起点,也就是initAndRegister()中,

AbstractBootstrap.java
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                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 = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

那么现在initAndRegister()调用也走完了,返回ChannelFuture到doBind(),这个regFuture 就是异步执行register0(promise)中的promise。

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

        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

调用完initAndRegister()方法后

  • 首先判断该future,是否isDone(),如果是,那么直接调用doBind0(regFuture, channel, localAddress, promise);
  • 如果不是,那么往future中加入一个监听器,为了好记我们称它为"RegisterListener",在监听器里面去调用
    doBind0(regFuture, channel, localAddress, promise);

我们这分析的时候future是没有完成的,它会走监听器,具体什么时候调用监听器呢?我们这时候回到刚刚异步执行的register0(promise)方法,这个方法在AbstractUnsafe类下,AbstractUnsafe是AbstractChannel的内部类,所以AbstractChannel类的属性,在AbstractUnsafe类下也可以访问。

AbstractUnsafe.java
        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;
                }
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

这里我们可以分为几个关键代码块,并进行依次分析

  • doRegister(),往NioEventLoop的Selector注册。
  • 把channel的registered变量标记为true。
  • pipeline.invokeHandlerAddedIfNeeded();还记得之前我们调用pipeline.addLast()讲的延迟调用,一旦注册后,就立马执行单链表上的channelHandler,那么这个单链表上的channelHandler就是在这依次被调用
  • safeSetSuccess(promise);触发promise的监听器的operationComplete()方法。
  • pipeline.fireChannelRegistered();触发pipeline中的inbound channelHandler的channelRegistered()方法
  • if(isActive()){},获取channel的绑定状态,如果已经绑定端口,而且是第一次注册,那么调用pipeline.fireChannelActive();

1、doRegister()

AbstractNioChannel.java
@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;
                }
            }
        }
    }
  • 这里注册的事件是0,即什么事件也不监听。
    – 这里可能会有疑问?为什么不直接注册ACCEPT事件,因为目前来说channel还没有绑定端口,那么现在注册ACCEPT事件也没什么意义。

2、pipeline.invokeHandlerAddedIfNeeded();

DefaultChannelPipeline.java
final void invokeHandlerAddedIfNeeded() {
        assert channel.eventLoop().inEventLoop();
        if (firstRegistration) {
            firstRegistration = false;
            callHandlerAddedForAllHandlers();
        }
    }
DefaultChannelPipeline.java
private void callHandlerAddedForAllHandlers() {
        final PendingHandlerCallback pendingHandlerCallbackHead;
        synchronized (this) {
            assert !registered;

            // This Channel itself was registered.
            registered = true;

            pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
            // Null out so it can be GC'ed.
            this.pendingHandlerCallbackHead = null;
        }

        // This must happen outside of the synchronized(...) block as otherwise handlerAdded(...) may be called while
        // holding the lock and so produce a deadlock if handlerAdded(...) will try to add another handler from outside
        // the EventLoop.
        PendingHandlerCallback task = pendingHandlerCallbackHead;
        while (task != null) {
            task.execute();
            task = task.next;
        }
    }
PendingHandlerAddedTask.java
        @Override
        void execute() {
            EventExecutor executor = ctx.executor();
            if (executor.inEventLoop()) {
                callHandlerAdded0(ctx);
            } else {
                try {
                    executor.execute(this);
                } catch (RejectedExecutionException e) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(
                                "Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
                                executor, ctx.name(), e);
                    }
                    remove0(ctx);
                    ctx.setRemoved();
                }
            }
        }

还记得我们往单链表pendingHandlerCallbackHead添加了一个ChannelInitializer对象,我们回顾一下

ServerBootstrap.java  init()中一个片段
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });

那么此时就会执行ChannelInitializer对象的handlerAdded()方法,并且因为已经注册了,那么就会执行initChannel(ctx)方法

  • 首先获取handler,即是最开始模板代码中我们调用ServerBootstrap.handler(new LoggingHandler())。
  • 然后往管理channel的NioEventLoop的队列中插入一个Task。
    那么此时pipeline中数据结构为,如下:
    Netty4.x源码分析:服务端绑定端口_第5张图片
    3、然后执行safeSetSuccess(promise);,即通知往promise自己注册的所有监听器,那么这时候我们要回到AbstractBoostrap的doBind(final SocketAddress localAddress)方法中
AbstractBoostrap.java doBind(final SocketAddress localAddress) 的一个片段
regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        promise.registered();
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });

那么这时候会执行operationComplete()方法,然后调用 doBind0(regFuture, channel, localAddress, promise);

AbstractBoostrap.java
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());
                }
            }
        });
    }

这里往channel的NioEventLoop队列里插入一个Task,因为NioEventLoop是个单线程线程池,因为该NioEventLoop正在执行register0(promise)这个方法,所以这个Task不会立马被执行,而是等register0(promise)执行完后才执行。
所以我们回到register0(promise)方法。
4、pipeline.fireChannelRegistered();调用pipleline中的双向链表中的inbound Handler的channelRegistered()方法
Netty4.x源码分析:服务端绑定端口_第6张图片
此时register0(promise)就执行完毕了,刚刚我们往负责Channel的NioEventLoop的队列中丢入了一个Task,那么现在它会被执行。

AbstractBoostrap.java
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());
                }
            }
        });
    }

调用了channel.bind(localAddress, promise)

AbstractChannel.java
@Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }
DefaultChannelPipeline.java
 @Override
    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }
AbstractChannelHandlerContext.java
 @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        if (!validatePromise(promise, false)) {
            // cancelled
            return promise;
        }

        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }
  • 首先调用findContextOutbound(),从链表的的尾部往前找属于outbound的AbstractChannelHandlerContext
  • 然后调用AbstractChannelHandlerContext 的invokeBind(localAddress, promise);
AbstractChannelHandlerContext.java
private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
        if (invokeHandler()) {
            try {
                ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
            } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        } else {
            bind(localAddress, promise);
        }
    }

直接调用handler的bind()方法

我们首先回顾一下当前pipeline中的双向链表:
Netty4.x源码分析:服务端绑定端口_第7张图片
这里比较关键的属于outbound的channelHandler为HeadContext,我们直接看HeadContext的bind()方法

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

            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }

            // See: https://github.com/netty/netty/issues/576
            if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                !PlatformDependent.isWindows() && !PlatformDependent.isRoot()) {
                // Warn a user about the fact that a non-root user can't receive a
                // broadcast packet on *nix if the socket is bound on non-wildcard address.
                logger.warn(
                        "A non-root user can't receive a broadcast packet if the socket " +
                        "is not bound to a wildcard address; binding to a non-wildcard " +
                        "address (" + localAddress + ") anyway as requested.");
            }

            boolean wasActive = isActive();
            try {
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }

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

            safeSetSuccess(promise);
        }

这个方法是整个Netty服务端绑定端口的最核心的方法

  • doBind(localAddress);绑定端口
  • pipeline.fireChannelActive();往pipeline中传递channelActive事件

1、 doBind(localAddress);

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

这里很熟悉了,用到了NIO的API,根据JDK版本

  • 如果>=7,ServerSocketChannel.bind(SocketAddress,config.getBacklog())
  • 否则,ServerSocketChannel.socket().bind(SocketAddress,config.getBacklog())

2、pipeline.fireChannelActive();往pipeline中传递channelActive事件

DefaultChannelPipeline.java
 @Override
    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

这里的head是双向链表的头指针,即为HeadContext

AbstractChannelHandlerContext.java
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelActive();
                }
            });
        }
    }
private void invokeChannelActive() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelActive(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelActive();
        }
    }

直接调用HeadContext的channelActive()方法

@Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();

            readIfIsAutoRead();
        }

首先把channelActive事件往下传递,因为其他channelHandler都不是关键,我们就不往下看了,然后调用readIfIsAutoRead();

HeadContext.java
private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }
HeadContext.java
 private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }
AbstractChannel.java
    @Override
    public Channel read() {
        pipeline.read();
        return this;
    }
DefaultChannelPipeline.java
 @Override
    public final ChannelPipeline read() {
        tail.read();
        return this;
    }
    @Override
    public ChannelHandlerContext read() {
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeRead();
        } else {
            Runnable task = next.invokeReadTask;
            if (task == null) {
                next.invokeReadTask = task = new Runnable() {
                    @Override
                    public void run() {
                        next.invokeRead();
                    }
                };
            }
            executor.execute(task);
        }
        return this;
    }

从双向链表的尾指针开始往前找outbound的ChannelHandler,然后调用ChannelHandler的read()方法
Netty4.x源码分析:服务端绑定端口_第8张图片
这里找到的最关键的ChannelHandler 就是HeadContext,所以我们来看HeadContext的read()

HeadContext.java
        @Override
        public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }
AbstractUnsafe.java
        public final void beginRead() {
            assertEventLoop();
            if (!isActive()) {
                return;
            }
            try {
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }
AbstractNioMessageChannel.java
    @Override
    protected void doBeginRead() throws Exception {
        if (inputShutdown) {
            return;
        }
        super.doBeginRead();
    }
AbstractNioChannel.java
    @Override
    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);
        }
    }

OK,readInterestOp就是之前的SelectionKey.OP_ACCEPT的值,又是我们很熟悉的NIO的API,通过selectionKey .interestOps()注册一个ACCEPT事件。

到此为止,Netty服务端的绑定端口源码执行完毕。

你可能感兴趣的:(Netty4.x源码分析)