回顾

Netty version:4.1.6

前两节的代码都是集中在initAndRegister()函数中的这节也不例外,只是我们继续往下看罢了。

Selector是什么

在了解Netty注册selector之前,我们得要先知道Selector是个啥,关于这点,我们不妨看看java 8文档中的selector,从官方的一些简介和它的函数我们不难发现,selector其实就是一个负责管理Channel的东西,网上也有人称它为多路复用器

它还可以检查一个或多个Channel(通道)的状态是否处于可读、可写。

为了更好的理解selector是个什么东西,我拿来之前的一张图:


注册Selector大致流程



开始追踪源码

让我们视角再次回到initAndRegister()函数中,如果忘记怎么进到这里面的,可以回到第一节中再看看。

现在我们先看看initAndRegister,在里面我们可以找到一个register的函数:

上面的register函数最终会调用到

io.netty.channel.AbstractChannel.AbstractUnsafe#register,

由于这个调用过程比较繁琐,我这里只列出一些关键步骤,详情还请去源码打断点:

  • 进入io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)。注意,这一步会遇到一个next方法,这个方法是分配NioEventLoop的,并且这个NioEventLoop是属于childGroup(workerGroup),如感兴趣,请看【创建选择器】
  • 进入io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
  • 进入io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.ChannelPromise),在这里可以看到以下代码:
java
  • 01
promise.channel().unsafe().register(this, promise);
  • 上面的register进去以后,就是:
    io.netty.channel.AbstractChannel.AbstractUnsafe#register
  • 此处还注册了NioEventLoop

现在将视角切换到io.netty.channel.AbstractChannel.AbstractUnsafe#register,看看函数部分源码:

可以看到上图的源码中出现了流程图中的两个步骤:eventLoop和resgiter0。并且可以看到断点中eventLoop就是我们自定义的NioEventLoop(Group)。

至于eventLoop具体干什么,先在这留个印象,以后再继续补充。

纠正,这里的register0方法不一定是调用if代码块中的register0方法,因为上面也说了next方法负责分配NioEventLoop,是不是if代码块取决于next方法的分配,以前截图的时候截错了,后期博客可能对这一块还有补充。


之后,我们将视角转移到上图的register0函数中,就可以发现流程图中剩下的三个函数:
io.netty.channel.AbstractChannel.AbstractUnsafe#register0

上图中还有个要注意的点,因为此时Netty还没绑定端口,所以此时isActive()还是false,而pipeline.fireChannelActive();没有被执行,也即ChannelInboundHandlerAdapter的channelActive函数其实还没被调用。

关于channelActive是哪里执行的,这里就需要补上前面章节没有补上的ServerHandler了:
ServerHander.java

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.concurrent.TimeUnit; public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { System.out.println("channelActive"); } @Override public void channelRegistered(ChannelHandlerContext ctx) { System.out.println("channelRegistered"); } @Override public void handlerAdded(ChannelHandlerContext ctx) { System.out.println("handlerAdded"); } @Override public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { super.channelRead(ctx, msg); new Thread(new Runnable() { @Override public void run() { // 耗时的操作 String result = loadFromDB(); ctx.channel().writeAndFlush(result); ctx.executor().schedule(new Runnable() { @Override public void run() { // ...do something } }, 1, TimeUnit.SECONDS); } }).start(); } private String loadFromDB() { return "hello world!"; } }

然后执行Server.java(代码上一节有,这里就不重复贴了),执行结果如下:

上上面说的channelActive,其实就是指上面ServerHandler.java的channelActive函数,而其中handlerAdded和channelRegistered事件对应的就是invokeHandlerAddedIfNeeded()和fireChannelRegistered()。




好了,现在将视角重新转回:io.netty.channel.AbstractChannel.AbstractUnsafe#register0

这里面有三个流程图中的函数:

  • doRegister();
  • invokeHandlerAddedIfNeeded();
  • fireChannelRegistered();

doRegister

我们进到doRegister函数源码:
io.netty.channel.nio.AbstractNioChannel#doRegister

java
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().selector, 0, this); return; } catch (CancelledKeyException e) { if (!selected) { eventLoop().selectNow(); selected = true; } else { throw e; } } } }

可以发现这里底层是用jdk的api注册并绑定selector的,参数ops=0表示现在不关注任何事件(绑定端口后会关注accept事件)。

javaChannel().register(eventLoop().selector, 0, this);中的this,最终会被包装成一个叫Attachment的东西,以后可能用到,就在这里记一记,看到Attachment可以当做是Channel。

如果我们再点进上面的javaChannel()函数,可以看见如下代码:
io.netty.channel.nio.AbstractNioChannel#javaChannel

java
  • 01
  • 02
  • 03
protected SelectableChannel javaChannel() { return ch; }

上面的ch类型为SelectableChannel,其实就是在【创建服务端Channel】那一节创建的,具体从下面这段代码开始:
io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel(java.nio.channels.ServerSocketChannel)

java
  • 01
new NioServerSocketChannelConfig(this, javaChannel().socket());

最终到AbstractNioChannel中的this.ch=ch;

至于流程图中最后两个函数:invokeHandlerAddedIfNeeded和fireChannelRegistered这里就先不解析源码了,目前只需要知道它们操作最终体现就是上面Server.java执行结果中的前两行,或者先按照函数名或源码注释理解它们大概做了什么也行。