netty② 源码- 服务端启动过程

128 阅读15分钟

netty启动过程 server

演示案例

public static void main(String[] args) {
    --服务端
    NioEventLoopGroup serverGroup = new NioEventLoopGroup();
    NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    try{
            ServerBootstrap bootstrap = new ServerBootstrap();
            //serverGroup赋值给group workerGroup赋值给childGroup
            ChannelFuture channelFuture = bootstrap.group(serverGroup, workerGroup)
                //指定channelFactory为ReflectiveChannelFactory() 通过反射创建Channel
                .channel(NioServerSocketChannel.class)
                //表示服务端启动时会经过那些工序 这里添加了一个自定义的handler,后续会在异步线程中添加到pipeline中
                .handler(new SimpleServerHandler())
                //为每个连接设置一些tcp参数,这里代表禁用Nagle算法,有数据就马上发送
                //其他还有childAttr()给每个连接指定自定义属性
                .childOption(ChannelOption.TCP_NODELAY, true)
                //主要用于设置一系列handler来处理每个连接的数据
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                                ch.pipeline().addLast(SimpleClientHandler.INSTANCE);
                                ch.pipeline().addLast(new StringDecoder());
                                ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                                        @Override
                                        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                                System.out.println("msg = " + msg);
                                        }
                                });
                        }
                }).bind(8000);
            channelFuture.channel().closeFuture()
            //等待服务端关闭端口绑定  其实就是让程序不退出
            .sync();
    }finally {
            serverGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
    }
}

 public static void main(String[] args) throws InterruptedException {
	Bootstrap bootstrap = new Bootstrap();
	NioEventLoopGroup group = new NioEventLoopGroup();
	bootstrap.group(group)
			.channel(NioSocketChannel.class)
			.handler(new ChannelInitializer<Channel>() {
				@Override
				protected void initChannel(Channel ch) throws Exception {
					ch.pipeline().addLast(new StringEncoder());
				}
			});
	Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
	while (true) {
		channel.writeAndFlush(new Date() + ":hello");
		Thread.sleep(2000);
	}
}

public class SimpleServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("----server channelActive----");
        super.channelActive(ctx);
    }
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("----server channelRegistered----");
        super.channelRegistered(ctx);
    }
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("----server handlerAdded----");
        super.handlerAdded(ctx);
    }
}


控制台打印:
----server handlerAdded----
----server channelRegistered----
----server channelActive----
...
----client handlerAdded----
----client channelRegistered----
----client channelActive----
----client channelRead----
msg = Tue Jul 05 11:45:12 CST 2022:hello
----client channelReadComplete----
...
----client channelInactive----
----client channelUnregistered----
----client handlerRemoved----

1.类图关系

Bootstrap image.png

通过类图得知,ServerBootstrap和Bootstrap共同享有父类AbstractBootstrap带来的group,options,attrs属性,ServerBootstrap 同时还扩展了childGroup,childOptions,childOptuibs,childHandler属性。

EventLoopGroup image.png 从这个类图可以看到NioEventLoopGroup和 NioEventLoop都实现了ExecutorService接口,这代表他们都是一个线程池,无非就是多线程还是单线程的关系了。

Channel ChannelConfig

image.png 这两个放在一起来看是因为NioSocketChannel和NioServerSocketChannel中各自维护了一个config对象,用 来辅助存放jdk中的SocketChannel,netty中的SocketChannel底层的调用依赖于jdk中的SocketChannel

Pipeline ChannelHandlerContext ChannelHandler

ChannelPipeline.png pipeline基于责任链模式,由一系列ChannelHandlerContext组成,ChannelHandlerContext包裹着ChannelHandler,基于ChannelHandlerContext辅助调用ChannelHandler实现

2. NioEventLoopGroup

构造方法

public NioEventLoopGroup() {
    this(0);
}

层层调用
//executor传参为null
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
     //Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", 
                 Runtime.getRuntime().availableProcessors() * 2));
     //基于配置或者两倍cpu核数
     super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    //INSTANCE就是该类自己的实例 DefaultEventExecutorChooserFactory
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

