Netty网络框架学习笔记-14(ChannelPipeline、ChannelHandler、 ChannelHandlerContext创建分析)

Netty网络框架学习笔记-14(ChannelPipeline、ChannelHandler、 ChannelHandlerContext创建分析_2020.06.22)

前言:

Netty 中的 ChannelPipeline 、 ChannelHandler 和 ChannelHandlerContext 是非常核心的组件, 从源码来分析 Netty 是如何设计这三个核心组件的,并分析是如何创建和协调工作的.

三者的关系

ChannelPipeline不是单独存在,它肯定会和Channel、ChannelHandler、ChannelHandlerContext关联在一起,

  1. 每当 ServerSocket 创建一个新的连接,就会创建一个 Socket,对应的就是目标客户端。
  2. 每一个新创建的 Socket 都将会分配一个全新的 ChannelPipeline(以下简称 pipeline)
  3. 每一个 ChannelPipeline 内部都含有多个 ChannelHandlerContext(以下简称 Context)
  4. 他们一起组成了双向链表,这些 Context 用于包装我们调用 addLast 方法时添加的 ChannelHandler(以下简称 handler)

jS6TBD.png

ChannelSocket 和 ChannelPipeline 是一对一的关联关系,而 pipeline 内部的多个 Context 形成了链表,Context 只是对 Handler 的封装

当一个请求进来的时候,会进入 Socket 对应的 pipeline,并经过 pipeline 所有的 handler,就是设计模式中的过滤器模式

ChannelPipeline作用及设计

Pipeline接口说明

ChannelPipeline 本身是个接口, 下面是其接口关系, 以及其中方法

jSgNzq.png

可以看到该接口继承了 inBound,outBound,Iterable 接口,表示他可以调用数据出站的方法和入站的方法,同时也能遍历内部的链表, 看看他的几个代表性的方法,基本上都是针对 handler 链表的插入,追加,删除,替换操作,类似是一个 LinkedList。同时,也能返回 channel(也就是 socket)

ChannelPipeline 接口文档:

OmEGVO.png

这是一个 handler 的 list,handler 用于处理或拦截入站事件和出站事件,pipeline 实现了过滤器的高级形式,以便用户控制事件如何处理以及 handler 在 pipeline 中如何交互。

上图描述了一个典型的 handler 在 pipeline 中处理 I/O 事件的方式,IO 事件由 inboundHandler 或者outBoundHandler 处理,并通过调用 ChannelHandlerContext.fireChannelRead 方法转发给其最近的处理程序 。

入站事件由入站处理程序以自下而上的方向处理,如图所示。入站处理程序通常处理由图底部的 I / O 线程生成入站数据。入站数据通常从如 SocketChannel.read(ByteBuffer) 获取。 出站反之、

通常一个 pipeline 有多个 handler,例如,一个典型的服务器在每个通道的管道中都会有以下处理程序:

消息协议的 编解码器( Decoder将二进制数据转换为 Java 对象, Encoder将 Java 对象转换为二进制数据) 、 自定义业务逻辑处理器、

自定义业务逻辑处理器程序不能将线程阻塞,会影响 IO 的速度,进而影响整个 Netty 程序的性能。如果你的业务程序很快, 就可以放在 IO 线程中,反之,你需要异步执行, 或者添加到NioEventLoop队列里面, 等当前NioEventLoop 处理完出站后触发从队列里面取任务执行, 不会阻塞。

ctx.executor().execute(()->{
           System.out.println("添加业务处理到NioEventLoop队列里面");
           ThreadUtil.sleep(30000);
       });

在或者在添加 handler 的时候添加一个线程池, 例如:

// 下面这个任务执行的时候,将不会阻塞 IO 线程,执行的线程来自 group 线程池 
pipeline.addLast(workerGroup,new MyBusinessHandler()); 

ChannelHandler作用及设计

Handler接口说明

j9GVoT.png

