Netty服务端启动解读

492 阅读17分钟

最近花了较多时间来看netty,本来是打算先搞本书看看的,放弃了。《Netty实战》这本书其实写的挺好的,主要还是翻译水平可能只比我好一点的原因,但是翻翻源码,了解了一些netty服务启动,建立连接和读取数据等部分后,再来看这本书还是不错的。那边权威指南,强烈不推荐看。另外netty官网推荐的闪电侠的博客写的真的不错,本篇博客有不少地方都是借鉴来着。

public class NettyServer {

    public static void main(String[] args) {

        NioEventLoopGroup parentGroup = new NioEventLoopGroup();
        NioEventLoopGroup childGroup = new NioEventLoopGroup(3);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128) 
                    .childOption(ChannelOption.SO_KEEPALIVE, true) 
                    .handler(new SimpleServerHandler())
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(8081).sync();
            channelFuture.channel().closeFuture().sync(); 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            parentGroup.shutdownGracefully();
            childGroup.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");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead");
        // 如果不继续往下调用channelRead, 会导致NioSocketChannel服务注册,  最终导致,服务端的子handler全部失效.
        ctx.fireChannelRead(msg);
    }
}

ServerBootstrap 是服务端的一个启动辅助类,通过给他设置一系列参数来绑定端口启动服务。

group(bossGroup, workerGroup) 我们需要两种类型的人干活,一个是老板,一个是工人,老板负责从外面接活,接到的活分配给工人干,放到这里,bossGroup的作用就是不断地accept到新的连接,将新的连接丢给workerGroup来处理

.channel(NioServerSocketChannel.class) 表示服务端启动的是nio相关的channel,channel在netty里面是一大核心概念,可以理解为一条channel就是一个连接或者一个服务端bind动作,后面会细说

.handler(new SimpleServerHandler() 表示服务器启动过程中,需要经过哪些流程,这里SimpleServerHandler最终的顶层接口为ChannelHander,是netty的一大核心概念,表示数据流经过的处理器,可以理解为流水线上的每一道关卡

childHandler(new ChannelInitializer)...表示一条新的连接进来之后,该怎么处理,也就是上面所说的,老板如何给工人配活

ChannelFuture f = b.bind(8888).sync(); 这里就是真正的启动过程了,绑定8888端口,等待服务器启动完毕,才会进入下行代码

f.channel().closeFuture().sync(); 等待服务端关闭socket

源码分析

​ 我们从入口方法bind进入, 一路点进去,最后停在doBind

AbstractBootstrap

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

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    
     // 忽略部分代码
    doBind0(regFuture, channel, localAddress, promise);
}

从方法initAndRegister进入, 看名字就知道这个方法是进行初始化以及注册的, 来看具体是怎么做的

AbstractBootstrap

final ChannelFuture initAndRegister() {
    // (1) 通过反射创建channel
    Channel channel = channelFactory.newChannel();

	// (2) 初始化channel
    init(channel);

    // (3) 进行注册
    ChannelFuture regFuture = config().group().register(channel);

    return regFuture;
}

来看(1) 通过反射创建channel, 首先得知道channelFactory是如何创建的

public B channel(Class<? extends C> channelClass) {
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
   // 忽略校验

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

显然创建channelFactory是从我们的用户代码.channel(NioServerSocketChannel.class)开始的, 最终channelFactory=new ReflectiveChannelFactory(channelClass), 来看该方法如何创建channel

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    private final Class<? extends T> clazz;

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

    @Override
    public T newChannel() {
        return clazz.newInstance();
    }
}

所以可以知道通过newChannel得到的是NioServerSocketChannel对象, 需要注意的是这里通过反射创建的对象必须有无参构造器, 我们深入类NioServerSocketChannel来看看创建的时候做了哪些事情

NioServerSocketChannel

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

// 除了这个构造方法, 剩下的api都是由NIO提供
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

// 创建 ServerSocketChannel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    return provider.openServerSocketChannel();
}

继续往下走

public NioServerSocketChannel(ServerSocketChannel channel) {
    // 构造父类,  指定感兴趣的事件为连接事件SelectionKey.OP_ACCEPT
    super(null, channel, SelectionKey.OP_ACCEPT);
    // 创建netty的配置参数类
    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;
    ch.configureBlocking(false);
}

