Netty源码剖析——接受请求过程源码剖析-上(三十三)

Netty启动过程梳理

  1. 创建2个EventLoopGroup线程池数组。数组默认大小CPU*2,方便 chooser选择线程池时提高性能
  2. BootStrap 将 boss 设置为 parent属性,将 worker 设置为 childer 属性
  3. 通过 bind 方法启动,内部重要方法为 initAndRegister 和 dobind0 方法
  4. initAndRegister 方法会反射创建 NioServerSocketChannel 及其相关的 NIO 的对象, pipeline, unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点。
  5. 在register方法成功以后调用在dobind方法中调用 doBind0方法,该方法会调用 NioServerSocketChannel的doBind方法对JDK 的channel和端口进行绑定,完成Netty服务器的所有启动,并开始监听连接事件

Netty 接受请求过程源码剖析

  1. 源码剖析目的
    • 服务器启动后肯定是要接收客户端请求并返回客户端想要的信息,下面源码分析Netty在启动之后是如何接受客户端请求的
    • 在 io.netty.example 包下
  2. 源码剖析

说明:

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 进入)

 

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值