Netty 服務端創建源碼分析
當我們直接使用 JDK 的 NIO類庫 開發基於 NIO 的異步服務端時,需要用到 多路複用器Selector、ServerSocketChannel、SocketChannel、ByteBuffer、SelectionKey 等,相比於傳統的 BIO開發,NIO 的開發要複雜很多,開發出穩定、高性能的異步通信框架,一直是個難題。Netty 爲了向使用者屏蔽 NIO通信 的底層細節,在和用戶交互的邊界做了封裝,目的就是爲了減少用戶開發工作量,降低開發難度。ServerBootstrap 是 Socket服務端 的啓動輔助類,用戶通過 ServerBootstrap 可以方便地創建 Netty 的服務端。
Netty 服務端創建時序圖
下面我們對 Netty服務端創建 的關鍵步驟和原理進行詳細解析。
1、創建 ServerBootstrap實例。ServerBootstrap 是 Netty服務端 的 啓動輔助類,它提供了一系列的方法用於設置服務端啓動相關的參數。底層對各種 原生NIO 的 API 進行了封裝,減少了用戶與 底層API 的接觸,降低了開發難度。ServerBootstrap 中只有一個 public 的無參的構造函數可以給用戶直接使用,ServerBootstrap 只開放一個無參的構造函數 的根本原因是 它的參數太多了,而且未來也可能會發生變化,爲了解決這個問題,就需要引入 Builder建造者模式。
2、設置並綁定 Reactor線程池。Netty 的 Reactor線程池 是 EventLoopGroup,它實際上是一個 EventLoop數組。EventLoop 的職責是處理所有註冊到本線程多路複用器 Selector 上的 Channel,Selector 的輪詢操作由綁定的 EventLoop線程 的 run()方法 驅動,在一個循環體內循環執行。值得說明的是,EventLoop 的職責不僅僅是處理 網絡IO事件,用戶自定義的Task 和 定時任務Task 也統一由 EventLoop 負責處理,這樣線程模型就實現了統一。從調度層面看,也不存在從 EventLoop線程 中再啓動其他類型的線程用於異步執行另外的任務,這樣就避免了多線程併發操作和鎖競爭,提升了 IO線程 的處理和調度性能。
3、設置並綁定 服務端Channel。作爲 NIO服務端,需要創建 ServerSocketChannel,Netty 對 原生NIO類庫 進行了封裝,對應的實現是NioServerSocketChannel。對於用戶而言,不需要關心 服務端Channel 的底層實現細節和工作原理,只需要指定具體使用哪種服務端 Channel 即可。因此,Netty 中 ServerBootstrap的基類 提供了 channel()方法,用於指定 服務端Channel 的類型。Netty 通過工廠類,利用反射創建 NioServerSocketChannel對象。由於服務端監聽端口往往只需要在系統啓動時纔會調用,因此反射對性能的影響並不大。相關代
碼如下。
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
/**
* 通過 參數channelClass 創建一個 Channel實例,
*/
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
}
4、鏈路建立的時候創建並初始化 ChannelPipeline。ChannelPipeline 並不是 NIO服務端 必需的,它本質就是一個負責處理網絡事件的職責鏈,負責管理和執行 ChannelHandler。網絡事件以事件流的形式在 ChannelPipeline 中流轉,由 ChannelPipeline 根據 ChannelHandler的執行策略 調度 ChannelHandler的執行。典型的網絡事件如下。
- 鏈路註冊;
- 鏈路激活;
- 鏈路斷開;
- 接收到請求消息;
- 請求消息接收並處理完畢;
- 發送應答消息;
- 鏈路發生異常;
- 發生用戶自定義事件。
5、初始化 ChannelPipeline 完成之後,添加並設置 ChannelHandler。ChannelHandler 是 Netty 提供給用戶定製和擴展的關鍵接口。利用 ChannelHandler 用戶可以完成大多數的功能定製,例如消息編解碼、心跳、安全認證、TSL/SSL 認證、流量控制和流量整形等。Netty 同時也提供了大量的 系統ChannelHandler 供用戶使用,比較實用的 系統ChannelHandler 總結如下。
- 系統編解碼框架,ByteToMessageCodec;
- 基於長度的半包解碼器,LengthFieldBasedFrameDecoder;
- 碼流日誌打印 Handler,LoggingHandler;
- SSL 安全認證 Handler,SslHandler;
- 鏈路空閒檢測 Handler,IdleStateHandler;
- 流量整形 Handler,ChannelTrafficShapingHandler;
- Base64 編解碼,Base64Decoder 和 Base64Encoder。
創建和添加 ChannelHandler 的代碼示例如下。
.childHandler( new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast( new EchoServerHandler() );
}
});
6、綁定並啓動監聽端口。在綁定監聽端口之前系統會做一系列的初始化和檢測工作,完成之後,會啓動監聽端口,並將 ServerSocketChannel 註冊到 Selector 上監聽客戶端連接。
7、Selector 輪詢。由 Reactor線程 NioEventLoop 負責調度和執行 Selector 輪詢操作,選擇準備就緒的 Channel集合,相關代碼如下。
public final class NioEventLoop extends SingleThreadEventLoop {
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
......
int selectedKeys = selector.select(timeoutMillis);
selectCnt ++;
......
}
}
8、當輪詢到 準備就緒的Channel 之後,就由 Reactor線程 NioEventLoop 執行 ChannelPipeline 的相應方法,最終調度並執行 ChannelHandler,接口如下圖所示。
9、執行 Netty 中 系統的ChannelHandler 和 用戶添加定製的ChannelHandler 。ChannelPipeline 根據網絡事件的類型,調度並執行 ChannelHandler,相關代碼如下。
public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
}
結合 Netty源碼 對服務端的創建過程進行解析
首先通過構造函數創建 ServerBootstrap實例,隨後,通常會創建兩個 EventLoopGroup實例 (也可以只創建一個並共享),代碼如下。
EventLoopGroup acceptorGroup = new NioEventLoopGroup();
EventLoopGroup iOGroup = new NioEventLoopGroup();
NioEventLoopGroup 實際就是一個 Reactor線程池,負責調度和執行客戶端的接入、網絡讀寫事件的處理、用戶自定義任務和定時任務的執行。通過 ServerBootstrap 的 group()方法 將兩個 EventLoopGroup實例 傳入,代碼如下。
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
/**
* Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
* {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
* {@link Channel}'s.
*/
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
}
其中 parentGroup對象 被設置進了 ServerBootstrap 的父類 AbstractBootstrap 中,代碼如下。
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
volatile EventLoopGroup group;
/**
* The {@link EventLoopGroup} which is used to handle all the events for the to-be-created
* {@link Channel}
*/
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return self();
}
}
該方法會被客戶端和服務端重用,用於設置 工作IO線程,執行和調度網絡事件的讀寫。線程組和線程類型設置完成後,需要設置 服務端Channel 用於端口監聽和客戶端鏈路接入。Netty 通過 Channel工廠類 來創建不同類型的 Channel,對於服務端,需要創建 NioServerSocketChannel。所以,通過指定 Channel類型 的方式創建 Channel工廠。ReflectiveChannelFactory 可以根據 Channel的類型 通過反射創建 Channel的實例,服務端需要創建的是 NioServerSocketChannel實例,代碼如下。
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Constructor<? extends T> constructor;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
@Override
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
}
指定 NioServerSocketChannel 後,需要設置 TCP 的一些參數,作爲服務端,主要是設置 TCP 的 backlog參數。
backlog 指定了內核爲此套接口排隊的最大連接個數,對於給定的監聽套接口,內核要維護兩個隊列:未鏈接隊列 和 已連接隊列,根據 TCP三次握手 的 三個子過程來分隔這兩個隊列。服務器處於 listen狀態 時,收到客戶端 syn過程(connect) 時在未完成隊列中創建一個新的條目,然後用三次握手的第二個過程,即服務器的 syn響應客戶端,此條目在第三個過程到達前 (客戶端對服務器 syn 的 ack) 一直保留在未完成連接隊列中,如果三次握手完成,該條目將從未完成連接隊列搬到已完成連接隊列尾部。當進程調用 accept 時,從已完成隊列中的頭部取出一個條目給進程,當已完成隊列爲空時進程將睡眠,直到有條目在已完成連接隊列中才喚醒。backlog 被規定爲兩個隊列總和的最大值,大多數實現默認值爲 5,但在高併發 Web服務器 中此值顯然不夠。 需要設置此值更大一些的原因是,未完成連接隊列的長度可能因爲客戶端 syn 的到達及等待三次握手的第三個過程延時 而增大。Netty 默認的 backlog 爲 100,當然,用戶可以修改默認值,這需要根據實際場景和網絡狀況進行靈活設置。
TCP參數 設置完成後,用戶可以爲啓動輔助類和其父類分別指定 Handler。兩者 Handler 的用途不同:子類中的 Handler 是 NioServerSocketChannel 對應的 ChannelPipeline 的 Handler;父類中的 Handler 是客戶端新接入的連接 SocketChannel 對應的 ChannelPipeline 的 Handler。兩者的區別可以通過下圖來展示。
本質區別就是:ServerBootstrap 中的 Handler 是 NioServerSocketChannel 使用的,所有連接該監聽端口的客戶端都會執行它;父類AbstractBootstrap 中的 Handler 是個工廠類,它爲每個新接入的客戶端都創建一個新的 Handler。
服務端啓動的最後一步,就是綁定本地端口,啓動服務,下面我們來分析下這部分代碼。
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
}
先看下上述代碼調用的 initAndRegister()方法。它首先實例化了一個 NioServerSocketChannel類型 的 Channel對象。相關代碼如下。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
NioServerSocketChannel 創建成功後,對它進行初始化,初始化工作主要有以下三點。
@Override
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
// 1、設置 Socket參數 和 NioServerSocketChannel 的附加屬性
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
// 2、將 AbstractBootstrap 的 Handler 添加到 NioServerSocketChannel
// 的 ChannelPipeline 中
ChannelPipeline p = channel.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));
}
// 3、將用於服務端註冊的 Handler ServerBootstrapAcceptor 添加到 ChannelPipeline 中
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
到此,Netty 服務端監聽的相關資源已經初始化完畢,就剩下最後一步,註冊 NioServerSocketChannel 到 Reactor線程 的多路複用器上,然後輪詢客戶端連接事件。在分析註冊代碼之前,我們先通過下圖,看看目前 NioServerSocketChannel 的 ChannelPipeline 的組成。
最後,我們看下 NioServerSocketChannel 的註冊。當 NioServerSocketChannel 初始化完成之後,需要將它註冊到 Reactor線程 的多路複用器上監聽新客戶端的接入,代碼如下。
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
/**
* 將完成初始化的 NioServerSocketChannel 註冊到 Reactor線程
* 的多路複用器上,監聽新客戶端的接入
*/
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
......
// 首先判斷是否是 NioEventLoop 自身發起的操作。如果是,則不存在併發操作,直接
// 執行 Channel註冊;如果由其他線程發起,則封裝成一個 Task 放入消息隊列中異步執行。
// 此處,由於是由 ServerBootstrap 所在線程執行的註冊操作,所以會將其封裝成 Task 投遞
// 到 NioEventLoop 中執行
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
......
}
}
}
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 該方法在本類中是一個空實現,下面看一下它在子類 AbstractNioChannel 中的實現
doRegister();
neverRegistered = false;
registered = true;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
}
public abstract class AbstractNioChannel extends AbstractChannel {
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 將 NioServerSocketChannel 註冊到 NioEventLoop 的 多路複用器Selector 上
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
......
}
}
}
}
到此,服務端監聽啓動部分源碼已經分析完成。
結合 Netty源碼 對客戶端接入過程進行解析
負責處理網絡讀寫、連接和客戶端請求接入的 Reactor線程 就是 NioEventLoop,下面我們看下 NioEventLoop 是如何處理新的客戶端連接接入的。當 多路複用器 檢測到新的準備就緒的 Channel 時,默認執行 processSelectedKeysOptimized()方法,代碼如下。
public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
selectedKeys.keys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
// 根據就緒的操作位 SelectionKey,執行不同的操作
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
// 根據就緒的操作位 SelectionKey,執行不同的操作
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// 由於不同的 Channel 執行不同的操作,所以 NioUnsafe 被設計成接口
// 由不同的 Channel 內部的 NioUnsafe實現類 負責具體實現
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
// read()方法 的實現有兩個,分別是 NioByteUnsafe 和 NioMessageUnsafe,
// 對於 NioServerSocketChannel,它使用的是 NioMessageUnsafe
// 下面看一下 NioMessageUnsafe 對 read() 方法的實現
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
}
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
// 接收新的客戶端連接並創建 NioSocketChannel
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 接收到新的客戶端連接後,觸發 ChannelPipeline 的 channelRead方法。
// 事件在 ChannelPipeline 中傳遞,執行 ServerBootstrapAcceptor 的
// channelRead方法
pipeline.fireChannelRead(readBuf.get(i));
}
......
}
}
}
}
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
/**
* 接收新的客戶端連接並創建 NioSocketChannel
*/
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
......
}
return 0;
}
}
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
/**
* 該方法主要分爲如下三個步驟。
*/
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
// 第一步:將啓動時傳入的 childHandler 加入到客戶端 SocketChannel 的 ChannelPipeline 中
child.pipeline().addLast(childHandler);
// 第二步:設置客戶端 SocketChannel 的 TCP參數
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
// 第三步:註冊 SocketChannel 到多路複用器
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
}
}
下面我們展開看下 NioSocketChannel 的 register()方法。NioSocketChannel 的註冊方法與 ServerSocketChannel 的一致, 也是將 Channel 註冊到 Reactor線程 的多路複用器上。由於註冊的操作位是 0,所以,此時 NioSocketChannel 還不能讀取客戶端發送的消息,下面我們看看 是什麼時候修改監聽操作位爲 OP_READ 的。
執行完註冊操作之後,緊接着會觸發 ChannelReadComplete 事件。我們繼續分析 ChannelReadComplete 在 ChannelPipeline 中的處理流程:Netty 的 Header 和 Tail 本身不關注 ChannelReadComplete事件 就直接透傳,執行完 ChannelReadComplete 後,接着執行 PipeLine 的 read()方法,最終執行 HeadHandler 的 read()方法。
HeadHandler 的 read()方法用來將網絡操作位修改爲讀操作。創建 NioSocketChannel 的時候已經將 AbstractNioChannel 的 readInterestOp 設置爲 OP_ READ,這樣,執行 selectionKey. interestOps(interestOps | readInterestOp)操作 時就會把操作位設置爲 OP_READ。代碼如下。
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
}
到此,新接入的客戶端連接處理完成,可以進行網絡讀寫等 IO操作。