Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一

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

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

前文回顾


在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网络IO模型的演变,最终在此基础上引出了Netty的网络IO模型如下图所示:

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

 netty中的reactor.png


详细内容可回看?《从内核角度看IO模型的演变》


后续我们又围绕着Netty的主从Reactor网络IO线程模型,在?《Reactor模型在Netty中的实现》一文中详细阐述了Netty的主从Reactor模型的创建,以及介绍了Reactor模型的关键组件。搭建了Netty的核心骨架如下图所示:

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

 主从Reactor线程组.png


在核心骨架搭建完毕之后,我们随后又在?《详细图解Reactor启动全流程》一文中阐述了Reactor启动的全流程,一个非常重要的核心组件NioServerSocketChannel开始在这里初次亮相,承担着一个网络框架最重要的任务--高效接收网络连接。我们介绍了NioServerSocketChannel的创建,初始化,向Main Reactor注册并监听OP_ACCEPT事件的整个流程。在此基础上,Netty得以整装待发,枕戈待旦开始迎接海量的客户端连接。Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

Reactor启动后的结构.png


随后紧接着我们在?《Netty如何高效接收网络连接》一文中详细介绍了Netty高效接收客户端网络连接的全流程,在这里Netty的核心重要组件NioServerSocketChannel开始正是登场,在NioServerSocketChannel中我们创建了客户端连接NioSocketChannel,并详细介绍了NioSocketChannel的初始化过程,随后通过在NioServerSocketChannel的pipeline中触发ChannelRead事件,并最终在ServerBootstrapAcceptor中将客户端连接NioSocketChannel注册到Sub Reactor中开始监听客户端连接上的OP_READ事件,准备接收客户端发送的网络数据也就是本文的主题内容。

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

 image.png


自此Netty的核心组件全部就绪并启动完毕,开始起飞~~~

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

 主从Reactor组完整结构.png


之前文章中的主角是Netty中主Reactor组中的Main Reactor以及注册在Main Reactor上边的NioServerSocketChannel,那么从本文开始,我们文章中的主角就切换为Sub Reactor以及注册在SubReactor上的NioSocketChannel了。

 

下面就让我们正式进入今天的主题,看一下Netty是如何处理OP_READ事件以及如何高效接收网络数据的。

 

1. Sub Reactor处理OP_READ事件流程总览

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区 OP_READ事件处理.png


客户端发起系统IO调用向服务端发送数据之后,当网络数据到达服务端的网卡并经过内核协议栈的处理,最终数据到达Socket的接收缓冲区之后,Sub Reactor轮询到NioSocketChannel上的OP_READ事件就绪,随后Sub Reactor线程就会从JDK Selector上的阻塞轮询APIselector.select(timeoutMillis)调用中返回。转而去处理NioSocketChannel上的OP_READ事件

 

注意这里的Reactor为负责处理客户端连接的Sub Reactor。连接的类型为NioSocketChannel,处理的事件为OP_READ事件。

 

在之前的文章中笔者已经多次强调过了,Reactor在处理Channel上的IO事件入口函数为NioEventLoop#processSelectedKey

public final class NioEventLoop extends SingleThreadEventLoop {

    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        ..............省略.................

        try {
            int readyOps = k.readyOps();

            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
               ..............处理OP_CONNECT事件.................
            }


            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
              ..............处理OP_WRITE事件.................
            }


            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                //本文重点处理OP_ACCEPT事件
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

}

这里需要重点强调的是,当前的执行线程现在已经变成了Sub Reactor,而Sub Reactor上注册的正是netty客户端NioSocketChannel负责处理连接上的读写事件。

 

所以这里入口函数的参数AbstractNioChannel ch则是IO就绪的客户端连接NioSocketChannel

 

开头通过ch.unsafe()获取到的NioUnsafe操作类正是NioSocketChannel中对底层JDK NIO SocketChannel的Unsafe底层操作类。实现类型为NioByteUnsafe定义在下图继承结构中的AbstractNioByteChannel父类中。

Netty如何高效接收网络数据?ByteBuffer动态自适应扩缩容机制一-开源基础软件社区

 image.png


下面我们到NioByteUnsafe#read方法中来看下Netty对OP_READ事件的具体处理过程:

标签
已于2022-7-28 17:50:53修改
收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