一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)

lemonvita
发布于 2022-7-18 17:31
浏览
0收藏

本系列Netty源码解析文章基于 4.1.56.Final版本

一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)-鸿蒙开发者社区 主从Reactor组完整结构.png


《Netty如何高效接收网络数据》一文中,我们介绍了 Netty 的 SubReactor 处理网络数据读取的完整过程,当 Netty 为我们读取了网络请求数据,并且我们在自己的业务线程中完成了业务处理后,就需要将业务处理结果返回给客户端了,那么本文我们就来介绍下 SubReactor 如何处理网络数据发送的整个过程。

 

我们都知道 Netty 是一款高性能的异步事件驱动的网络通讯框架,既然是网络通讯框架那么它主要做的事情就是:

 

•接收客户端连接。


•读取连接上的网络请求数据。


•向连接发送网络响应数据。


前边系列文章在介绍Netty的启动以及接收连接的过程中,我们只看到 OP_ACCEPT 事件以及 OP_READ 事件的注册,并未看到 OP_WRITE 事件的注册。

 

•那么在什么情况下 Netty 才会向 SubReactor 去注册 OP_WRITE 事件呢?

 

•Netty 又是怎么对写操作做到异步处理的呢?

 

本文笔者将会为大家一一揭晓这些谜底。我们还是以之前的 EchoServer 为例进行说明。

@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        //此处的msg就是Netty在read loop中从NioSocketChannel中读取到的ByteBuffer
        ctx.write(msg);
    }

}

我们将在《Netty如何高效接收网络数据》一文中读取到的 ByteBuffer (这里的 Object msg),直接发送回给客户端,用这个简单的例子来揭开 Netty 如何发送数据的序幕~~

 

在实际开发中,我们首先要通过解码器将读取到的 ByteBuffer 解码转换为我们的业务 Request 类,然后在业务线程中做业务处理,在通过编码器对业务 Response 类编码为 ByteBuffer ,最后利用 ChannelHandlerContext ctx 的引用发送响应数据。

 

本文我们只聚焦 Netty 写数据的过程,对于 Netty 编解码相关的内容,笔者会在后续的文章中专门介绍。
一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)-鸿蒙开发者社区

本文概要.png


1. ChannelHandlerContext

一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)-鸿蒙开发者社区 pipeline结构.png


通过前面几篇文章的介绍,我们知道 Netty 会为每个 Channel 分配一个 pipeline ,pipeline 是一个双向链表的结构。Netty 中产生的 IO 异步事件会在这个 pipeline 中传播。

 

Netty 中的 IO 异步事件大体上分为两类:

 

•inbound事件:入站事件,比如前边介绍的 ChannelActive 事件, ChannelRead 事件,它们会从 pipeline 的头结点 HeadContext 开始一直向后传播。

 

•outbound事件:出站事件,比如本文中即将要介绍到的 write事件 以及 flush 事件,出站事件会从相反的方向从后往前传播直到 HeadContext 。最终会在 HeadContext 中完成出站事件的处理。

 

    ◆本例中用到的 channelHandlerContext.write()  会使 write 事件从当前 ChannelHandler 也就是这里的 EchoServerHandler 开始沿着 pipeline 向前传播。

 

    ◆而 channelHandlerContext.channel().write() 则会使 write 事件从 pipeline 的尾结点 TailContext 开始向前传播直到 HeadContext 。
一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)-鸿蒙开发者社区

客户端channel pipeline结构.png


而 pipeline 这样一个双向链表数据结构中的类型正是 ChannelHandlerContext  ,由 ChannelHandlerContext 包裹我们自定义的 IO 处理逻辑 ChannelHandler。

 

ChannelHandler 并不需要感知到它所处的 pipeline 中的上下文信息,只需要专心处理好 IO 逻辑即可,关于 pipeline 的上下文信息全部封装在 ChannelHandlerContext中。


ChannelHandler 在 Netty 中的作用只是负责处理 IO 逻辑,比如编码,解码。它并不会感知到它在 pipeline 中的位置,更不会感知和它相邻的两个 ChannelHandler。事实上 ChannelHandler也并不需要去关心这些,它唯一需要关注的就是处理所关心的异步事件

 

而 ChannelHandlerContext 中维护了 pipeline 这个双向链表中的 pre 以及 next 指针,这样可以方便的找到与其相邻的 ChannelHandler ,并可以过滤出一些符合执行条件的 ChannelHandler。正如它的命名一样, ChannelHandlerContext  正是起到了维护 ChannelHandler 上下文的一个作用。而 Netty 中的异步事件在 pipeline 中的传播靠的就是这个 ChannelHandlerContext 。

 

这样设计就使得 ChannelHandlerContext 和 ChannelHandler 的职责单一,各司其职,具有高度的可扩展性。

 

2. write事件的传播


我们无论是在业务线程或者是在 SubReactor 线程中完成业务处理后,都需要通过 channelHandlerContext 的引用将 write事件在 pipeline 中进行传播。然后在 pipeline 中相应的 ChannelHandler 中监听 write 事件从而可以对 write事件进行自定义编排处理(比如我们常用的编码器),最终传播到 HeadContext 中执行发送数据的逻辑操作。

 

前边也提到 Netty 中有两个触发 write 事件传播的方法,它们的传播处理逻辑都是一样的,只不过它们在 pipeline 中的传播起点是不同的。

 

• channelHandlerContext.write() 方法会从当前 ChannelHandler 开始在 pipeline 中向前传播 write 事件直到 HeadContext。

 

• channelHandlerContext.channel().write() 方法则会从 pipeline 的尾结点 TailContext 开始在 pipeline 中向前传播 write 事件直到 HeadContext 。
一文搞懂Netty发送数据全流程 | 你想知道的细节全在这里(一)-鸿蒙开发者社区

客户端channel pipeline结构.png


在我们清楚了 write 事件的总体传播流程后,接下来就来看看在 write 事件传播的过程中Netty为我们作了些什么?这里我们以 channelHandlerContext.write() 方法为例说明。

标签
已于2022-7-18 17:31:56修改
收藏
回复
举报
回复
    相关推荐