2021-02-04 18:56发布
先来看看Netty官方有关出站入站机制的解释:按照图片的理解,则是在通道中,每次出现读事件时,会从头至尾依次调用Inbound即入站方法处理;而触发写事件时,则会从尾到头依次调用outbound即出站方法处理。
这里会给人一种错觉,那就是netty在内部维护了两个单向链表实现出站入站操作,而事实上,ChannelPipeline中实际上是维护了一条由ChannelHandlerContext组成的双向链表,这里我们后面再说。
我们先来看一个例子,模拟一个场景,客户端向服务器发送数据,服务器写回客户端一个响应数据:整体流程如图:下面进行每一步的讲解:
首先服务器接收到来自客户端的数据,通过read方法获取,此时数据是存储在ByteBuf中
读取后的下一步首先要解码(入站)获取其真实数据,并且我们在解码器中定义了一个方法,将数据写回
执行写回操作之前,首先会调用Encoder(出站)进行编码,最后才将数据写回给客户端
以上体现了出站和入站的基本思想,即:1. 入站操作主要是指读取数据的操作;而出站操作主要是指写入数据的操作2. 入站会从先读取,再执行入站的Handler;出站会先执行出站的Handler,再写入
到这里其实出入站的操作已经解释完毕,但是底层的问题还没有解决:为什么只用了一个双向链表就可以实现?
还是看源码,首先是ChannelPipeline的默认实现类:
public class DefaultChannelPipeline implements ChannelPipeline { final AbstractChannelHandlerContext head; final AbstractChannelHandlerContext tail; /*省略*/}12345
这里只保留了核心的部分,可以看到,它的首尾结点是由AbstractChannelHandlerContext 组成的,我们继续点开:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint { volatile AbstractChannelHandlerContext next; volatile AbstractChannelHandlerContext prev;}1234
这里应该看得比较明白了,它拥有next指向下一个结点,也有prev指向前一个结点。这里先提一点,handler的事件处理是通过fireChannelxxx完成的。
我们在AbstractChannelHandlerContext中找到如下方法:
private AbstractChannelHandlerContext findContextInbound(int mask) { AbstractChannelHandlerContext ctx = this; EventExecutor currentExecutor = executor(); do { ctx = ctx.next; } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND)); return ctx;}private AbstractChannelHandlerContext findContextOutbound(int mask) { AbstractChannelHandlerContext ctx = this; EventExecutor currentExecutor = executor(); do { ctx = ctx.prev; } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND)); return ctx;}1234567891011121314151617
虽然看起来像是用了两个链表,其实是通过遍历同一个双向链表来实现入站和出站操作的。
当操作为读时,会调用findContextInbound(int mask)方法,从头至尾遍历InboundHandler,注意,只遍历Inbound操作;
而当操作为写时,会调用findContextOutbound(int mask),从尾到头遍历OutboundHandler,这时只有OutBound操作被执行
最多设置5个标签!
先来看看Netty官方有关出站入站机制的解释:
按照图片的理解,则是在通道中,每次出现读事件时,会从头至尾依次调用Inbound即入站方法处理;
而触发写事件时,则会从尾到头依次调用outbound即出站方法处理。
这里会给人一种错觉,那就是netty在内部维护了两个单向链表实现出站入站操作,而事实上,ChannelPipeline中实际上是维护了一条由ChannelHandlerContext组成的双向链表,这里我们后面再说。
我们先来看一个例子,模拟一个场景,客户端向服务器发送数据,服务器写回客户端一个响应数据:
整体流程如图:
下面进行每一步的讲解:
首先服务器接收到来自客户端的数据,通过read方法获取,此时数据是存储在ByteBuf中
读取后的下一步首先要解码(入站)获取其真实数据,并且我们在解码器中定义了一个方法,将数据写回
执行写回操作之前,首先会调用Encoder(出站)进行编码,最后才将数据写回给客户端
以上体现了出站和入站的基本思想,即:
1. 入站操作主要是指读取数据的操作;而出站操作主要是指写入数据的操作
2. 入站会从先读取,再执行入站的Handler;出站会先执行出站的Handler,再写入
到这里其实出入站的操作已经解释完毕,但是底层的问题还没有解决:为什么只用了一个双向链表就可以实现?
还是看源码,首先是ChannelPipeline的默认实现类:
这里只保留了核心的部分,可以看到,它的首尾结点是由AbstractChannelHandlerContext 组成的,我们继续点开:
这里应该看得比较明白了,它拥有next指向下一个结点,也有prev指向前一个结点。
这里先提一点,handler的事件处理是通过fireChannelxxx完成的。
我们在AbstractChannelHandlerContext中找到如下方法:
虽然看起来像是用了两个链表,其实是通过遍历同一个双向链表来实现入站和出站操作的。
当操作为读时,会调用findContextInbound(int mask)方法,从头至尾遍历InboundHandler,注意,只遍历Inbound操作;
而当操作为写时,会调用findContextOutbound(int mask),从尾到头遍历OutboundHandler,这时只有OutBound操作被执行
一周热门 更多>