Channel的register流程

学习目标

  • 掌握Channel的register的本质
  • 掌握Channel的register流程

Channel的register的本质

本质就是将channel注册到线程NioEventLoop中的Selector上,让Selector帮我们监听感兴趣的IO事件(accept,read,write,connect)。如图所示:image

Channel的register流程

Channel的register流程分为 启动时Channel的register流程 新连接Channel的register流程 两大部分内容。

启动时Channel的register流程

研究server和client端的register流程代码入口分别为:ServerBootstrap#bind方法和Bootstrap#connect方法。无论server端还是client端入,它们最终都会调到AbstractBootstrap#initAndRegister方法,我们直接看这个方法。

// AbstractBootstrap 类
final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // 创建一个NioServerSocketChannel实例
        channel = channelFactory.newChannel();
        // 初始化channel配置
        init(channel);
    } catch (Throwable t) {
        ......
    }
    // 服务端channel注册到bossGroup线程池,由bossGroup线程池来处理accept事件
    ChannelFuture regFuture = config().group().register(channel);
    ......
}

从config().group().register(channel);这行代码开始register流程,这个register方法调用的就是NioEventLoopGroup父类MultithreadEventLoopGroup的register方法。

// MultithreadEventLoopGroup 类
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}
// next()方法
public EventLoop next() {
    return (EventLoop) super.next();
}

其中的next()方法是通过选择器chooser选择一个线程NioEventLoop,调用该线程的register方法,把channel注册到上去,让我们来看看该线程的register方法,其实调用的是父类SingleThreadEventLoop的register方法。

// SingleThreadEventLoop类
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    // 调用AbstractChannel的register方法,当前线程NioEventLoop作为参数,传递过去了
    promise.channel().unsafe().register(this, promise);
    return promise;
}

接下来,我们在看下AbstractChannel的register方法:

// AbstractChannel类
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
       ......
    AbstractChannel.this.eventLoop = eventLoop;
    // 判断当前线程和EventLoop中的线程是不是同一个线程。
    // 启动时,当前线程是main主线程,EventLoop中的线程为null,不是同一个线程
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
        try {
               // 把register当成一个任务,提交给线程池eventLoop执行
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            ......
        }
    }
}

由于当前线程和EventLoop不是同一个线程,随后把register操作当成一个任务,提交给线程池eventLoop了,eventLoop是个单线程的线程池。顺便来看下execute方法吧。

// SingleThreadEventExecutor类
private void execute(Runnable task, boolean immediate) {
    boolean inEventLoop = inEventLoop();
    // 添加到队列中
    addTask(task);
    // 启动时,肯定不是同一个线程
    if (!inEventLoop) {
        // 启动一个线程执行
        startThread();
        ......
    }
    ......
}
private void doStartThread() {
        ......
        // executor是ThreadPerTaskExecutor,在创建NioEventLoop时传的进来的 
        executor.execute(new Runnable() {
            @Override
            public void run() {
                // 设置当前线程
                thread = Thread.currentThread();
                try {
                    // 执行run方法,调用的是NioEventLoop的run方法,
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                }
        });
    }

我们可以了解到NioEventLoop创建时,并没有立即启动线程执行,进入execute方法,就是想告诉大家这个,让我们在回到register0方法。

// AbstractChannel 类
private void register0(ChannelPromise promise) {
    ......
    // 调用AbstractNioChannel#doRegister方法
       doRegister();
    ......
}
// AbstractNioChannel类
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // channel注册到selector,但是监听事件为0。
            // 对server来说,监听事件应该是ACCEPT
            // 对client来说,监听事件应该是READ
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {

        }
    }

在这篇文章《Netty源码-Channel讲解》中讲到创建channel时,ACCEPT/READ事件以参数形式传递给父类AbstractNioChannel的readInterestOp成员变量,什么时候把ACCEPT/READ事件绑定到Selector上呢?不再展开细说了,最终会在AbstractNioChannel的doBeginRead方法中绑定到Selector上,有兴趣的同学,可以按照下面的流程打断点跟踪下。

AbstractBootstrap#doBind0-->....-->DefaultChannelPipeline#bind-->...

--> DefaultChannelPipeline.HeadContext#bind

--> AbstractChannel#bind(){...pipeline.fireChannelActive()...}

--> DefaultChannelPipeline.HeadContext#channelActive(){...readIfIsAutoRead()...}--> ....

---> DefaultChannelPipeline.HeadContext#read

--> AbstractNioChannel#doBeginRead(){...selectionKey.interestOps(interestOps | readInterestOp);}

Channel的register流程源码先分析到这,本篇只研究了启动时Channel的register流程,还剩下新连接Channel的register流程没有分析,等研究完线程EventLoop后,在续上剩下的部分。

总结

  • register的本质是channel注册到selector上,而不是EventLoop上,selector帮忙监听ACCEPT/READ事件。
  • 线程EventLoop创建时,没有立即运行,而是在第一个任务提交时运行的

results matching ""

    No results matching ""