ChannelHandler 的作用就是处理 IO 事件或拦截 IO 事件,并将其转发给下一个处理程序 ChannelHandler。

Handler 处理事件时分入站和出站的,两个方向的操作都是不同的,因此,Netty 定义了两个子接口继承ChannelHandler

public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler

ChannelDuplexHandler 是一个双工可以同时处理 出入站的 Handler

ChannelHandlerContext作用及设计

Context接口说明

j9YurR.md.png

ChannelInboundInvokerChannelOutboundInvoker 这两个 invoker 就是针对入站或出站方法来的,在入站或出站 handler 的外层再包装一层,达到在方法前后拦截并做一些特定操作的目的。

j9teFf.png

Context 不仅仅时继承了他们两个的方法,同时也定义了一些自己的方法

这些方法能够获取 Context 上下文环境中对应的比如 channel,executor,handler ,pipeline,内存分配器,关联的 handler 是否被删除。

Context 就是包装了 handler 相关的一切,以方便 pipeline 方便的操作 handler

pipeline、handler、context 创建过程

  1. 任何一个 SocketChannel 创建的同时都会创建 一个 pipeline。
  2. 创建pipeline 时候都会创建好默认的 handler
  3. 创建handler时都会创建包装handlercontext , 这些 context 在 pipeline 中组成了双向链表。

1.0.1 pipeline的创建

j9aPlF.png

  1. 将 channel 赋值给 channel 字段,用于 pipeline 操作 channel。
  2. 创建一个 future 和 promise,用于异步回调使用。
  3. 创建一个既是 inbound 类型又是 outbound 类型的head和 只入站的tail的Context
  4. 最后,将两个 Context 互相连接,形成双向链表。

tailContext 和 HeadContext 非常的重要,所有 pipeline 中的事件都会流经他们,

1.0.2 pipeline#addLast方法是怎么创建Context

方法支持可变参数, 可以一次性添加多个

j90mYq.png

  1. pipeline 添加 handler,参数是线程池,name 是 null, handler 是我们或者系统传入的 handler。Netty 为了防止多个线程导致安全问题,同步了这段代码
  2. 检查这个 handler 实例是否是共享的,如果不是,并且已经被别的 pipeline 使用了,则抛出异常。
  3. 用 newContext(group, filterName(name, handler), handler) 方法,创建一个 Context。从这里可以看出来了, 每次添加一个 handler 都会创建一个关联 Context。
  4. 用 addLast0 方法,将 Context 追加到链表中。
  5. 如果这个通道还没有注册到 selecor 上,就将这个 Context 添加到这个 pipeline 的待办任务中。当注册好了以 后,就会调用 callHandlerAdded0 方法(默认是什么都不做,可以自定义实现这个方法)。

到这里,针对三对象创建过程,了解的差不多了,和最初说的一样,每当创建 SocketChannel 的时候都会创建 一个绑定的 pipeline,一对一的关系,创建 pipeline 的时候也会创建 tail 节点和 head 节点,形成最初的链表。tail 是入站 inbound 类型的 handler, head 既是 inbound 也是 outbound 类型的 handler。在调用 pipeline 的 addLast 方法的时候,会根据给定的 handler 创建一个 Context,然后,将这个 Context 插入到链表的尾端(tail 前面)。

总结:

  1. 每当创建 SocketChannel 的时候都会创建一个绑定的 pipeline,一对一的关系,创建 pipeline 的时候也会创建 tail 节点和 head 节点,形成最初的链表。
  2. 在调用 pipeline 的 addLast 方法的时候,会根据给定的 handler 创建一个 Context,然后,将这个 Context 插入到链表的尾端(tail 前面)。
  3. Context 包装 handler,多个 Context 在 pipeline 中形成了双向链表
  4. 入站方向叫 inbound,由 head 节点开始,出站方法叫 outbound ,由 tail 节点开始

1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵懵懂懂程序员

如果节省了你的时间, 请鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值