Netty之Channel(一)read与accept操作

这篇博客将对
(1). Netty NIO服务器读取(read)客户端数据的过程
(2). Netty NIO服务器接收(accept)客户端数据的过程
(3). Netty NIO客户端接收(read)服务器端数据结果
这三点进行分析。

客户端服务端 read 操作实现逻辑:
客户端自身使用了Netty NioSocketChannel,服务端在接受客户端连接请求后,创建客户端对应的Netty NioSocketChannel。

NioByteUnsafe实现了AbstractNioUnsafe抽象类:

protected class NioByteUnsafe extends AbstractNioUnsafe {

    public final void read() {/*...省略内部实现*/}

    private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close, RecvByteBufAllocator.Handle allocHandle) { /** 省略内部实现 **/ }

    private void closeOnRead(ChannelPipeline pipeline) { /** 省略内部实现 **/ }

}

这里一共有3个方法,第一个为read()方法,其他两个方法被他调用。

在NioEventLoop中processSelectedKey(SelectionKey k, AbstractNioChannel ch) 方法有:

// SelectionKey.OP_READ 或 SelectionKey.OP_ACCEPT 就绪
// readyOps == 0 是对 JDK Bug 的处理,防止空的死循环
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    unsafe.read();
}

在这里,当 (readyOps & SelectionKey.OP_READ) != 0 时,即就是NioSocketChannel所在的EventLoop的线程轮询到有新数据写入。再调用NioByteUnsafe中read() 方法,读取新写入的数据。

NioByteUnsafe中的read() 方法,读取新的写入数据。代码如下:

 @Override
 @SuppressWarnings("Duplicates")
  public final void read() {
      final ChannelConfig config = config();      // 这里若 inputClosedSeenErrorOnRead = true ,移除对 SelectionKey.OP_READ 事件的感兴趣。
     if (shouldBreakReadReady(config)) {
         clearReadPending();
         return;
     }
     final ChannelPipeline pipeline = pipeline();
     final ByteBufAllocator allocator = config.getAllocator();
     // 获得 RecvByteBufAllocator.Handle 对象
     final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
     // 重置 RecvByteBufAllocator.Handle 对象
     allocHandle.reset(config);
 
     ByteBuf byteBuf = null;
     boolean close = false; // 是否关闭连接
     try {
         do {
             // 申请 ByteBuf 对象
             byteBuf = allocHandle.allocate(allocator);
             // 读取数据
             // 设置最后读取字节数
             allocHandle.lastBytesRead(doReadBytes(byteBuf));
             // 如果未读取到数据
             if (allocHandle.lastBytesRead() <= 0) {
                 // 释放 ByteBuf 对象
                 // nothing was read. release the buffer.
                 byteBuf.release();
                 // ByteBuf 对象置空
                 byteBuf = null;
                 // 如果最后读取的字节为小于 0 ,说明对端已经关闭
                 close = allocHandle.lastBytesRead() < 0;
                 // TODO
                 if (close) {
                     // There is nothing left to read as we received an EOF.
                     readPending = false;
                 }
                 // 结束循环
                 break;
             }
 
             // 读取到数据 
             // 读取消息数量 + localRead
             allocHandle.incMessagesRead(1);
             // readPending
             readPending = false;
             // 触发 Channel read 事件到 pipeline 中。 
             pipeline.fireChannelRead(byteBuf);
             // ByteBuf 对象置空
             byteBuf = null;
         } while (allocHandle.continueReading()); // 循环判断是否继续读取
 
         // 读取完成
         allocHandle.readComplete();
         // 触发 Channel readComplete 事件到 pipeline 中。
         pipeline.fireChannelReadComplete();
 
         // 关闭客户端的连接
         if (close) {
             closeOnRead(pipeline);
         }
     } catch (Throwable t) {
         handleReadException(pipeline, byteBuf, t, close, allocHandle);
     } finally {
        
         // Check if there is a readPending which was not processed yet.
         // This could be for two reasons:
         // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
         // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
         //
         // See https://github.com/netty/netty/issues/2254
         if (!readPending && !config.isAutoRead()) {
             removeReadOp();
         }
     }
 }

