由前面的分析可知NioEventLoop的run()方法是一個無限循環,NioEventLoop會不斷的調用Selector的select(timeout)方法查詢是否有新的IO事件,所以當一個客戶端連接進入的時候會被Boss線程select到,故新連接接入流程的入口爲Bose線程的select方法。
select(boolean oldWakenUp)方法如下所示(僅保留相關代碼):
private void select(boolean oldWakenUp) throws IOException {
......
int selectedKeys = selector.select(timeoutMillis);
......
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
break;
}
......
}
當客戶端發起連接請求時,int selectedKeys = selector.select(timeoutMillis);返回的selectedKeys的值將會從0變爲1。從而滿足後面selectedKeys != 0 的判斷,進而跳出select()方法,執行後續的操作。
select到IO事件後會進入NIOEventLoop的processSelectedKeys()方法,因爲默認情況netty會對選擇器Selector的SelectionKeys做相關優化(將原先的HashSet通過反射的方式修改爲數組),故會執行NIOEventLoop的processSelectedKeysOptimized方法。
NIOEventLoop的processSelectedKeysOptimized()如下所示,因爲當前是服務端,所以當前ServerSocketChannel是AbstractNioChannel類的子類,故進而由調用processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法處理
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) {
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;
}
}
}
NIOEventLoop的processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法如下所示
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
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();
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
該方法的主要作用是根據SelectionKey中就緒事件的類型,執行不同的邏輯。因爲當前分析的是服務端處理新連接的過程,由下面的代碼片段可知,對於SelectionKey.OP_ACCEPT類型的事件,將會調用Unsafe接口的read()方法進行處理,這裏實際調用的是AbstractNioMessageChannel的read()方法。
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
AbstractNioMessageChannel的read()方法如下:
read()方法中有三個核心的步驟,即:
int localRead = doReadMessages(readBuf);
**pipeline.fireChannelRead(readBuf.get(i));**
**pipeline.fireChannelReadComplete();**
public void read() {
assert eventLoop().inEventLoop();//斷言當前線程爲ServerSocketChannel綁定的EventLoop
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 {
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;
pipeline.fireChannelRead(readBuf.get(i));//觸發通道讀時間
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();//觸發通道讀取完成時間
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
doReadMessages(List buf)
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());//調用JDK的accpt方法獲取一個客戶端連接通道
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));//創建一個netty的客戶端通道類,並加入到集合中
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
fireChannelRead(Object msg)方法
該方法會觸發Pipeline上所有的ChannelHandler的channelRead方法,最終會觸發ServerBootstrapAcceptor類的channelRead方法,該實例是在ServerSocketChannel實例化的時候添加到pipeline的尾部的(ServerBootStrap的init(Channel channel)方法)
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
接着分析ServerBootstrapAcceptor的channelRead(ChannelHandlerContext ctx, Object msg)方法
該方法主要邏輯爲:
1、根據ServerBootStrap中的配置,對客戶端通道SocketChannel進行設置
2、在work線程組中選擇一個NioEventLoop,並將當前客戶連接註冊在NioEventLoop的Selector上
當客戶端後續再有read事件被觸發,則會由當前work線程組中獲得的NioEventLoop進行處理
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;//客戶端連接channel
child.pipeline().addLast(childHandler);//添加childHandler
setChannelOptions(child, childOptions, logger);//設置通道的option
//設置socketChannel的屬性
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
//在work線程組裏挑選一個EventLoop,並將當前的SocketChannel註冊到EventLoop的Selector上
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);
}
}
pipeline.fireChannelReadComplete();
pipeline.fireChannelReadComplete();方法同上面fireChannelRead方法的流程類似,將Pipeline中的所有ChannelHandler中的channelReadComplete(ChannelHandlerContext ctx)方法都執行一遍