最终调用至MultithreadEventExecutorGroup构造方法
 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    ...
    if (executor == null) {
         //2.1 创建线程池  ThreadPerTaskExecutor实现了Executor接口,
          //它的调用其实就是通过传入的ThreadFactory,每次都new一个线程
         executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
   //在类图中可以看到  NioEvenyloop是EventExecutor的实现
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
            //2.2 创建  NioEventLoop
                    children[i] = newChild(executor, args);
                    success = true;
            } catch (Exception e) {
                  ...
            } finally {
                 ...
            }
    }
    2.3 获取线程选择器
    chooser = chooserFactory.newChooser(children);
    ...
}

2.1创建线程池 newDefaultThreadFactory

MultithreadEventLoopGroup#newDefaultThreadFactory

 protected ThreadFactory newDefaultThreadFactory() {
    //getClass也就是NioEventLoopGroup了   权重设置为5
     return new DefaultThreadFactory(getClass(), Thread.MAX_PRIORITY);
}

 public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
    //取首字母小写 也就是nioEventLoopGroup ;   非守护线程
    this(toPoolName(poolType), daemon, priority);
}

public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
	...这也就是为什么netty的线程都是 nioEventLoopGroup-2-1这样
	prefix = poolName + '-' + poolId.incrementAndGet() + '-';
	this.daemon = daemon;
	this.priority = priority;
	this.threadGroup = threadGroup;
}

2.2创建 NioEventLoop newChild

NioEventLoopGroup#newChild

protected EventLoop newChild(Executor executor, Object... args) throws Exception {
   // NioEventLoop了,  拒绝策略无法加入到工作队列时抛出异常 
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}


NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
			 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
         //下方SingleThreadEventExecutor addTaskWakesUp设置为false ,最大任务数根据配置 如果没有配置就是Interger.MAX了
	super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
	...
	provider = selectorProvider;
        // 2.2.1 获取Selector(jdk) 
	selector = openSelector();
	selectStrategy = strategy;
}


protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
	boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
        //保存NioEventLoopGroup的引用
	super(parent);
	this.addTaskWakesUp = addTaskWakesUp;
	this.maxPendingTasks = Math.max(16, maxPendingTasks);
        //前面创建的DefaultThreadFactory
	this.executor = ObjectUtil.checkNotNull(executor, "executor");
        //Mpsc队列 多生产者单消费者,单消费者指的是该NioEventloop对应的线程,多生产者对应的是之外的线程,
          //通常就是我们的业务线程。比如writeAndFlush不用考虑线程安全,随意调用。
          //这是普通任务队列 ,
          //对应的还有的定时任务队列 scheduledTaskQueue(PriorityQueue 根据定时任务的到期时间进行排序)
	taskQueue = newTaskQueue(this.maxPendingTasks);
	rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