上面调用RecvByteBufAllocator.Handle里lastBytesRead(int bytes) 方法,设置最后读取字节数。代码如下(部分):

// AdaptiveRecvByteBufAllocator.HandleImpl.java
@Override
public void lastBytesRead(int bytes) {
    // If we read as much as we asked for we should check if we need to ramp up the size of our next guess.
    // This helps adjust more quickly when large amounts of data is pending and can avoid going back to
    // the selector to check for more data. Going back to the selector can add significant latency for large
    // data transfers.
    if (bytes == attemptedBytesRead()) {
        record(bytes);
    }
    super.lastBytesRead(bytes);
}

// DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle.java
@Override
public void lastBytesRead(int bytes) {
    lastBytesRead = bytes; // 设置最后一次读取字节数 
    if (bytes > 0) {
        totalBytesRead += bytes; 
        // 总共读取字节数
    }
}

上面在调用 continueReading() 方法中,判断是否循环是否继续,读取新的数据:

// DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle.java
private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
    @Override
    public boolean get() {
        return attemptedBytesRead == lastBytesRead; // 最后读取的字节数,是否等于,最大可写入的字节数(一般情况下,最后读取的字节数不等于最大可写入的字节数)
    }
};

@Override
public boolean continueReading() {
    return continueReading(defaultMaybeMoreSupplier);
}

@Override
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
    return config.isAutoRead() &&
           (!respectMaybeMoreData || maybeMoreDataSupplier.get()) && // <1>
           totalMessages < maxMessagePerRead &&
           totalBytesRead > 0;
}

handleReadException:handleReadException(hannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close, RecvByteBufAllocator.Handle allocHandle) 方法,处理异常。代码如下:

 private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close, RecvByteBufAllocator.Handle allocHandle) {
 //若这里byteBuf非空,则说明在发生异常之前,至少申请ByteBuf对象是成功的
     if (byteBuf != null) {
         if (byteBuf.isReadable()) {
         //调用 ByteBuf#isReadable() 方法,判断 ByteBuf 对象是否可读,即剩余可读的字节数据。
             readPending = false;
             // 触发 Channel read 事件到 pipeline 中。
             pipeline.fireChannelRead(byteBuf);
         } else {
				//运行到这里说明ByteBuf对象不可读
             // 释放 ByteBuf 对象
             byteBuf.release();
         }
     }
     // 读取完成
     allocHandle.readComplete();
     // 触发 Channel readComplete 事件到 pipeline 中。
     pipeline.fireChannelReadComplete();
     // 触发 exceptionCaught 事件到 pipeline 中。
     pipeline.fireExceptionCaught(cause);
   
     if (close || cause instanceof IOException) {
         closeOnRead(pipeline);
     }
 }

pipeline中尾结点TailContext处理异常:打印告警日志,调用 ReferenceCountUtil.release(Object msg) 方法,释放和异常相关的资源。

// TailContext.java
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    onUnhandledInboundException(cause);
}

// DefaultChannelPipeline.java
protected void onUnhandledInboundException(Throwable cause) {
    try {
        logger.warn("An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " +
                        "It usually means the last handler in the pipeline did not handle the exception.",
                cause);
    } finally {
        ReferenceCountUtil.release(cause);
    }
}

doReadBytes(ByteBuf buf) 抽象方法,读取写入的数据到方法参数 buf 中。
它是一个抽象方法,定义在 AbstractNioByteChannel 抽象类中:

/**
 * Read bytes into the given {@link ByteBuf} and return the amount.
 */
protected abstract int doReadBytes(ByteBuf buf) throws Exception;

