抓到Netty一个Bug,聊一下Netty是如何高效接收网络连接的(六)
6. ChannelRead事件的响应
接收客户端连接.png
在前边介绍接收连接的整体核心流程框架的时候,我们提到main reactor线程是在一个do{.....}while(...)循环read loop中不断的调用ServerSocketChannel#accept方法来接收客户端的连接。
当满足退出read loop循环的条件有两个:
1.在限定的16次读取中,已经没有新的客户端连接要接收了。退出循环。
2.从NioServerSocketChannel中读取客户端连接的次数达到了16次,无论此时是否还有客户端连接都需要退出循环。
main reactor就会退出read loop循环,此时接收到的客户端连接NioSocketChannel暂存与List<Object> readBuf集合中。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
try {
try {
do {
........省略.........
//底层调用NioServerSocketChannel->doReadMessages 创建客户端SocketChannel
int localRead = doReadMessages(readBuf);
........省略.........
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));
}
........省略.........
} finally {
........省略.........
}
}
}
随后main reactor线程会遍历List<Object> readBuf集合中的NioSocketChannel,并在NioServerSocketChannel的pipeline中传播ChannelRead事件。
传播ChannelRead事件.png
最终ChannelRead事件会传播到ServerBootstrapAcceptor中,这里正是Netty处理客户端连接的核心逻辑所在。
ServerBootstrapAcceptor主要的作用就是初始化客户端NioSocketChannel,并将客户端NioSocketChannel注册到Sub Reactor Group中,并监听OP_READ事件。
在ServerBootstrapAcceptor 中会初始化客户端NioSocketChannel的这些属性。
比如:从Reactor组EventLoopGroup childGroup,用于初始化NioSocketChannel中的pipeline用到的ChannelHandler childHandler,以及NioSocketChannel中的一些childOptions和childAttrs。
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
//向客户端NioSocketChannel的pipeline中
//添加在启动配置类ServerBootstrap中配置的ChannelHandler
child.pipeline().addLast(childHandler);
//利用配置的属性初始化客户端NioSocketChannel
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
try {
/**
* 1:在Sub Reactor线程组中选择一个Reactor绑定
* 2:将客户端SocketChannel注册到绑定的Reactor上
* 3:SocketChannel注册到sub reactor中的selector上,并监听OP_READ事件
* */
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);
}
}
}
正是在这里,netty会将我们在?《详细图解Netty Reactor启动全流程》的启动示例程序中在ServerBootstrap中配置的客户端NioSocketChannel的所有属性(child前缀配置)初始化到NioSocketChannel中。
public final class EchoServer {
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure the server.
//创建主从Reactor线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)//配置主从Reactor
.channel(NioServerSocketChannel.class)//配置主Reactor中的channel类型
.option(ChannelOption.SO_BACKLOG, 100)//设置主Reactor中channel的option选项
.handler(new LoggingHandler(LogLevel.INFO))//设置主Reactor中Channel->pipline->handler
.childHandler(new ChannelInitializer<SocketChannel>() {//设置从Reactor中注册channel的pipeline
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
// Start the server. 绑定端口启动服务,开始监听accept事件
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
以上示例代码中通过ServerBootstrap配置的NioSocketChannel相关属性,会在Netty启动并开始初始化NioServerSocketChannel的时候将ServerBootstrapAcceptor的创建初始化工作封装成异步任务,然后在NioServerSocketChannel注册到Main Reactor中成功后执行。
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
@Override
void init(Channel channel) {
................省略................
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
................省略................
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
}
在经过ServerBootstrapAccptor#chanelRead回调的处理之后,此时客户端NioSocketChannel中pipeline的结构为:
客户端channel pipeline初始结构.png
随后会将初始化好的客户端NioSocketChannel向Sub Reactor Group中注册,并监听OP_READ事件。
如下图中的步骤3所示:
netty中的reactor.png