绑定了AbstractNioChannel的内部channel为NioServerSocketChannel中创建的NIO的ServerSocketChannel, parent还是null, 感兴趣的事件this.readInterestOp = SelectionKey.OP_ACCEPT, 设置ServerSocketChannel为非阻塞. 接着看服务构造方法

AbstractChannel

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

这个unsafe和pipeline相当重要,基本上贯穿整个服务启动和客户端连接, 暂时简单看下

AbstractNioMessageChannel

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

private final class NioMessageUnsafe extends AbstractNioUnsafe {

    private final List<Object> readBuf = new ArrayList<Object>();
    @Override
    public void read() {
        assert eventLoop().inEventLoop();
        final ChannelConfig config = config();
        final ChannelPipeline pipeline = pipeline();
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        allocHandle.reset(config);

        boolean closed = false;
        Throwable exception = null;
        do {
            int localRead = doReadMessages(readBuf);
            if (localRead == 0) {
                break;
            }
            if (localRead < 0) {
                closed = true;
                break;
            }

            allocHandle.incMessagesRead(localRead);
        } while (allocHandle.continueReading());

        int size = readBuf.size();
        for (int i = 0; i < size; i ++) {
            readPending = false;
            pipeline.fireChannelRead(readBuf.get(i));
        }
        readBuf.clear();
        allocHandle.readComplete();
        pipeline.fireChannelReadComplete();
    }
    	
}
protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

DefaultChannelPipeline

 protected DefaultChannelPipeline(Channel channel) {
     this.channel = ObjectUtil.checkNotNull(channel, "channel");
     // ..

     tail = new TailContext(this);
     head = new HeadContext(this);

     head.next = tail;
     tail.prev = head;
 }

从构造方法中可以看到, 创建pipeline的时候会绑定channel, 同时构建双向链表, 后续的pipeline.addLast(hander)的时候, 该处理器就添加到链表尾部(tail的前一个节点)

简单总结下通过工厂的反射创建NioServerSocketChannel做了哪些事:

  1. 继承关系:NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel -> Channel
  2. NioServerSocketChannel内部会持有NIO的ch=ServerSocketChannel(javvaSocket()返回)设置为了非阻塞,感兴趣的事件readInterestOp=SelectionKey.OP_ACCEPT; pipeline,unsafe,config=NioServerSocketChannelConfig

注意到,在上面说到了netty的配置类NioServerSocketChannelConfig,其继承自DefaultServerSocketChannelConfig,可以认为几乎是一样的,内部会持有很多netty配置相关的参数。

DefaultServerSocketChannelConfig

public class DefaultServerSocketChannelConfig extends DefaultChannelConfig
                                              implements ServerSocketChannelConfig {

    protected final ServerSocket javaSocket;
    private volatile int backlog = NetUtil.SOMAXCONN;
    
    /**
     * Creates a new instance.
     */
    public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
        
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        this.javaSocket = javaSocket;
    }
    
    @Override
    public <T> boolean setOption(ChannelOption<T> option, T value) {
        validate(option, value);

        if (option == SO_RCVBUF) {
            setReceiveBufferSize((Integer) value);
        } else if (option == SO_REUSEADDR) {
            setReuseAddress((Boolean) value);
        } else if (option == SO_BACKLOG) {
            setBacklog((Integer) value);
        } else {
            // 父类的设置也是类似,只是拥有更多的属性,比如设置最大超时时间
            return super.setOption(option, value);
        }

        return true;
    }
    
    @Override
    public ServerSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
        try {
            javaSocket.setReceiveBufferSize(receiveBufferSize);
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
        return this;
    }
    
    @Override
    public ServerSocketChannelConfig setBacklog(int backlog) {
        if (backlog < 0) {
            throw new IllegalArgumentException("backlog: " + backlog);
        }
        this.backlog = backlog;
        return this;
    }
}

再来看(2) 初始化channel

ServerBootstrap

@Override
void init(Channel channel) throws Exception {
    ChannelPipeline p = channel.pipeline();
    
    // 设置channel的option和attr
	final Map<ChannelOption<?>, Object> options = options();
    synchronized (options) {
        // 遍历options,设置到config中
        setChannelOptions(channel, options, logger);
    }

    // 关于io的channel的选项设置
    // 忽略部分

    // 这部分相当重要,  后续讲解   在注册的时候会调用到
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            // 该handler为用户代码中设置的.handler(new SimpleServerHandler())
            ChannelHandler handler = config.handler();
            if (handler != null) {
                // 加入到pipeline
                pipeline.addLast(handler);
            }
			
            // 往任务队列添加任务
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    // ServerBootstrapAcceptor用来绑定IOchannel, 后面介绍
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}


static void setChannelOptions(
        Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
    for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
        setChannelOption(channel, e.getKey(), e.getValue(), logger);
    }
}

private static void setChannelOption(
            Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
    try {
        if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
            logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
        }
    } catch (Throwable t) {
        logger.warn(
                "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
    }
}

总结一下init:获取child相关参数,比如childGroup,childHandler,options,attrs,设置到ServerBootstrapAcceptor这个处理器中,然后添加到pipeline中。当然在这之前需要将boss的初始化处理器加到 pipeline中。之后在注册的时候会调用initChannel方法,将两个处理器加到pipeline中。

再说简单点就是为channel的pipeline中设置初始化的处理器。当注册的时候执行initChannel方法,将两个处理器再加入到pipeline中,其中一个是用于处理数据读取 的。

最后来看 (3) 进行注册

AbstractBootstrap

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

group方法返回的就是NioEventLoopGroup这个bossGroup,创建这个的时候就已经创建好了的NioEventLoop孩子children[默认的核数*2], 每个孩子里面都有个相同的线程池这个在后面会详细讲到

MultithreadEventLoopGroup

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

// 继承自MultithreadEventExecutorGroup
public EventExecutor next() {
    return chooser.next();
}

// MultithreadEventExecutorGroup的内部类,next()返回children数组中的一个NioEventLoop, 即轮询得到n个中的一个
private final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
    @Override
    public EventExecutor next() {
        return children[childIndex.getAndIncrement() & children.length - 1];
    }
}

所以说注册用的是NioEventLoop, 因为从next返回的是NioEventLoop,它是继承自SingleThreadEventLoop

SingleThreadEventLoop

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

@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
    channel.unsafe().register(this, promise);
    return promise;
}

获取channel的unsafe,进行注册。这类实际是NioMessageUnsafe

补充: Unsafe类为Channel的内部接口, 通常是不给外层使用的, 注意这里的不安全是针对外部来说的.

AbstractChannel.AbstractUnsafe

@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {

    AbstractChannel.this.eventLoop = eventLoop;

    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
         // 往任务队列添加任务, 如果是首次会绑定线程
         eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    // 真正进行注册,  所以在执行任务队列中的任务的时候才会进行注册
                    register0(promise);
                }
            });
    }
}

eventLoop.inEventLoop()判断当前线程(这里是main)是否等于NioEventLoop中持有的线程,初始肯定是false

接着我们来看eventLoop.execute

SingleThreadEventExecutor

public void execute(Runnable task) {
  	// 判断当前线程是否为EventLoop持有的线程,前面说了初始启动肯定是false
    boolean inEventLoop = inEventLoop();
    if (inEventLoop) {
        addTask(task);
    } else {
        // (1) 启动线程
        startThread();
        // (2)添加任务到任务队列
        addTask(task);
    }

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

先来看(1) 启动线程干了哪些事

private void startThread() {
    if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
        if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
            doStartThread();
        }
    }
}

private void doStartThread() {
    assert thread == null;
    executor.execute(new Runnable() {
        @Override
        public void run() {
            thread = Thread.currentThread();

            boolean success = false;
            SingleThreadEventExecutor.this.run();
            success = true;
        }
    });
}

我们还是先看下executor.execute(runnable)是怎么回事

@Override
public void execute(Runnable command) {
	threadFactory.newThread(command).start();
}

这里和4.0版本有点出入, 4.0的doStartThread方法就是thread.start,thread是内部持有的,在构建eventloop的时候就构建好了,利用的是线程工厂。其实 4.1版本虽然用的线程池,其实最终也还是使用的threadFactory.newThread(command)。道理是一样的。4.0版本构建的thread内部的runnable逻辑也和上面一样。

newThread得到的是一个FastThreadLocalThread, 暂时不介绍了, 之后便执行线程的start()方法, 即执行command.run() -> 执行了SingleThreadEventExecutor.this.run(), 由于当前对象是NioEventLoop,所以执行的是该对象的run方法, 我们进去一览

NioEventLoop

