由前面的分析可知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();
}
}
}
@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;
}
该方法会触发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();方法同上面fireChannelRead方法的流程类似,将Pipeline中的所有ChannelHandler中的channelReadComplete(ChannelHandlerContext ctx)方法都执行一遍