Netty启动过程梳理
- 创建2个EventLoopGroup线程池数组。数组默认大小CPU*2,方便 chooser选择线程池时提高性能
- BootStrap 将 boss 设置为 parent属性,将 worker 设置为 childer 属性
- 通过 bind 方法启动,内部重要方法为 initAndRegister 和 dobind0 方法
- initAndRegister 方法会反射创建 NioServerSocketChannel 及其相关的 NIO 的对象, pipeline, unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点。
- 在register方法成功以后调用在dobind方法中调用 doBind0方法,该方法会调用 NioServerSocketChannel的doBind方法对JDK 的channel和端口进行绑定,完成Netty服务器的所有启动,并开始监听连接事件
Netty 接受请求过程源码剖析
- 源码剖析目的
- 服务器启动后肯定是要接收客户端请求并返回客户端想要的信息,下面源码分析Netty在启动之后是如何接受客户端请求的
- 在 io.netty.example 包下
- 源码剖析
说明:
1)从之前服务器启动的源码中,我们得知服务器最终注册了一个 Accept 事件等待客户端的连接。我们也知道,NioServerSocketChannel 将自己注册到了 boss 单例线程池(reactor线程)上,也就是 EventLoop
2)先简单说下EventLoop的逻辑
EventLoop的作用是一个死循环,而这个循环中做3件事情:1)有条件的等待 Nio 事件。2)处理 Nio 事件。3)处理消息队列中的任务。
3)仍用前面的项目来分析:进入到 NioEventLoop 源码中后,在 private void processSelectedKey(SelectionKey k,AbstractNioChannel ch) 方法开始调试最终我们要分析到AbstractNioChannel 的 doBeginRead 方法,当到这个方法时,针对于这个客户端的连接就完成了,接下来就可以监听读事件
源码分析过程
- 断点位置 NioEventLoop 的如下方法 processSelectedKe
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
执行浏览器 http:/localhost:8071,客户端发出请求
从断点我们可以看到,readyOps 是 16 ,也就是 Accept 事件。说明浏览器的请求已经进来了。
这个 unsafe 是 boss 线程中 NioServerSocketChannel 的 AbstractNioMessageChannel的NioMessageUnsafe内部类对象。我们进入到 AbstractNioMessageChannel的NioMessageUnsafe 的 read 方法中
read 方法代码并分析:
@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 {
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 {
// 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();
}
}
}
说明:
1)检查该 eventloop 线程是否是当前线程。assert eventLoop.inEventLoop()
2) 执行 doReadMessages 方法,并传入一个容器List<Object> readBuf
3) 循环容器list,执行pipeline.fireChannelRead(readBuf.get(i));
4) doReadMessages 是读取 boss 线程中的 NioServerSocketChannel 接受到的请求。并把这些请求放进容器
5) 循环遍历容器中的所有请求,调用 pipeline 的 fireChannelRead 方法,用于处理这些接受的请求或者其他事件,在 read 方法中,循环谓用 ServerSocket 的 pipeline 的 fireChannelRead 方法,开始执行管道中的handler 的 ChannelRead 方法(debug 进入)