定时任务比如:
ctx.channel().eventloop().schedule(()->{ xxxxx,60,TimeUnit.SECONDS);

每个NioEventloop中都维护有一个Selector 和 MPSC队列

2.2.1 获取Selector(jdk) openSelector

NioEventLoop#openSelector

private Selector openSelector() {
    final Selector selector;
    selector = provider.openSelector();
    ...
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
   ...
    final Class<?> selectorImplClass = Class.forName(
                        "sun.nio.ch.SelectorImpl")
    Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
                selectedKeysField.setAccessible(true);
                publicSelectedKeysField.setAccessible(true);
                selectedKeysField.set(selector, selectedKeySet);
                publicSelectedKeysField.set(selector, selectedKeySet);
     //赋值给NioEventloop           
    selectedKeys = selectedKeySet;            
    ...
    return selector;
}

大致的意思就是用SelectedSelectionKeySet这个类替换掉 SelectorImpl中的 selectedKeys,publicSelectedKeys,
因为SelectedSelectionKeySet也继承AbstractSet了,所以可以直接替换,但替换肯定是有原因的

SelectedSelectionKeySet

final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {

    private SelectionKey[] keysA;
    private int keysASize;
    private SelectionKey[] keysB;
    private int keysBSize;
    private boolean isA = true;

    SelectedSelectionKeySet() {
        keysA = new SelectionKey[1024];
        keysB = keysA.clone();
    }

    @Override
    public boolean add(SelectionKey o) {
        if (o == null) {
            return false;
        }
        if (isA) {
            int size = keysASize;
            //直接添加到数据的末尾
            keysA[size ++] = o;
            keysASize = size;
            if (size == keysA.length) {
                //扩容
                doubleCapacityA();
            }
        } else {
            int size = keysBSize;
            keysB[size ++] = o;
            keysBSize = size;
            if (size == keysB.length) {
                doubleCapacityB();
            }
        }
        return true;
    }
}

这里底层使用了两个数组 交替使用充当set,在4.1.9Final中变为一个数组,因为是等效的,我们就默认只有一个数组就好,
它的实质就是每次完成select操作完成后,如果有io事件发生,需要向selectedKeys中调用add方法填充数据时, 
操纵的是一个全新的数组,但是只要每次把下标置为0,用一个数组是等效的。
当后续的代码中 遍历selectedKeys 发现这样的代码
if (selectedKeys[i] == null) {
	break;
}
其实就是到达了边界,后续不用看了。
使用数组的好处就是遍历的时候非常高效,add的复杂度在不扩容的情况下永远是O(1)

2.3 获取线程选择器 newChooser

DefaultEventExecutorChooserFactory#newChooser

public EventExecutorChooser newChooser(EventExecutor[] executors) 
    // (val & -val) == val 基于位运算判断 线程数是否为2的幂 
    if (isPowerOfTwo(executors.length)) {
       //idx.getAndIncrement() & executors.length - 1  位运算 性能更佳
        return new PowerOfTowEventExecutorChooser(executors);
    } else {
        //Math.abs(idx.getAndIncrement() % executors.length) 取模
        return new GenericEventExecutorChooser(executors);
    }
    不管何种方式,最终效果都是从第一个NioEventLoop遍历到最后一个,反复如此
}

image.png 这就是上述NioEventloopGroup与NioEventloop的关系

3.doBind()

案例中 bind()方法 ,其内部又调用了doBind方法

AbstractBootstrap#doBind

private ChannelFuture doBind(final SocketAddress localAddress) {
    //3.1 初始化channel 并注册到 selector
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }
    //register操作异步线程实现的 只有当register0方法结束 regFuture才算完成
    if (regFuture.isDone()) {
        ChannelPromise promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        //还未执行完register操作,添加listner,待register执行完毕后 触发回调
        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();
                       //3.2 绑定端口
                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

3.1初始化并注册 AbstractBootstrap#initAndRegister

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        //3.1.1 基于反射创建channel,示例中传入了NioServerSocketChannel.class 
        channel = channelFactory.newChannel();
        //3.1.2 初始化
        init(channel);
    } catch (Throwable t) {
        ...
        return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
    }
    //3.1.3 注册
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        ...
    }
    return regFuture;
}

3.1.1 反射创建 NioServerSocketChannel实例

无参构造调用到这里, ServerSocketChannel 是jdk中的类 使用 SelectorProvider.openServerSocketChannel()获得
public NioServerSocketChannel(ServerSocketChannel channel) {
    //感兴趣的ops为 OP_ACCEPT,代表我只关心 客户端的连入操作
    super(null, channel, SelectionKey.OP_ACCEPT);
    //保存channel ,ServerSocket
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

父类构造
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
   //根据前文传参parent设置为null,生成随机id编号,
   //初始化unsafe设置为NioMessageUnsafe,这个unsafe也是netty自定义的接口,非jdk,后续jdk中的类用(jdk)表示
   //初始化pipeline为DefaultChannelPipeline
    super(parent);
    //保存jdk中的channel
    this.ch = ch;
    //保存感兴趣的操作 这里是16(1<<4)  SelectionKey.OP_ACCEPT
    this.readInterestOp = readInterestOp;
    try {
        //设置为非阻塞
        ch.configureBlocking(false);
    } catch (IOException e) {
       .....
    }
}