返回值为读取到的字节数,当返回值小于0时,即就是对端已关闭。
NioSocketChannel 对该方法的实现代码如下:

 @Override
 protected int doReadBytes(ByteBuf byteBuf) throws Exception {
     // 获得 RecvByteBufAllocator.Handle 对象
     final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
    // 设置最大可读取字节数量。因为 ByteBuf 目前最大写入的大小为 byteBuf.writableBytes()
     allocHandle.attemptedBytesRead(byteBuf.writableBytes());
     // 读取数据到 ByteBuf 中
     return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
 }

上面调用 ByteBuf.writeBytes(ScatteringByteChannel in, int length) 方法,读取数据到 ByteBuf 对象中。因为 ByteBuf 有多种实现,这里以默认的 PooledUnsafeDirectByteBuf 举例子。代码如下:

// AbstractByteBuf.java
@Override
public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
    ensureWritable(length);
    int writtenBytes = setBytes(writerIndex, in, length); // 调用 setBytes(int index, ScatteringByteChannel in, int length) 方法
    if (writtenBytes > 0) { //判断对端是否断开,断开返回-1,没断开返回读取数据的字节数。
        writerIndex += writtenBytes;
    }
    return writtenBytes;
}

// PooledUnsafeDirectByteBuf.java
@Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
    checkIndex(index, length);
    ByteBuffer tmpBuf = internalNioBuffer();
    index = idx(index);
    tmpBuf.clear().position(index).limit(index + length);
    try {
        return in.read(tmpBuf); //调用 Java NIO 的 read(ByteBuffer) 方法,读取数据到临时的 Java NIO ByteBuffer 中
    } catch (ClosedChannelException ignored) {
        return -1;
    }
}

accept操作:
在 NioEventLoop 的 processSelectedKey(SelectionKey k, AbstractNioChannel ch) 方法中:

// SelectionKey.OP_READ 或 SelectionKey.OP_ACCEPT 就绪
// readyOps == 0 是对 JDK Bug 的处理,防止空的死循环
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    unsafe.read();
}

当 (readyOps & SelectionKey.OP_ACCEPT) != 0 时,这就是服务端 NioServerSocketChannel 的 boss EventLoop 线程轮询到有新的客户端连接接入。接着,调用 NioMessageUnsafe.read() 方法,“读取"新的客户端连接连入。

(这里和上面看过的的read操作差不多嘛)

NioMessageUnsafe.read() 方法:


// SelectionKey.OP_READ 或 SelectionKey.OP_ACCEPT 就绪
// readyOps == 0 是对 JDK Bug 的处理,防止空的死循环
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    unsafe.read();
}

当 (readyOps & SelectionKey.OP_ACCEPT) != 0 时,这就是服务端 NioServerSocketChannel 的 boss EventLoop 线程轮询到有新的客户端连接接入。
接着,调用 NioMessageUnsafe.read() 方法,“读取”新的客户端连接连入。
NioMessageUnsafe#read() 方法,代码如下:

 private final class NioMessageUnsafe extends AbstractNioUnsafe {
 
     /**
      * 新读取的客户端连接数组
      */
     private final List<Object> readBuf = new ArrayList<Object>();
 
     @SuppressWarnings("Duplicates")
     @Override
     public void read() {
         assert eventLoop().inEventLoop();
         final ChannelConfig config = config();
         final ChannelPipeline pipeline = pipeline();
         // 获得 RecvByteBufAllocator.Handle 对象
         final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
         // 重置 RecvByteBufAllocator.Handle 对象
         allocHandle.reset(config);
 
         boolean closed = false;
         Throwable exception = null;
         try {
             try {
                 do {
                     // 读取客户端的连接到 readBuf 中
                     int localRead = doReadMessages(readBuf);
                     // 若无可读取的客户端的连接,结束
                     if (localRead == 0) {
                         break;
                     }
                     // 读取出错
                     if (localRead < 0) {
                         closed = true; // 标记关闭服务端
                         break;
                     }
 
                     // 读取消息数量 + localRead
                     allocHandle.incMessagesRead(localRead);
                 } while (allocHandle.continueReading()); // 循环判断是否继续读取
             } catch (Throwable t) {
                 // 记录异常
                 exception = t;
             }
 
             // 循环 readBuf 数组,触发 Channel read 事件到 pipeline 中。
             int size = readBuf.size();
             for (int i = 0; i < size; i ++) {
                readPending = false;
                 // 在内部,会通过 ServerBootstrapAcceptor ,将客户端的 Netty NioSocketChannel 注册到 EventLoop 上
                 pipeline.fireChannelRead(readBuf.get(i));
             }
             // 清空 readBuf 数组
             readBuf.clear();
             // 读取完成
             allocHandle.readComplete();
             // 触发 Channel readComplete 事件到 pipeline 中。
             pipeline.fireChannelReadComplete();
 
             // 发生异常
             if (exception != null) {
                 // 判断是否要关闭 
                 closed = closeOnReadError(exception);
 
                 // 触发 exceptionCaught 事件到 pipeline 中。
                 pipeline.fireExceptionCaught(exception);
             }
 
             if (closed) {
                 inputShutdown = true;
                 if (isOpen()) {
                     close(voidPromise());
                 }
             }
         } finally {
             // Check if there is a readPending which was not processed yet.
             // This could be for two reasons:
             // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
             // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
             if (!readPending && !config.isAutoRead()) {
                 removeReadOp();
             }
         }
     }
 }