protected void run() {
    for (;;) {
        // 事件轮询   内部是个for死循环, 判断是否有任务或者定时任务或者监听到事件
        select(wakenUp.getAndSet(false));

        // 处理SelectedKey
        processSelectedKeys();

        // 执行任务队列中的任务
        runAllTasks();
            
    }
}

服务刚启动的时候, 我们就往任务队列里面加入了任务, 比如前面的注册任务register0(promise); 但是并不会监听到连接事件, 所以 select在得知任务队列的时候会跳出循环, 进入到processSelectedKeys()后,由于没有key所以会接着退出执行runAllTasks(), 加入断点也能发现这里执行的就是我们注册的任务register0(promise). 那么我们就来看看是如何注册的吧.

ps: 关于processSelectedKeys是非常重要的, 在后续客户端进行连接的时候会详细介绍, 那会就会进入该方法了.

AbstractChannel

private void register0(ChannelPromise promise) {

    doRegister();
    
    // 执行SimpleServerHandler.handlerAdded(ctx)
    pipeline.invokeHandlerAddedIfNeeded();

    // 执行SimpleServerHandler.channelRegistered(ctx)
    pipeline.fireChannelRegistered();

}

doRegister则是真正的进行注册

AbstractNioChannel

protected void doRegister() throws Exception {
    for (;;) {
        selectionKey = javaChannel().register(eventLoop().selector, 0, this);
        return;
    }
}

javaChannel()获得的就是NIO的ServerSocketChannel, 后面的register等就是NIO中的channel注册到selector的代码了(由于是异步的, debug后一直走发现还有两个, 一个是最开始的init中添加的, 还有个是最后的doBind0()添加的).

我们再回头看下如何执行所有任务的

SingleThreadEventExecutor

// 从任务队列中不断获取任务并执行
protected boolean runAllTasks(long timeoutNanos) {
    // 获取第一个任务
    Runnable task = pollTask();

    long lastExecutionTime;
    for (;;) {
        // 执行任务
        safeExecute(task);

        // 循环获取任务 
        task = pollTask();
        if (task == null) {
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            break;
        }
    }
    this.lastExecutionTime = lastExecutionTime;
    return true;
}

AbstractEventExecutor

// 最初执行的task就是register0(promise)
protected static void safeExecute(Runnable task) {
    task.run();
}

到这里我们做个较长的总结:

  1. 首先是使用channel.unsafe进行注册,这个注册需要传入eventLoop,设置为channel的成员变量。
  2. 接着执行eventLoop的execute方法,继承自Executor框架,传入注册的任务Runnable(register0(promise)。
  3. eventLoop.execute中,首先是判断是不是当前线程也就是main线程是不是等于eventLoop中的线程工厂创建的线程,第一次进来的时候肯定不是。所以需要启动线程,即执行startThread()方法
  4. startThread()方法其实就是启动线程,里面的任务则是轮询,处理轮询到的keys,处理任务队列。注册的任务是在这步之前添加到任务队列中的,所以启动线程后会进行任务的注册。
  5. 那么注册register0(promise)主要做了什么事呢
    • 首先是nio的注册javaChannel().register(eventLoop().selector, 0, this); this为channel,进行了attachement,JavaChannel就是ServerSocketChannel
    • 然后设置registered = true,执行pipeline的invokeHandlerAddedIfNeede和fireChannelRegistered() 这两个方法很重要,在最开始初始化的时候有部分代码就在这里被执行。

handler的invokeHandlerAddedIfNeede()分析:

调用pipeline中的各个处理器的handlerAdded方法,注意ChannelInitializer的handlerAdded是已经实现了的,是个模板方法,其中需要实现initChannel,而之前在init方法中是往pipeline中加了 ChannelInitializer这么个处理器的,所以在invokeHandlerAddedIfNeede()方法中最终会被执行。init的那部分代码再次贴出来

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }
        
        // 添加到任务队列
        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

一直往下跟

DefaultChannelPipeline

final void invokeHandlerAddedIfNeeded() {
    assert channel.eventLoop().inEventLoop();
    if (firstRegistration) {
        firstRegistration = false;
        
        callHandlerAddedForAllHandlers();
    }
}

看名字和注释就知道,channel注册了EventLoop后,需要调用所有处理器的added方法

