Netty如何高效接收网络数据?聊透ByteBuffer动态自适应扩缩 三

r660926
发布于 2022-7-28 17:51
浏览
0收藏

2.1 ChannelRead与ChannelReadComplete事件的区别


有些小伙伴可能对Netty中的一些传播事件触发的时机,或者事件之间的区别理解的不是很清楚,概念容易混淆。在后面的文章中笔者也会从源码的角度出发给大家说清楚Netty中定义的所有异步事件,以及这些事件之间的区别和联系和触发时机,传播机制。

 

这里我们主要探讨本文主题中涉及到的两个事件:ChannelRead事件与ChannelReadComplete事件。

 

从上述介绍的Netty接收网络数据流程总览中我们可以看出ChannelRead事件和ChannelReadComplete事件是不一样的,但是对于刚接触Netty的小伙伴来说从命名上乍一看感觉又差不多。

 

下面我们来看这两个事件之间的差别:

 

Netty服务端对于一次OP_READ事件的处理,会在一个do{}while()循环read loop中分多次从客户端NioSocketChannel中读取网络数据。每次读取我们分配的ByteBuffer容量大小,初始容量为2048。

 

 • ChanneRead事件:一次循环读取一次数据,就触发一次ChannelRead事件。本次最多读取在read loop循环开始分配的DirectByteBuffer容量大小。这个容量会动态调整,文章后续笔者会详细介绍。

 

 • ChannelReadComplete事件:当读取不到数据或者不满足continueReading的任意一个条件就会退出read loop,这时就会触发ChannelReadComplete事件。表示本次OP_READ事件处理完毕。

 

这里需要特别注意下触发ChannelReadComplete事件并不代表NioSocketChannel中的数据已经读取完了,只能说明本次OP_READ事件处理完毕。因为有可能是客户端发送的数据太多,Netty读了16次还没读完,那就只能等到下次OP_READ事件到来的时候在进行读取了。
 
以上内容就是Netty在接收客户端发送网络数据的全部核心逻辑。目前为止我们还未涉及到这部分的主干核心源码,笔者想的是先给大家把核心逻辑讲解清楚之后,这样理解起来核心主干源码会更加清晰透彻。

 

经过前边对网络数据接收的核心逻辑介绍,笔者在把这张流程图放出来,大家可以结合这张图在来回想下主干核心逻辑。

Netty如何高效接收网络数据?聊透ByteBuffer动态自适应扩缩 三-鸿蒙开发者社区

 Netty接收网络数据流程.png


下面笔者会结合这张流程图,给大家把这部分的核心主干源码框架展现出来,大家可以将我们介绍过的核心逻辑与主干源码做个一一对应,还是那句老话,我们要从主干框架层面把握整体处理流程,不需要读懂每一行代码,文章后续笔者会将这个过程中涉及到的核心点位给大家拆开来各个击破!!

 

3. 源码核心框架总览
     

@Override
        public final void read() {
            final ChannelConfig config = config();

            ...............处理半关闭相关代码省略...............
            //获取NioSocketChannel的pipeline
            final ChannelPipeline pipeline = pipeline();
            //PooledByteBufAllocator 具体用于实际分配ByteBuf的分配器
            final ByteBufAllocator allocator = config.getAllocator();
            //自适应ByteBuf分配器 AdaptiveRecvByteBufAllocator ,用于动态调节ByteBuf容量
            //需要与具体的ByteBuf分配器配合使用 比如这里的PooledByteBufAllocator
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            //allocHandler用于统计每次读取数据的大小,方便下次分配合适大小的ByteBuf
            //重置清除上次的统计指标
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    //利用PooledByteBufAllocator分配合适大小的byteBuf 初始大小为2048
                    byteBuf = allocHandle.allocate(allocator);
                    //记录本次读取了多少字节数
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    //如果本次没有读取到任何字节,则退出循环 进行下一轮事件轮询
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        if (close) {
                            ......表示客户端发起连接关闭.....
                        }
                        break;
                    }

                    //read loop读取数据次数+1
                    allocHandle.incMessagesRead(1);
                    //客户端NioSocketChannel的pipeline中触发ChannelRead事件
                    pipeline.fireChannelRead(byteBuf);
                    //解除本次读取数据分配的ByteBuffer引用,方便下一轮read loop分配
                    byteBuf = null;
                } while (allocHandle.continueReading());//判断是否应该继续read loop

                //根据本次read loop总共读取的字节数,决定下次是否扩容或者缩容
                allocHandle.readComplete();
                //在NioSocketChannel的pipeline中触发ChannelReadComplete事件,表示一次read事件处理完毕
                //但这并不表示 客户端发送来的数据已经全部读完,因为如果数据太多的话,这里只会读取16次,剩下的会等到下次read事件到来后在处理
                pipeline.fireChannelReadComplete();

                .........省略连接关闭流程处理.........
            } catch (Throwable t) {
                ...............省略...............
            } finally {
               ...............省略...............
            }
        }
    }

这里再次强调下当前执行线程为Sub Reactor线程,处理连接数据读取逻辑是在NioSocketChannel中。

 

首先通过config()获取客户端NioSocketChannel的Channel配置类NioSocketChannelConfig。

 

通过pipeline()获取NioSocketChannel的pipeline。我们在?《详细图解Netty Reactor启动全流程》一文中提到的Netty服务端模板所举的示例中,NioSocketChannelde pipeline中只有一个EchoChannelHandler。

Netty如何高效接收网络数据?聊透ByteBuffer动态自适应扩缩 三-鸿蒙开发者社区

 客户端channel pipeline结构.png

标签
已于2022-7-28 17:51:49修改
收藏
回复
举报
回复
    相关推荐