上面关于重置 RecvByteBufAllocator.Handle 对象:

@Override
public void reset(ChannelConfig config) {
    this.config = config; // 重置 ChannelConfig 对象
    maxMessagePerRead = maxMessagesPerRead(); // 重置 maxMessagePerRead 属性
    totalMessages = totalBytesRead = 0; // 重置 totalMessages 和 totalBytesRead 属性
}

上面关于调用 continueReading() 方法,判断是否循环是否继续,读取(即就是 接受 )新的客户端连接:

// AdaptiveRecvByteBufAllocator.HandleImpl.java
@Override
public boolean continueReading() {
    return continueReading(defaultMaybeMoreSupplier);
}

// DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle.java
@Override
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
    return config.isAutoRead() &&
           (!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
           totalMessages < maxMessagePerRead &&
           totalBytesRead > 0; //此时 totalBytesRead 等于 0 ,会返回 false 。因此,循环会结束。所以,对于 NioServerSocketChannel 来说,每次只接受一个新的客户端连接 
           //然而因为服务端 NioServerSocketChannel 对 Selectionkey.OP_ACCEPT 事件感兴趣,所以后续的新的客户端连接还是会被接受的
}

doReadMessages(List buf) 抽象方法,读取客户端的连接到方法参数 buf 中。它是一个抽象方法,定义在 AbstractNioMessageChannel 抽象类中:返回值是读取到的数量

/**
 * Read messages into the given array and return the amount which was read.
 */
protected abstract int doReadMessages(List<Object> buf) throws Exception;

NioServerSocketChannel 对该方法的实现:

  @Override
  protected int doReadMessages(List<Object> buf) throws Exception {
      // 接受客户端连接
      SocketChannel ch = SocketUtils.accept(javaChannel());
  
      try {
          // 创建 Netty NioSocketChannel 对象
          if (ch != null) {
              buf.add(new NioSocketChannel(this, ch));
              return 1;
 //即就是成功接受一个新的客户端连接
          }
      } catch (Throwable t) {
          logger.warn("Failed to create a new channel from an accepted socket.", t);
          // 发生异常,关闭客户端的 SocketChannel 连接
          try {
              ch.close();
          } catch (Throwable t2) {
              logger.warn("Failed to close a socket.", t2);
          }
      }
  
      return 0;
 //即就是接受到0个新客户端连接
  }
 
 @Override
protected ServerSocketChannel javaChannel() {
    return (ServerSocketChannel) super.javaChannel();
}

上面调用 accept(ServerSocketChannel serverSocketChannel) 方法,接受客户端连接:

public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
            @Override
            public SocketChannel run() throws IOException {
                return serverSocketChannel.accept(); // 调用该方法连接客户端连接。

            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}

