Netty分布式pipeline管道创建方法跟踪解析
作者:向南是个万人迷 发布时间:2023-11-03 02:55:51
上一章节回顾:Netty分布式源码分析监听读事件
概述
pipeline, 顾名思义, 就是管道的意思, 在netty中, 事件在pipeline中传输, 用户可以中断事件, 添加自己的事件处理逻辑, 可以直接将事件中断不再往下传输, 同样可以改变管道的流向, 传递其他事件.这里有点类似于Spring的AOP, 但是比AOP实现起来简单的多
事件通常分为两种, 一是inBound事件, 另一种是outBound事件, inBound事件, 顾名思义, 就是从另一端流向自己的事件, 比如读事件, 连接完成事件等等, outBound, 是从自己流向另一端的事件, 比如连接事件, 写事件, 刷新缓冲区事件等等
在netty中, 事件是通过handler对象进行处理的, 里面封装着事件的处理逻辑.而每个handler, 是由HandlerContext进行包装的, 里面封装了对事件传输的操作
通过之前的学习, 我们知道每一个channel绑定一个pipeline, 那么pipeline和handler又是什么关系呢?
其实pipeline我们可以理解成是一个双向链表的数据结构, 只是其中存放的并不是数据而是HandlerContext, 而HandlerContext又包装了handler, 事件传输过程中, 从头结点(或者尾节点)开始, 找到下一个HandlerContext, 执行其Handler的业务逻辑, 然后再继续往下走, 直到执行到尾节点(或者头结点, 反向)为止, 通过一幅图了解其大概逻辑:
这里head代表pipeline的头结点, tail代表pipeline的尾节点, 这两个节点是会随着pipeline的初始化而创建, 并且不会被删除
HandlerContext的简单继承关系比较简单, 默认的是DefaultChannelHandlerContext, 继承于AbstractChannelHandlerContext
而Handler分为InboundHandler和outBoundHandler, Inbound专门处理inbound事件, outBound专门用于处理outBound事件
继承关系如下图:
从图中不难看出, 如果属于ChannelInboundHandler的子类, 则属于Inbound类型的handler
如果是ChannelOutboundHandler的子类, 则属于Outbound类型的handler
了解了其大概逻辑, 我们继续跟到源码中, 看其实如何体现的:
pipeline的创建
回顾之前NioServerSocketChannel的创建过程
我们看AbstractChannel的构造方法:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
我们跟到newChannelPipeline()中:
protected DefaultChannelPipeline newChannelPipeline() {
//传入当前channel
return new DefaultChannelPipeline(this);
}
我们看到这里创建了一个DefaultChannelPipeline, 并将自身channel传入
继续跟DefaultChannelPipeline的构造方法:
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
首先保存了当前channel
然后保存了两个属性succeededFuture, voidPromise, 这两个属性是Future相关的内容, 之后的章节会讲到
首先, 这里初始化了两个节点, head节点和tail节点, 在pipeline中代表头结点和尾节点
我们首先跟到这tail节点类中,也就是TailContext类:
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
TailContext(DefaultChannelPipeline pipeline) {
//inbound处理器
super(pipeline, null, TAIL_NAME, true, false);
//将当前节点设置为已添加, head和tail.....
setAddComplete();
}
//自身也是handler
@Override
public ChannelHandler handler() {
return this;
}
//方法省略
这个是DefualtPipline的内部类, 首先看其继承了AbstractChannelHandlerContext类, 说明自身是个HandlerContext, 同时也实现ChannelInboundHander接口, 并且其中的handler()方法返回了自身, 说明自身也是handler, 而实现ChannelInboundHander, 说明自身只处理Inbound事件
构造方法中, 调用了父类的构造器, 看其中参数:
pipeline是自身所属的pipeline
executor为null
TAIL_NAME是当前handler, 也就是自身的命名
true代表自身是inboundHandler
fasle代表自身不是outboundHandler
继续跟到父类构造方法中:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
//名字
this.name = ObjectUtil.checkNotNull(name, "name");
//pipeline
this.pipeline = pipeline;
//线程处理器
this.executor = executor;
//事件标识
this.inbound = inbound;
this.outbound = outbound;
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
这里初始化了自身父类的几个属性
pipeline为自身绑定的pipeline
exeutor是线程执行器, 这里为空
inbound和outbound是事件标志, 这里分别是true和false, 也就是自身属于inboundHnadler而不属于outboundHandler
回到DefaultChannelPipeline的构造方法:
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
再看HeadContext类:
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
private final Unsafe unsafe;
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, false, true);
unsafe = pipeline.channel().unsafe();
setAddComplete();
}
@Override
public ChannelHandler handler() {
return this;
}
}
看过了tail节点, head节点就不难理解, 同样继承了AbstractChannelHandlerContext, 说明自身是一个HandlerContext, 与tail不同的是, 这里实现了ChannelOutboundHandler接口和ChannelOutboundHandler接口, 说明其既能处理inbound事件也能处理outbound的事件, handler方法返归自身, 说明自身是一个handler
在构造方法中初始化了一个Unsafe类型的成员变量, 是通过自身绑定的channel拿到的, 说明这个类中可以进行对channel的读写操作
这里同样调用了父类的构造方法, 不同的是, 这里inbound参数传入了false, 而outbound参数传入了true, 这里说明这里标志的事件是outbound事件
同学们可能疑惑, 为什么同时执行ChannelOutboundHandler接口和ChannelOutboundHandler但是标志的事件不同?
其实这两个地方应用的场景是不同的, 继承ChannelOutboundHandler和ChannelOutboundHandler, 说明其既能处理inbound事件也能处理outBound的事件, 但是只有outbound属性为true说明自身是一个outboundhandler, 是一个可以处理inbound事件的outboundhandler(估计被绕晕了), 这两种handler主要是保证在事件传输中保证事件的单方向流动, 在后面事件传输我们能领会到
再跟进父类的构造方法, 又是我们熟悉的部分:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
//名字
this.name = ObjectUtil.checkNotNull(name, "name");
//pipeline
this.pipeline = pipeline;
//线程处理器
this.executor = executor;
//事件标识
this.inbound = inbound;
this.outbound = outbound;
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
初始化了pipeline, executor, 和事件标识的属性
回到DefaultChannelPipeline的构造方法:
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
我们介绍完了head, 和tail这两个context, 继续往下看:
head.next = tail;
tail.prev = head;
tail节点和head节点中的next和prev属性, 其实是其父类AbstractChannelHandlerContext, 每一个handlerContext都拥有这两个属性, 代表自身的下一个节点和上一个节点, 因为我们概述中介绍过pipeline其实是一个双向链表, 所以其中每一个节点必须有指向其他节点的指针, 熟悉双向链接数据结构的同学应该不会陌生
这里head节点的next属性是tail节点, tail节点的prev属性是head, 说明当前双向链表只有两个节点, head和tail, 其中head下一个节点指向tail, tail的上一个节点指向head, 如图所示:
来源:https://www.cnblogs.com/xiangnan6122/p/10204243.html