DefaultChannelPipeline构造
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;
}
Unsafe

netty的中的Unsafe在Channel中定义,与Chnnel密切相关

image.png 从它的方法列表可以得知,它主要负责

  • 分配内存
  • Socket四元组信息
  • 注册到selector
  • 绑定端口
  • Socket的连接和关闭
  • Socket的读写
NioMessageUnsafe

与新连接建立操作相关

NioByteUnsafe

与连接的字节数据读写相关

总结: 示例中"channel(NioServerSocketChannel.class)" 其实就是在声明服务端的类型。启动时,反射创建NioServerSocketChannel,内部会维护ServerSocketChannel(通过SelectorProvider.openServerSocketChannel()创建而来),并将其设置为非阻塞,保留SelectionKey.OP_ACCEPT为感兴趣的操作(与Selector交互时用到,此时暂时还未注册),实例化NioMessageUnsafe,DefaultChannelPipeline。最后自身的属性NioServerSocketChannelConfig会持有this(NioServerSocketChannel)和ServerSocket(jdk)的引用

3.1.2 初始化 ServerBootstrap#init

void init(Channel channel) throws Exception {
    //我们传给bootStrap的options 示例中没有进行配置
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        // channel.config()中也就是NioServerSocketChannelConfig中有很多属性,
        //这里将options的挨个提取并按条件赋值
        channel.config().setOptions(options);
    }

    final Map<AttributeKey<?>, Object> attrs = attrs0();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            //设置服务端 attr属性值
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            channel.attr(key).set(e.getValue());
        } 
    }
    //提取  childOptions childAttrs 此时只有NioServerSocketChannel一个channel。
    //ServerSocketChannel(jdk)与NioServerSocketChannel对应。那么SocketChannel(jdk)肯定也会和某个类对应。
    ....

    //3.1.2.1 添加用户自定义的handler 上述的类图里可以看到 ChannelInitializer也属于ChannelHandler  
    //p是 NioServerSocketChannel构造中实例化的pipepline
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            //config.handler() 其实就是我们传给ServerBootStrap的自定义handler
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    //添加一个接入器 用于接收新请求
                    //currentChildOptions currentChildAttrs 也就是上面提取的childOptions childAttrs
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}
options:

ServerBootstrap 设置 Channel 属性有option和childOption两个方法,option 主要负责设置 Boss 线程组,而 childOption 对应的是 Worker 线程组。

  • SO_KEEPALIVE: 设置为 true 代表启用了 TCP SO_KEEPALIVE 属性,TCP 会主动探测连接状态,即连接保活
  • SO_BACKLOG:已完成三次握手的请求队列最大长度,同一时刻服务端可能会处理多个连接,在高并发海量连接的场景下,该参数应适当调大
  • TCP_NODELAY Netty 默认是 true,表示立即发送数据。如果设置为 false 表示启用 Nagle 算法,该算法会将 TCP 网络数据包累积到一定量才会发送,虽然可以减少报文发送的数量,但是会造成一定的数据延迟。Netty 为了最小化数据传输的延迟,默认禁用了 Nagle 算法
  • SO_SNDBUF:TCP 数据发送缓冲区大小
  • SO_RCVBUF:TCP数据接收缓冲区大小,TCP数据接收缓冲区大小
  • SO_LINGER:设置延迟关闭的时间,等待缓冲区中的数据发送完成
  • CONNECT_TIMEOUT_MILLIS :建立连接的超时时间
3.1.2.1 添加用户自定义的handler