ServerBootstrapAcceptor :继承 ChannelInboundHandlerAdapter 类,服务器接收器( acceptor ),负责将接受的客户端的 NioSocketChannel 注册到 EventLoop 中。

这里,从继承的是 ChannelInboundHandlerAdapter 类,可以看出它是 Inbound 事件处理器。(呼应一下以前的文章)

ServerBootstrapAcceptor 构造方法:
在服务器启动过程中, ServerBootstrapAcceptor 注册到服务端的 NioServerSocketChannel 的 pipeline 的尾部:

// 记录当前的属性
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
    currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
}
synchronized (childAttrs) {
    currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}

// 添加 ChannelInitializer 对象到 pipeline 中,用于后续初始化 ChannelHandler 到 pipeline 中。
p.addLast(new ChannelInitializer<Channel>() {

    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();

        // 添加配置的 ChannelHandler 到 pipeline 中。
        ChannelHandler handler = config.handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }

        // 添加 ServerBootstrapAcceptor 到 pipeline 中。
        // 使用 EventLoop 执行的原因,参见 https://github.com/lightningMan/netty/commit/4638df20628a8987c8709f0f8e5f3679a914ce1a
        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); // 在此创建ServerBootstrapAccept对象
            }
        });
    }

});

在创建ServerBootstrapAccept对象处:

private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
/**
 * 自动恢复接受客户端连接的任务
 */
private final Runnable enableAutoReadTask;

ServerBootstrapAcceptor(
        final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
        Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
    this.childGroup = childGroup;
    this.childHandler = childHandler;
    this.childOptions = childOptions;
    this.childAttrs = childAttrs;

    // Task which is scheduled to re-enable auto-read.
    // It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may
    // not be able to load the class because of the file limit it already reached.
    //
    // See https://github.com/netty/netty/issues/1328
    enableAutoReadTask = new Runnable() { // 初始化 enableAutoReadTask 属性,自动恢复接受客户端连接的任务。
        @Override
        public void run() {
            channel.config().setAutoRead(true);
        }
    };
}

channelRead(ChannelHandlerContext ctx, Object msg) 方法,将接受的客户端的 NioSocketChannel 注册到 EventLoop 中:

 1: @Override
 2: public void channelRead(ChannelHandlerContext ctx, Object msg) {
 3:     
 4: 
 5:     // 接受的客户端的 NioSocketChannel 对象
 6:     final Channel child = (Channel) msg;
 7:     // 添加 NioSocketChannel 的处理器
 8:     child.pipeline().addLast(childHandler);
 9:     // 设置 NioSocketChannel 的配置项
10:     setChannelOptions(child, childOptions, logger);
11:     // 设置 NioSocketChannel 的属性
12:     for (Entry<AttributeKey<?>, Object> e: childAttrs) {
13:         child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
14:     }
15: 
16:     try {
17:         // 注册客户端的 NioSocketChannel 到 work EventLoop 中。
18:         childGroup.register(child).addListener(new ChannelFutureListener() {
19: 
20:             @Override
21:             public void operationComplete(ChannelFuture future) throws Exception {
22:                 // 注册失败,关闭客户端的 NioSocketChannel
23:                 if (!future.isSuccess()) {
24:                     forceClose(child, future.cause());
25:                 }
26:             }
27: 
28:         });
29:     } catch (Throwable t) {
30:         // 发生异常,强制关闭客户端的 NioSocketChannel
31:         forceClose(child, t);
32:     }
33: }

添加监听器,如果注册失败,则调用 forceClose(Channel child, Throwable t) 方法,强制关闭客户端的 NioSocketChannel 连接:

private static void forceClose(Channel child, Throwable t) {
    child.unsafe().closeForcibly(); //强制关闭客户端的NioSocketChannelister an accepted channel: {}", child, t);
}

exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 方法,捕获到异常时,暂停 1 秒,不再接受新的客户端连接;然后,再恢复接受新的客户端连接。代码如下:

 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
     final ChannelConfig config = ctx.channel().config();
     if (config.isAutoRead()) {
         // 关闭接受新的客户端连接
         // stop accept new connections for 1 second to allow the channel to recover
         // See https://github.com/netty/netty/issues/1328
         config.setAutoRead(false);
         // 发起 1 秒的延迟任务,恢复重启开启接受新的客户端连接
         ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
     }
 
     // 继续传播 exceptionCaught 给下一个节点
     // still let the exceptionCaught event flow through the pipeline to give the user
     // a chance to do something with it
     ctx.fireExceptionCaught(cause);
 }
// DefaultChannelConfig.java
/**
 * {@link #autoRead} 的原子更新器
 */
private static final AtomicIntegerFieldUpdater<DefaultChannelConfig> AUTOREAD_UPDATER = AtomicIntegerFieldUpdater.newUpdater(DefaultChannelConfig.class, "autoRead");
/**
 * 是否开启自动读取的开关
 *
 * 1 - 开启
 * 0 - 关闭
 */
@SuppressWarnings("FieldMayBeFinal")
private volatile int autoRead = 1;

@Override
public ChannelConfig setAutoRead(boolean autoRead) {
    // 原子更新,并且获得更新前的值。使用 AUTOREAD_UPDATER 更新 autoRead 字段,并获得更新前的值
    boolean oldAutoRead = AUTOREAD_UPDATER.getAndSet(this, autoRead ? 1 : 0) == 1;
    // 发起读取。autoRead && !oldAutoRead 返回 true ,意味着恢复重启开启接受新的客户端连接。所以调用 read() 方法
    if (autoRead && !oldAutoRead) {
        channel.read();
    // 关闭读取。!autoRead && oldAutoRead 返回 false ,意味着关闭接受新的客户端连接。所以调用 autoReadCleared() 方法,移除对 SelectionKey.OP_ACCEPT 事件的感兴趣。
    } else if (!autoRead && oldAutoRead) {
        autoReadCleared();
    }
    return this;
}

autoReadCleared()

// NioServerSocketChannel.java

@Override
protected void autoReadCleared() {
    clearReadPending();
}

clearReadPending()

protected final void clearReadPending() {
    if (isRegistered()) {
        EventLoop eventLoop = eventLoop();
        if (eventLoop.inEventLoop()) {
            clearReadPending0();
        } else {
            eventLoop.execute(clearReadPendingRunnable);
        }
    } else {
        // Best effort if we are not registered yet clear readPending. This happens during channel initialization.
        // NB: We only set the boolean field instead of calling clearReadPending0(), because the SelectionKey is
        // not set yet so it would produce an assertion failure.
        readPending = false;
    }
}

private final Runnable clearReadPendingRunnable = new Runnable() {
    @Override
    public void run() {
        clearReadPending0();
    }
};

private void clearReadPending0() {
    readPending = false;
    // 移除对“读”事件的感兴趣。
    ((AbstractNioUnsafe) unsafe()).removeReadOp();
}

最终在EventLoop线程中调用AbstractNioUnsafe#clearReadPending0() 方法,移除对读事件的感兴趣( 对于 NioServerSocketChannel 的 读事件就是 SelectionKey.OP_ACCEPT )。

// AbstractNioUnsafe.java

protected final void removeReadOp() {
    SelectionKey key = selectionKey();
    // 忽略,如果 SelectionKey 不合法,例如已经取消
    // Check first if the key is still valid as it may be canceled as part of the deregistration
    // from the EventLoop
    // See https://github.com/netty/netty/issues/2104
    if (!key.isValid()) {
        return;
    }
    // 取反求并,移除对“读”事件的感兴趣。
    int interestOps = key.interestOps();
    if ((interestOps & readInterestOp) != 0) {
        // only remove readInterestOp if needed
        key.interestOps(interestOps & ~readInterestOp);
    }
}

至此,Channel内read与accept的源码简单概况完毕。

发布了46 篇原创文章 · 获赞 6 · 访问量 3847

猜你喜欢

转载自blog.csdn.net/weixin_43257196/article/details/104366906