背景
本文介绍Netty的通道组件NioServerSocketChannel和NioSocketChannel,从源码的角度介绍其实现原理。
1.NioServerSocketChannel
Netty本质是对NIO的封装和增强,因此Netty框架中必然包含了对于ServerSocketChannel的构建、配置以及向选择器注册,如下所示:
java
复制代码
// 创建ServerSocketChannel对象
ServerSocketChannel serverSocketChannel = SelectorProvider.provider().openServerSocketChannel();
// ServerSocketChannel通道设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 将ServerSocketChannel通道注册至选择器
serverSocketChannel.register(Selector, opts, attachment);
// 接收客户端连接得到SocketChannel通道
SocketChannel socketChannel = serverSocketChannel.accept();
其中的构建和配置过程发生在NioServerSocketChannel的实例化过程。
1.1 NioServerSocketChannel构造函数
NioServerSocketChannel实例化过程包含了对serverSocketChannel的创建以及配置
Netty启动时,通过反射调用NioServerSocketChannel的无参构造函数创建NioServerSocketChannel对象.
java
复制代码
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
DEFAULT_SELECTOR_PROVIDER是Provider对象,用于创建通道和选择器,newSocket方法返回一个ServerSocketChannel对象,如下所示:
java
复制代码
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
NioServerSocketChannel中还维护了一个config对象用于储存该通道相关的配置,后续通过通道对象的config()
方法获取该config对象。
继续调用父类的构造方法:
java
复制代码
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
logger.warn("Failed to close a partially initialized socket.", e2);
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
// super(parent)内容如下:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
因此NioServerSocketChannel中包含如下属性:
1\] SelectableChannel ch:实际为ServerSocketChannel类型,即NIO中的服务端通道类型,并将其配置为非阻塞类型,以便后续向选择器注册;
\[2\] int readInterestOp: 值固定为SelectionKey.OP_ACCEPT,表示仅处理连接事件;
\[3\] pipeline: Netty的Pipeline组件,每个channel都有一个属于自己的Pipeline对象;
\[4\] unsafe: 对底层IO进行了封装,实际的读写操作在该类中进行处理;
\[5\] 其他: id唯一ID标识,parent固定为空。
### 1.2 NioServerSocketChannel注册
> NioServerSocketChannel包含了ServerSocketChannel对象,向选择器注册NioServerSocketChannel本质是将ServerSocketChannel注册到选择器
在Netty启动流程流程中,依次构造ServerSocketChannel, 并注册到选择器上,具体逻辑为:
```java
// NioServerSocketChannel的父类AbstractNioChannel中
// 删除try-catch异常逻辑
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
}
}
```
其中: javaChannel()获取NioServerSocketChannel对象的ServerSocketChannel属性;eventLoop().unwrappedSelector()为NioEventLoop这个线程绑定的选择器;此处的this表明将ServerSocketChannel注册到选择器上时,将当前的NioServerSocketChannel对象作为attachment保存到SelectionKey中,并使用`volatile SelectionKey selectionKey;`属性保存了注册结果。
说明:后续选择器会执行select而阻塞,当该选择器被IO事件唤醒时,可通过SelectionKey的attachment获取NioServerSocketChannel对象,从而可以获取包括ServerSocketChannel、Pipeline、Config等其他所有相关信息。
### 1.3 NioServerSocketChannel处理连接
章节1.1中提到了NioServerSocketChannel的unsafe属性,unsafe用于封装底层具体的IO行为,具体的实现类为NioMessageUnsafe.
当有连接请求到达NioServerSocketChannel后,进入NioMessageUnsafe的read()方法中(详细的调用流程和线程处理关系在后续Netty的消息处理流程中介绍, 这里仅对read方法实现逻辑进行说明),read方法省去内存分配优化策略以及异常处理逻辑后的主线逻辑如下:
```java
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List