DefaultChannelPipeline#addLast

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(handler);
       //包装为 DefaultChannelHandlerContext
        newCtx = newContext(group, filterName(name, handler), handler);
        //加入双向链表 头尾之间 
        addLast0(newCtx);
        
        //还未完成注册 注册的动作在3.1.3中
        if (!registered) {
            newCtx.setAddPending();
            //将ctx包装为PendingHandlerAddedTask , 等待后续调用ctx.handler().handlerAdded(ctx); 
             //也就是下方3.1.3.2 register0中注册完毕后完成的回调
            callHandlerCallbackLater(newCtx, true);
            return this;
        }
       //已经完成注册 我们自定义的handler逻辑进入这里
        EventExecutor executor = newCtx.executor();
        ...
    }
    //回调handlerAdded方法 也就是我们实例中的 打印 ----server handlerAdded----
    callHandlerAdded0(newCtx);
    return this;
}

总结:创建完NioServerSocketChannel需要进行初始化,我们传入的的options和attrs分别有channel的config属性和自身维护。将子类的childOptions childAttrs封装到一个ServerBootstrapAcceptor类中(此类后续会加入到pipeline中)。向自身的pipeline中加入一个ChannelInitializer用于辅助添加我们定义的handler,此时由于尚未完成注册操作,ChannelInitializer会被包装成PendingHandlerAddedTask等待后续回调。

3.1.3 注册 register

config().group().register(channel);

MultithreadEventLoopGroup#register

public ChannelFuture register(Channel channel) {
   //next()就是调用chooser线程选择器获取EventExecutor[]数组中的一个也就是NioEventLoop
    return next().register(channel);
}

SingleThreadEventLoop#register

public ChannelFuture register(Channel channel) {
    //promise中维护有 channel 和 EventExecutor(当前为NioEventLoop)
    return register(new DefaultChannelPromise(channel, this));
}

public ChannelFuture register(final ChannelPromise promise) {
	//基于unsafe进行注册
	promise.channel().unsafe().register(this, promise);
	return promise;
}

AbstractChannel.AbstractUnsafe#register

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    ...
    //将当前的EventLoop 也就是NioEventLoop的引用指向给NioServerSocketChannel
    AbstractChannel.this.eventLoop = eventLoop;
    //判断 Thread.currentThread() 是不是等于 当前NioEventLoop自己维护的thread(当前为null)
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
            //3.1.3.1 NioEventLoop execute 执行任务
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    //3.1.3.2 真正的注册逻辑
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            ...
        }
    }
}

3.1.3.1 NioEventLoop execute 执行任务
public void execute(Runnable task) {
    boolean inEventLoop = inEventLoop();
    if (inEventLoop) {
       //是同一个线程操作就直接添加任务到队列中
        addTask(task);
    } else {
        //判断是否需要创建线程
         //第一次进入这里 因为thread引用还为空  激活Thread 内部调用 doStartThread方法,此方法只会触发一次
        startThread();
        //任务添加到   taskQueue中
        addTask(task);
        if (isShutdown() && removeTask(task)) {
            reject();
        }
    }
    //判断任务是不是NonWakeupRunnable  不是就需要唤醒selector ,只有WriteTask不需要
    if (!addTaskWakesUp && wakesUpForTask(task)) {
        //内部还会再判断,inEventLoop为false才会执行,代表此时是其他线程提交了新的任务。
        //并且wakenUp状态为false才会唤醒,执行select操作时会置为false,代表此时线程阻塞住,需要被唤醒
        wakeup(inEventLoop);
    }
}

SingleThreadEventExecutor#doStartThread

private void doStartThread() {
    assert thread == null;
    //executor是 2.1中创建的ThreadPerTaskExecutor,内部持有的是DefaultThreadFactory
  //这里调用的是 是DefaultThreadFactory.newThread方法,创建一个 FastThreadLocalThread线程并传入任务 开启线程
    executor.execute(new Runnable() {
        @Override
        public void run() {
            //将NioEventLoop持有的thread引用指向 上面创建的FastThreadLocalThread 
            thread = Thread.currentThread();
            if (interrupted) {
                thread.interrupt();
            }
            boolean success = false;
            updateLastExecutionTime();
            try {
                //NioEventloop就绪 开始 死循环执行任务了
                SingleThreadEventExecutor.this.run();
                success = true;
            } catch (Throwable t) {
                logger.warn("Unexpected exception from an event executor: ", t);
            } finally {
                清除任务 关闭selector
            }
        }
    });
}
3.1.3.2 真正的注册逻辑 register0

