NioSocketChannel 与 NioServerSocketChannel。
网上很多资料都将创建的 NioEventLoopGroup 对象称之为: bossGroup(boss) 和 workGroup(work),这里OP不会这么称呼,而是采用源码中变量的命名,即: parentGroup和 childGroup。
本文 Netty 源码解析基于 4.1.86.Final 版本。
1. 继承关系
NioSocketChannel 与 NioServerSocketChannel 继承关系如下:
2. NioServerSocketChannel
parentGroup 中管理的 Channel 是 NioServerSocketChannel。
2.1. 创建时机
在服务端 ServerBootstrap 启动时,Netty 会创建、初始化 NioServerSocketChannel,初始化时会在 NioServerSocketChannel 的 pipeline 中添加一个 ServerBootstrapAcceptor 处理器,然后将 NioServerSocketChannel 注册到 parentGroup 中某个 NioEventLoop 的 Selector(多路复用器)[1] 上。
更多细节,详阅: Netty_ServerBootstrap启动过程 和 Netty_ServerBootstrapAcceptor 。
2.2. NioServerSocketChannel构造函数
NioServerSocketChannel 在服务端 ServerBootstrap 启动时,通过 ReflectiveChannelFactory 中的反射方法,通过无参构造函数创建的。
看下构造函数相关源码:
1 | // io.netty.channel.socket.nio.NioServerSocketChannel |
line: 5 -> 创建
java.nio.channels.spi.SelectorProvider
[2],用于创建 JDK NIO 原生 ServerSocketChannel [1]。line: 28、line: 10 -> newChannel 方法创建 JDK NIO 原生 ServerSocketChannel 实例。
line: 31 -> 调用父类构造函数。注意,调用父类构造函数时传递了
SelectionKey.OP_ACCEPT
标识用于设置监听, 说明 NioServerSocketChannel 关注 OP_ACCEPT 事件。line: 32 -> 创建 NioServerSocketChannel 的配置类 NioServerSocketChannelConfig,其中封装了对 NioServerSocketChannel 底层的一些配置行为和
java.net.ServerSocket
,以及创建 NioServerSocketChannel 用来接收数据的 Buffer 分配器 ServerChannelRecvByteBufAllocator。
从 line:31 ,继续向上看看父类构造函数中做了什么。
2.3. 父类构造函数
AbstractNioMessageChannel
此父类的构造函数中并无其他逻辑操作,继续调用上层父类构造函数。
1
2
3protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}单独看 AbstractNioMessageChannel 类,主要是对 NioServerSocketChannel 底层读写行为的封装和定义,比如accept接收客户端连接等。
AbstractNioChannel
此父类的构造函数中,设置了相关参数;
line: 6 -> 将
java.nio.channels.ServerSocketChannel
设置成非阻塞1
2
3
4
5
6
7
8protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) { ... }
}AbstractChannel
此父类构造函数中负责创建 unsafe 和 pipeline。
1
2
3
4
5
6
7
8protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
// unsafe 用于定义实现对 Channel 的底层操作
unsafe = newUnsafe();
// 为 channel 分配独立的 pipeline 用于IO事件编排
pipeline = newChannelPipeline();
}line: 5 -> 创建 NioServerSocketChannel 底层操作类 Unsafe,这里创建的是
AbstractNioMessageChannel.NioMessageUnsafe
。line: 7 -> 为 NioServerSocketChannel 分配独立的 pipeline 用于IO事件编排。pipeline 其实是一个 ChannelHandlerContext 类型的双向链表。头结点 HeadContext ,尾结点 TailContext 。 ChannelHandlerContext 中包装着 ChannelHandler。
至此,通过 NioServerSocketChannel 相关构造函数可以得知以下信息:
- NioServerSocketChannel 中包含
java.nio.channels.ServerSocketChannel
。 - NioServerSocketChannel 关注 OP_ACCEPT 事件。
- NioServerSocketChannel 是非阻塞的。
2.4. NioMessageUnsafe
Unsafe 作为 Channel 的内部类,承担着 Channel 网络相关功能的具体实现,比如读写操作,之所以命名为 Unsafe 是不希望外部使用,并非是不安全的。
NioServerSocketChannel 对应的 Unsafe 实例,在父类 AbstractNioMessageChannel 中进行创建。
1 | // io.netty.channel.nio.AbstractNioMessageChannel#newUnsafe |
NioMessageUnsafe 是 AbstractNioMessageChannel 的内部类;
1 | // io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read |
line: 4 -> 属性名称虽然是 readBuf,但类型是 Object,并且集合中存储的是 NioSocketChannel。
line: 25 -> 调用子类实现的 NioServerSocketChannel#doReadMessages 方法,获取客户端Socket连接,并创建 NioSocketChannel 。
line: 20 ~ line: 37 -> 注意是 do…while… 循环,每次循环不超过16次。
line: 40 ~ line: 47 -> 循环处理存储在 readBuf 中的 NioSocketChannel,通过 NioServerSocketChannel 的 pipeline 传播 ChannelRead 事件,即调用:
ServerBootstrap.ServerBootstrapAcceptor#channelRead
方法。line: 48 ~ line:67 -> 后续清理逻辑。
line: 80 -> 创建 NioSocketChannel,包装客户端连接(
java.nio.channels.SocketChannel
)。
至此,通过 NioMessageUnsafe 可以得知以下信息:
- NioMessageUnsafe 每次循环不超过16次,换个角度来说,每次循环最多创建16个 NioSocketChannel。
- NioMessageUnsafe 会依次处理客户端连接,最后统一触发 ChannelRead 事件,即通过 NioServerSocketChannel 的 pipeline 传播 ChannelRead 事件。
3. NioSocketChannel
childGroup 中管理的 Channel 是 NioSocketChannel 。
3.1. 创建时机
当客户端连接到达(OP_ACCEPT 事件),parentGroup 中 NioEventLoop 就会调用 NioServerSocketChannel 所属的 NioMessageUnsafe#read 方法进行处理。在 NioMessageUnsafe#read 方法中,会创建 NioSocketChannel。
更多细节详阅: NioMessageUnsafe(点击跳转) 。
3.2. NioSocketChannel构造函数
参数列表中的 socket 是 java.nio.channels.SocketChannel
。
1 | public NioSocketChannel(Channel parent, SocketChannel socket) { |
从 line: 2 ,继续向上看看父类中做了什么。
3.3. 父类构造函数
AbstractNioByteChannel
1
2
3protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}此父类的构造函数中并无其他逻辑操作,继续调用上层父类构造函数。
需要注意的是,继续向上调用父类构造函数时传递了
SelectionKey.OP_READ
标识用于设置监听,说明 NioSocketChannel 关注 OP_READ 事件。AbstractNioChannel
此父类的构造函数中,设置了相关参数;
line: 6 -> 将
java.nio.channels.ServerSocketChannel
设置成非阻塞1
2
3
4
5
6
7
8protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) { ... }
}AbstractChannel
此父类构造函数中负责创建 unsafe 和 pipeline。
1
2
3
4
5
6
7
8protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
// unsafe 用于定义实现对 Channel 的底层操作
unsafe = newUnsafe();
// 为 channel 分配独立的 pipeline 用于IO事件编排
pipeline = newChannelPipeline();
}line: 5 -> 创建 NioSocketChannel 底层操作类 Unsafe,这里创建的是
AbstractNioByteChannel.NioByteUnsafe
。line: 7 -> 为 NioSocketChannel 分配独立的 pipeline 用于IO事件编排。pipeline 其实是一个 ChannelHandlerContext 类型的双向链表。头结点 HeadContext ,尾结点 TailContext 。 ChannelHandlerContext 中包装着 ChannelHandler。
3.4. NioByteUnsafe
Unsafe 作为 Channel 的内部类,承担着 Channel 网络相关功能的具体实现,比如读写操作,之所以命名为 Unsafe 是不希望外部使用,并非是不安全的。
NioSocketChannel 对应的 Unsafe 实例,在父类 AbstractNioByteChannel 中进行创建。
1 | // io.netty.channel.nio.AbstractNioByteChannel#newUnsafe |
NioByteUnsafe 是 AbstractNioByteChannel 的内部类,NioByteUnsafe 类中有三个方法,这里关注 read 方法,另两个就不贴了。
1 | // io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read |
line: 7 ~ line: 10 -> 获取 byteBuf 分配器,清空并重置。
line: 17 -> 为 byteBuf 分配空间。
line: 19 -> doReadBytes 方法,从
java.nio.channels.SocketChannel
读取数据到 ByteBuf 。line: 37 -> 通过 Channel 的 pipeline 传播 ChannelRead 事件。
line: 15 ~ line: 39 -> 注意是 do…while… 循环。
至此,通过 NioByteUnsafe 可以得知以下信息:
- byteBuf 每次都会重置清空。
- 每读取到一定字节就触发 ChannelRead 事件,而不是全部读取完再触发。
4. 小结
NioServerSocketChannel 是非阻塞的, 并且关注 OP_ACCEPT 事件, 具体读写细节在内部类 NioMessageUnsafe 中,会依次处理客户端连接最后统一触发 ChannelRead 事件。
NioSocketChannel 是非阻塞的, 并且关注 OP_READ 事件, 具体读写细节在内部类 NioByteUnsafe 中,每读取到一定字节就触发 ChannelRead 事件。
NioServerSocketChannel 要求高吞吐量,尽可能多的接受客户端连接; 而 NioSocketChannel 要求尽可能快的响应远端请求。
5. Reference
- 1.java.nio.channels.Selector(多路复用器) 可以看成是操作系统底层中 select/epoll/poll 多路复用函数的Java包装类。只要 Selector 监听的任一一个 Channel 上有事件发生,就可以通过 Selector.select() 方法监测到,并且使用 SelectedKeys 可以得到对应的 Channel,然后对它们执行相应的I/O操作。 ↩
- 2.java.nio.channels.spi.SelectorProvider 相当于一个工厂类,提供了对 java.nio.channels.ServerSocketChannel、java.nio.channels.SocketChannel、java.nio.channels.Selector 等创建方法。 ↩