Netty 权威指南笔记(六):Channel 解读
《Netty 权威指南》书上使用的源码是 Netty5 的,但是 Netty5 已经被废弃了,所以本文是参照 Netty4.1 的源码解读的。
JDK 的 NIO 类库中,提供了 SocketChannel 和 ServerSocketChannel 用于非阻塞 I/O 操作。类似于 NIO 的 Channel,Netty 提供了自己的 Channel 和其子类实现。
功能说明
io.netty.channel.Channel 是 Netty 的网络操作抽象类,聚合了一组功能,包括但不限于网络读写、客户端发起连接、主动关闭连接,同时也包含了 Netty 框架相关的一些功能,包括获取 Channel 的 EventLoop,获取缓冲区分配器 ByteBufAllocator 和 pipeline 等。
为了 Netty 不使用 NIO 的原生 Channel,而是要另起炉灶呢?主要原因如下:
1. JDK 的 SocketChannel 和 ServerSocketChannel 没有统一的 Channel 接口供业务开发者使用。对用户而言,没有统一的操作视图,使用起来不方便。
2. JDK 的 SocketChannel 和 ServerSocketChannel 是 SPI 类接口,通过继承来扩展很不方便,不如开发一个新的。
3. Netty 的 Channel 需要能跟 Netty 架构融合在一起。
4. 自定义 Channel 功能实现会更灵活。
基于以上原因,Netty 重新设计了 Channel,其主要设计理念如下:
1. 在 Channel 接口层,采用 Facade 模式统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供。
2. Channel 接口定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度上实现功能和接口的重用。
3. 具体实现采用聚合而非包含的方式,Channel 负责统一分配和调度,更加灵活。
Netty 的 Channel 都有哪些功能呢?
1. 常见的网络 IO 操作:读、写、绑定端口、连接、关闭连接等。
2. 获取 EventLoop。
3. 获取 parent Channel,对于服务端 SocketChannel 来说,parent 就是创建它的 ServerSocketChannel。
4. 唯一标志 id。
5. 元数据 metadata,获取 TCP 参数配置等。
源码分析
继承关系类图
NioServerSocketChannel、NioSocketChannel 两者都继承了 Channel、AbstractChannel、AbstractNioChannel。
AbstractChannel
主要成员变量如下所示:
1. 父 Channel。
2. 全局唯一 id。
3. Unsafe 实例。
4. 当前 Channel 对应的 DefaultChannelPipeline。
5. EventLoop。
6. 本地和远程地址。
private final Channel parent;
private final ChannelId id;
private final Unsafe unsafe;
private final DefaultChannelPipeline pipeline;
private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
private final CloseFuture closeFuture = new CloseFuture(this);
private volatile SocketAddress localAddress;
private volatile SocketAddress remoteAddress;
private volatile EventLoop eventLoop;
private volatile boolean registered;
private boolean closeInitiated;
/** Cache for the string representation of this channel */
private boolean strValActive;
private String strVal;
AbstractChannel 中的网络 I/O 操作都是调用 pipeline 中的对应方法,继而由 pipeline 调用 ChannelHandler 进行处理。
@Override
public ChannelFuture bind(SocketAddress localAddress) {
return pipeline.bind(localAddress);
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress) {
return pipeline.connect(remoteAddress);
}
@Override
public ChannelFuture write(Object msg) {
return pipeline.write(msg);
}
AbstractNioChannel
主要成员变量有:
1. SelectableChannel:这是一个 Java NIO SocketChannel 和 ServerSocketChannel 的公共父类,放在这里是因为 AbstractNioChannel 也是 NioSocketChannel 和 NioServerSocketChannel 的公共父类。
2. readInterestOp:代表 JDK SelectionKey 的 OP_READ。
3. SelectionKey:Channel 注册到 EventLoop(Selector)时返回的 key,修改它可以改变感兴趣的事件。
4. connectPromise:代表连接操作结果。
5. connectTimeoutFuture:连接超时定时器。
6. requestedRemoteAddress:connect 时的远程地址。
private final SelectableChannel ch;
protected final int readInterestOp;
volatile SelectionKey selectionKey;
boolean readPending;
/**
* The future of the current connection attempt. If not null, subsequent
* connection attempts will fail.
*/
private ChannelPromise connectPromise;
private ScheduledFuture<?> connectTimeoutFuture;
private SocketAddress requestedRemoteAddress;
AbstractNioChannel 类里比较重要的方法是 doRegister,该方法负责将 Channel 注册到多路复用器 Selector。
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch