Netty的AbstractNioChannel源码解析

Netty的AbstractNioChannel源码解析教程

从前面章节的类关系图上面我们可以了解到 AbstractNioChannel 继承 AbstractChannel。AbstractChannel 里面的变量 AbstractNioChannel 都拥有,并且它会实现部分 AbstractChannel 的抽象方法。

AbstractNioChannel的成员变量

AbstractNioChannel 的成员变量大体如下:

private static final ClosedChannelException DO_CLOSE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( new ClosedChannelException(), AbstractNioChannel.class, "doClose()"); private final SelectableChannel ch; protected final int readInterestOp; volatile SelectionKey selectionKey; boolean readPending; private final Runnable clearReadPendingRunnable = new Runnable() { @Override public void run() { clearReadPending0(); } }; private ChannelPromise connectPromise; private ScheduledFuture<?> connectTimeoutFuture; private SocketAddress requestedRemoteAddress;
  1. 在 Netty 中,NioScoketChannel 和 NioServerSocketChannel 有需要共用的地方,所以就需要 SelectableChannel 登场。 SelectableChannel 是 java.nio.SocketChannel 和 java.nio.ServerSocketChannel 的公共父类,它主要用于设置 SelectableChannel 参数和进行 I/O 操作。
  2. readInterestOp 该参数和 JDK SelectionKey 的 OP_READ 一样。
  3. selectionKey 是 Channel 注册到 EventLoop 后返回的选择键。
  4. connectPromise 和 connectTimeoutFuture 它们分别表示了请求的连接操作结果和连接超时定时器。

核心方法

doRegister()源码

protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) { if (!selected) { // Force the Selector to select now as the "canceled" SelectionKey may still be // cached and not removed because no Select.select(..) operation was called yet. eventLoop().selectNow(); selected = true; } else { // We forced a select operation on the selector before but the SelectionKey is still cached // for whatever reason. JDK bug ? throw e; } } } }

JDK 的 SelectableChannel.register

public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;

代码中的 selected 是一个布尔类型的变量,它是用来标识注册操作是否成功,调用 SelectableChannel.register 方法,将当前的 Channel 注册到 EventLoop 的多路复用器上。在注册 Channel 的时候,需要指定监听的网络操作位来表示 Channel 对哪几类网络事件感兴趣,它具体的枚举如下:

  • public static final int OP_READ = 1 << 0; 读操作位
  • public static final int OP_WRITE = 1 << 2; 写操作位
  • public static final int OP_CONNECT = 1 << 3; 客户端连接服务端操作位
  • public static final int OP_ACCEPT = 1 << 4; 服务端接收客户端连接操作位

AbstractNioChannel 注册的是 0,说明对任何事件都不感兴趣,仅仅完成注册操作。注册的时候可以指定附件,后续Channel接收到网络事件通知时可以从 SelectionKey 中重新获取之前的附件进行处理,此处将 AbstractNioChannel 的实现子类自身当作附件注册。如
果注册 Channel 成功,则返回 selectionKey,通过 selectionKey 可以从多路复用器中获取 Channel 对象。

如果当前注册返回的 selectionKey 已经被取消,则抛出 CancelledKeyException 异常,捕获该异常进行处理。如果是第一次处理该异常,调用多路复用器的 selectNow() 方法将已经取消的 selectionKey 从多路复用器中删除掉。操作成功之后,将 selected 置为 true, 说明之前失效的selectionKey 已经被删除掉。继续发起下一次注册操作,如果成功则退出,如果仍然发生 CancelledKeyException 异常,说明我们无法删除已经被取消的 selectionKey,按照 JDK 的 API 说明,这种意外不应该发生。如果发生这种问题,则说明可能 NIO 的相关类库存在不可恢复的 BUG,直接抛出 CancelledKeyException 异常到上层进行统一处理。

doBeginRead()源码

@Override protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); } }

先判断下 Channel 是否关闭,如果处于关闭中,则直接返回。获取当前的 SelectionKey 进行判断,如果可用,说明 Channel 当前状态正常,则可以进行正常的操作位修改。将 SelectionKey 当前的操作位与读操作位进行按位与操作,如果等于 0,说明目前并没有设置读操作位,通过 interestOps | readInterestOp 设置读操作位,最后调用 selectionKey 的 interestOps 方法重新设置通道的网络操作位,这样就可以监听网络的读事件了。

Netty的AbstractNioChannel源码总结

AbstractNioChannel 继承自 AbstractChannel。它里面包含了 AbstractChannel 的所有变量,并且选择性的将一些公共的方法实现。