private void callHandlerAddedForAllHandlers() {
    final PendingHandlerCallback pendingHandlerCallbackHead;
    synchronized (this) {
        assert !registered; // pipeline中和channel都是各持各的registered

        registered = true;

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

    PendingHandlerCallback task = pendingHandlerCallbackHead;
    while (task != null) {
        task.execute();
        task = task.next;
    }
}

可能你会问this.pendingHandlerCallbackHead;是啥, 什么时候赋值的; 其实这些都是在pipeline.addLast(handler)中做的, 还是先来看看这个addLast

public final ChannelPipeline addLast(String name, ChannelHandler handler) {
    return addLast(null, name, handler);
}

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

        // 创建包裹了处理器handler的DefaultChannelHandlerContext
        newCtx = newContext(group, filterName(name, handler), handler);

        // 将ctx添加到pipeline的尾部
        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) {
            // 这里形成单向链表
            callHandlerCallbackLater(newCtx, true);
            return this;
        }

        // 忽略部分代码
    }
    // 调用实现了handlerAdded方法的handler
    callHandlerAdded0(newCtx);
    return this;
}

private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
    return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}

private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}

// ctx封装成PendingHandlerCallback, 拼接成单向链表
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
 
    // 区分是addTask还是removeTask
    PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
    PendingHandlerCallback pending = pendingHandlerCallbackHead;
    if (pending == null) { // 如果是初次addLast那么就设置头部
        pendingHandlerCallbackHead = task;
    } else {
        // Find the tail of the linked-list.
        while (pending.next != null) {
            pending = pending.next;
        }
        pending.next = task;
    }
}

总结一下addLast: 封装handler成context, 并且添加到pipeline的链表尾部; 同时如果发现是还未注册, 那么构建PendingHandlerAddedTask封装ctx, 并且拼接成单向链表. 注意到由于是未注册的时候才会拼接这个单向链表,所以在前面的init中显然是将ChannelInitialier拼接进来了,所以在后面callHandlerAddedForAllHandlers方法会得到执行。

简单点说就是,在还未注册,即初始化的时候将处理器,主要是ChannelInitialier构建成ctx添加到pipeline中,同时封装成PendingHandlerCallback来构成单向链表,后续注册完后会对链表各节点进行调用。

我们再来看一下DefaultChannelHandlerContext

DefaultChannelHandlerContext

final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {

    private final ChannelHandler handler;

    DefaultChannelHandlerContext(
            DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
        super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        this.handler = handler;
    }

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

    private static boolean isInbound(ChannelHandler handler) {
        return handler instanceof ChannelInboundHandler;
    }

    private static boolean isOutbound(ChannelHandler handler) {
        return handler instanceof ChannelOutboundHandler;
    }
}

它继承自AbstractChannelHandlerContext, 内部持有处理器,提供了两个方法用来判断是入队方法还是出队方法

AbstractChannelHandlerContext

private final boolean inbound;
private final boolean outbound;
private final DefaultChannelPipeline pipeline;
private final String name;

AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
                              boolean inbound, boolean outbound) {
    this.name = ObjectUtil.checkNotNull(name, "name");
    this.pipeline = pipeline;
    this.executor = executor;
    this.inbound = inbound;
    this.outbound = outbound;
}

@Override
public Channel channel() {
    return pipeline.channel();
}

@Override
public ChannelPipeline pipeline() {
    return pipeline;
}

@Override
public EventExecutor executor() {
    if (executor == null) {
        return channel().eventLoop();
    } else {
        return executor;
    }
}

每个context中是拥有pipeline的,这点很重要,同时还可以获取channel。 由于可以获取到channel,而channel中又在注册的时候绑定了EventLoop,所以根据context可以获取eventLoop

我们再回到callHandlerAddedForAllHandlers方法

PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
    task.execute();
    task = task.next;
}

这一部分就是不停的从单向链表获取节点PendingHandlerCallback来执行execute,显然介绍到这可以知道这个单向链表中目前就只有一个节点,即在init方法中添加进来的。

我们来看PendingHandlerCallback的实现类PendingHandlerAddedTask

DefaultChannelPipeline

private abstract static class PendingHandlerCallback implements Runnable {
    final AbstractChannelHandlerContext ctx;
    PendingHandlerCallback next;

    PendingHandlerCallback(AbstractChannelHandlerContext ctx) {
        this.ctx = ctx;
    }

    abstract void execute();
}

private final class PendingHandlerAddedTask extends PendingHandlerCallback {

    PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) {
        super(ctx);
    }

    @Override
    public void run() {
        callHandlerAdded0(ctx);
    }

    @Override
    void execute() {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
            callHandlerAdded0(ctx);
        } else {
            // ...
        }
    }
}

内部持有一个ctx以及指向下一个节点的指针。注意ctx.executor()返回的是NioEventLoop,因为每个ctx中是有pipeline的,从pipeline获取channel,从channel获取之前绑定的eventloop。

继续往下走可以看到

private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
    ctx.handler().handlerAdded(ctx);
}

这里的得到的处理器是ChannelInitializer, 就是init方法中添加到pipeline中的

ChannelInitializer

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    if (ctx.channel().isRegistered()) {
        initChannel(ctx);
    }
}

@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
        try {
            initChannel((C) ctx.channel());
        } catch (Throwable cause) {
            
        } finally {
            remove(ctx); // 注意这里,执行完了以后会将这个ctx,这里就是ChannelInitializer在pipeline的双向链表中移除掉。
        }
        return true;
    }
    return false;
}

protected abstract void initChannel(C ch) throws Exception;

initChannel就是我们的init方法里面添加到pipeline中的处理器ChannelInitializer来实现的,所以从这里点进去就到了下面这个地方(又贴了一次)

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }

        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

进入到initChannel中后,将设置给ServerBootStrap中的处理器也加到pipeline中. 然后构建任务加到任务队列中。

注意这里pipeline.addLast(handler);会导致调用一次该处理器SimpleServerHandler的handlerAdded方法。

接下来的execute方法会将处理器添加到队列中,在select方法中轮询出来会继续执行

我们再回到最初的AbstractBootstrap的doBind0(regFuture, channel, localAddress, promise);

AbstractBootstrap

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

    // This method is invoked before channelRegistered() is triggered. 
    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());
            }
        }
    });
}

AbstractChannel

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

DefaultPipelineChannel

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

最终在

AbstractChannelHandlerContext

public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise){
	
    // DefaultChannelPipeline$HeadContext
    final AbstractChannelHandlerContext next = findContextOutbound();
    // NioEventLoop
    EventExecutor executor = next.executor();
    next.invokeBind(localAddress, promise);
    return promise;
}

// 获取头部Context  此时已经有了两个中间节点了,一个是自定义的SimpleChannelHandler构成的context,一个是init方法中的ServerBootstrapAcceptor
private AbstractChannelHandlerContext findContextOutbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.prev;
    } while (!ctx.outbound);
    return ctx;
}

点进去invokeBind, 最终调用到HeadContext

HeadContext

@Override
public void bind(
    ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
    throws Exception {
    // unsafe = pipeline.channel().unsafe();,在构建head的时候构建出来的
    unsafe.bind(localAddress, promise);
}

再往下走到NioServerSocketChannel

@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog()); // 绑定并设置最大连接数
    }
}

javaChannel().bind(localAddress, config.getBacklog());也就是NIO中的地址绑定

到这里我们再总结一下:前面我们说了启动线程后会进行轮询,判断队列里面有任务之后,执行队列里面的任务。注意eventLoop.execute会将任务加到队列,第一次没启动线程的时候则先启动线程。

任务队列里面首先是一个注册的任务,注册要干三件事:利用selector进行注册,执行单向链表pendingHandlerCallbackHead中的execut方法,最终会调用到其中包含的ctx.handler的handlerAdded方法,而此时 该单向链表中仅有一个ctx,就是init方法中调用了pipeline.addLast加入进来的处理器ChannelInitializer,注意:只有当还未注册的时候调用pipeline.addLast才会去构建那个单向列表。

而处理器ChannelInitializer的handlerAdded方法中会调用initChannel方法,这是够抽象方法,这init里面的已经实现了。调用该方法会将构建ServerBootstrap的handler(SimpleChannelHandler)加入到pipeline 中。并且执行eventLoop.execute将任务加入到任务队列中,该任务为将ServerBootstrapAcceptor加入到pipeline中。

在后续doBind中又会构建任务加入到任务队列中,所以此时任务队列中有两个任务了。轮询出来后会依次执行这两个任务,一个是将ServerBootstrapAcceptor加入到pipeline中,记住此时pipeline中有两个处理器了; 一个是执行地址绑定。