猜你喜欢
- 在2020.1.1版本之前IDEA pom文件导包是这样的最近新装新版本IDEA之后,这个图标没有了,对于习惯旧操作没有图标了还真不习惯。就
- 仿水波纹流球进度条控制器,Android实现高端大气的主流特效,供大家参考,具体内容如下效果图:CircleView这里主要是实现中心圆以及
- pageHelper分页失效及配置问题我在使用pageHelper的系统中加入mybatis-plus, 结果所有分页都失效了原因我这边的失
- RecyclerView上拉加载,先看效果:网上有很多这类得框架,不过在自己的项目只用到上拉加载的功能,所以自己封装一个简单点的。主要依赖B
- 上来就给点干货吧利用脚本,一键设置java环境变量(默认安装路径)@echo offcolor 0aecho.---------------
- 不想废话,直接写了!因为是留给自己做随笔的,所以大神们看到别喷…… 1.必须有微信公众账号 2.你也可以申请测试微信号,
- 本文实例为大家分享了C#实现简易多人聊天室的具体代码,供大家参考,具体内容如下只有一个群聊的功能服务端using System;using
- 一、场景笔者就Zuul网关下实现其负载均衡与熔断机制(雪崩)进行实践,前提是已经导入zuul相关依赖springboot版本:1.5.9.R
- 这篇文章主要介绍了SpringBoot下如何实现支付宝接口的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 目录Fanout交换机模型RabbitMQ控制台操作新增两个队列绑定fanout交换机示例效果图核心代码消息发布消息订阅Fanout交换机模
- 目录SpringBoot整合OpenApiOpenAPI依赖编写配置类改造优化OpenAPI常用注解介绍实体类controller类演示网上
- 概念代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理。一个代理体系建立涉及到3个参与角色:真实对象(A),代
- 本文实例讲述了C#实现对Json字符串处理方法,分享给大家供大家参考。具体分析如下:一般对于web应用开发人员来说对Json字符串都会很熟悉
- java 中遍历取值异常(Hashtable Enumerator)解决办法用迭代器取值时抛出的异常:java.util.NoSuchEle
- 本文实例为大家分享了Android自定义控件实现简单滑动开关的具体代码,供大家参考,具体内容如下ToggleButton 滑动开关项目概述滑
- 代码如下:try { // 创建一个线程 Thread thread = new Thread() {
- 在淘宝、京东等电商举办活动的时候,经常可以看到在移动客户端推出的各种刮奖活动,而这种活动也受到了很多人的喜爱。从客户端的体验来说,这种效果应
- 开发Android程序时,有时候在程序运行的时候,不能让系统休眠,否则有一些运行会停止,因此我们需要设置禁止休眠,有两种方式: &
- 传播行为定义关于客户端和被调用方法的事务边界。Spring定义了7种截然不同的传播行为。1,PROPAGATION_MANDATORY,表示
- class文件中的特殊字符串首先说明一下, 所谓的特殊字符串出现在class文件中的常量池中,本着循序渐进和减少跨度的原则, 首先把clas