AbstractChannel.AbstractUnsafe#register0

private void register0(ChannelPromise promise) {
    try {
        ...
        boolean firstRegistration = neverRegistered;
        //兜兜转转 又回到了 NioServerSocketChannel  调用jdk的注册方法
        doRegister();
        neverRegistered = false;
        registered = true;
        //调用PendingHandlerAddedTask内部的channelHandler#handlerAdded,
        //ChannelInitializer的handlerAdded内部继续调用initChannel方法方法 
      //也就是会回调3.1.2.1 中 添加的ChannelInitializer.initChannel,回调的时候最终会将自身从pipeline中移除
      //这也就是为什么我们可以通过
      //childHandler 方法传入 ChannelInitializer,initChannel方法 反复调用 ch.pipeline().addLast(xxx);
      //来添加多个handler
        pipeline.invokeHandlerAddedIfNeeded();
        //标记注册完成 同时回到listner  doBind0绑定端口操作要么是在register结束后主线程中执行
          //或者 在此处回调listner执行
        safeSetSuccess(promise);
        //遍历执行pipeline中的ChannelInboundHandler类型的 channelHandler#channelRegistered方法 
           //head- >我们自定义的handler -> tail 
        pipeline.fireChannelRegistered();
        //判断 javaChannel().socket().isBound() 此时流程还未走到将ServerSocket绑定到address上
        //这里我们注册的是NioServerSocketChannel NioSocketChannel也会调用这个方法进行注册,
         //它的判断依据是SocketChannel(jdk)是否保持连接,所以会立即执行
        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
       ...
    }
}

AbstractNioChannel#doRegister 调用jdk的注册方法

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            //eventLoop() 是 3.1.3 中AbstractChannel.AbstractUnsafe#register赋值的,
               //也就是ServerBootStrap的group中 children第一个NioEventloop
           //本质上就是调用jdk 注册方法,并将自身NioServerSocketChannel作为attachment绑定起来
            selectionKey = javaChannel().register(eventLoop().selector, 0, this);
            return;
        } catch (CancelledKeyException e) {
           ...
        }
    }
}

总结:注册完成的逻辑比较多。不仅是将jdk的channel注册到selector上,同时有可能回调bind操作,做端口绑定。

  1. NioServerSocketChannel完成初始化后,需要完成register操作。NioEventloopGroup从自身维护的NioEventloop[]数组中取出一个,将 AbstractUnsafe#register0方法交给NioEventloop处理(加到taskQuene中),同时将NioEventloop指向给NioServerSocketChannel.此时NioEventloop内部持有的线程为null,调用DefaultThreadFactory创建一个线程,将线程指向给NioEventloop.线程内部开始执行NioEventloop中的run方法,其中就有真正的register操作。
  2. 那么回到AbstractUnsafe#register0,调用SelectableChannel#register 注册方法,将ServerSocketChannel(jdk)注册到selector,同时将NioSercerSocketChannel作为attachment绑定起来。
  3. 之后回调上文提到的PendingHandlerAddedTask,即ChannelInitializer.initChannel方法,此方法会将我们自定义的handler加入到pipeline中,同时还会将添加ServerBootstrapAcceptor这样一个handler到pipeline的任务加入到taskQuene中,等待后续执行。
  4. 标记注册完成,此时可能会处理bind操作的回调方法,之所以是可能,因为bind操作时在主线程,有可能此线程执行完毕,主线程尚未走到bind操作,也就没有回调,届时bind操作在主线程完成,否则在此处回调 无论如何,bind操作都是将真正的任务放到taskQuene中,等待后续异步执行
  5. 调用pipeline.fireChannelRegistered();
  6. NioServerSocketChannel和NioSocketChannel register操作都是调用的这个方法。在接下来的这一步NioServerSocketChannel直接返回了,因为bind操作还未完成。而NioSocketChannel的判断依据是SocketChannel(jdk)是否保持连接,所以会立即执行,调用pipeline.fireChannelActive();这个方法后续会有介绍,其实就是将感兴趣的操作注册到selector上。

3.2 绑定端口 doBind0

AbstractBootstrap#doBind0

private static void doBind0(
        final ChannelFuture regFuture, final Channel channel,
        final SocketAddress localAddress, final ChannelPromise promise) {
    //再度往taskQueue中提交任务 前面还有两次操作 register0 和 添加ServerBootstrapAcceptor 
    channel.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            if (regFuture.isSuccess()) {
             //内部其实是调用pipeline 从tail节点开始向前查找outbound类型的节点,
              //最后查找到head节点调用其bind方法.内部实际上调用的是unsafe.bind
                channel.bind(localAddress,  promise)
                //添加一个回调 如果绑定失败 就关闭channel
                .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                promise.setFailure(regFuture.cause());
            }
        }
    });
}

AbstractChannel.AbstractUnsafe#bind

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
     
    ... 判断是否已经绑定端口
    boolean wasActive = isActive();
    try {
        //基于java版本 7及以上使用sockectChannel 否则使用 使用socket 
        doBind(localAddress);
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        closeIfClosed();
        return;
    }

    if (!wasActive && isActive()) {
        //依然是 向NioServerSocketChannel绑定的NioEventLoop中 taskQuene提交任务
        invokeLater(new Runnable() {
            @Override
            public void run() {
                //回调active并开启监听  
                pipeline.fireChannelActive();
            }
        });
    }
    //设置状态  回调listner
    safeSetSuccess(promise);
}

3.2.1 回调active并开启监听 fireChannelActive

DefaultChannelPipeline#fireChannelActive

public void channelActive(ChannelHandlerContext ctx) throws Exception {
   //从head节点开始,其channelActive方法 查找下一个Inbound节点,回调其 channelActive方法,
   /这也就是为什么我们要在示例中  channelActive方法末尾要添加 super.channelActive(ctx); 传播active事件
    ctx.fireChannelActive();
    //默认从tail节点向上查找outbound类型节点,找到head,调用read方法,内部又调用unsafe.beginRead(),
      //最终调用到 下方 AbstractNioChannel#doBeginRead 方法
    readIfIsAutoRead();
}

AbstractNioChannel#doBeginRead

protected void doBeginRead() throws Exception {
    final SelectionKey selectionKey = this.selectionKey;
    readPending = true;
    //这里interestOps 为0 因为我们还没有把将SelectionKey.OP_ACCEPT绑定上去
    //每轮读完成后也就是 channelRead方法完成后,传播channelReadComplete事件后,head节点还会调用此方法 当然 
       //此时已经注册了OP_ACCEPT事件,if不成立,不会反复注册
    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
       //注册 SelectionKey.OP_ACCEPT  至此  就绪完毕 socket可以检测新连接的接入了
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

总结:bind操作是异步执行的,因为不知何时完成register操作。bind操作主要就是绑定端口,内部会判断java版本, 7及以上使用sockectChannel 否则使用 使用socket 。绑定端口之后,调用pipeline.fireChannelActive();将感兴趣的操作注册到selector上, ServerSocketChannel监听连接事件,SocketChannel监听读事件,至此,channel就绪完毕,可以处理连接请求了。(SocketChannel可以处理请求数据了)

总结

时序图就不画了,大纲其实看目录的结构就知道了。

  1. 创建EventloopGroup其实就是在创建Reactor线程池,我们创建了两个,使用的是主从多线程模型,一个负责处理连接,一个负责处理各个连接的读写事件。每个reactor线程都有独立的 Selector 对象。

  2. NioServerSocketChannel,NioSocketChannel对应的就是jdk的ServerSocketChannel和SocketChannel.每个channel都维护有pipeline,netty使用了pipeline可以让开发者加入自定义的handler,来处理请求的各个阶段。

在下一章,我们看看netty的主从reactor线程如何联动,充分发挥cpu的效能。以及reactor线程如何兼顾事件的监听,处理io事件和定时任务。