上面一篇文章介绍了 ChannelPipeline,它维护了一个有序的 ChannelHandler 列表。当 ChannelHandler 加入到 ChannelPipeline 的时候,会创建一个对应的 ChannelHandlerContext 并绑定,ChannelPipeline 实际维护的是和 ChannelHandlerContext 的关系,例如在 DefaultChannelPipeline:

//
public class DefaultChannelPipeline implements ChannelPipeline {//...final AbstractChannelHandlerContext head; // 头结点final AbstractChannelHandlerContext tail; // 尾结点
}

DefaultChannelPipeline 会保存第一个ChannelHandlerContext以及最后一个ChannelHandlerContext的引用。而 AbstractChannelHandlerContext 中维护了 next 和 prev 指针:

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {//...volatile AbstractChannelHandlerContext next; // 前驱节点volatile AbstractChannelHandlerContext prev; // 后置节点
}

这样 ChannelHandlerContext 之间形成了双向链表。

1.ChannelHandlerContext 方法

我们来看一下 ChannelHandlerContext 的继承关系:


它继承了 AttributeMap 用于存储信息,实现了 ChannelInboundInvoker 和 ChannelOutboundInvoker 可进行事件的传播。

PS:ChannelPipeline 和 ChannelHandlerContext 都同时继承了 ChannelInboundInvoker 和 ChannelOutboundInvoker接口

1.AttributeMap 接口的方法

public interface AttributeMap {<T> Attribute<T> attr(AttributeKey<T> key);<T> boolean hasAttr(AttributeKey<T> key);
}

2.ChannelInboundInvoker 接口的方法

public interface ChannelInboundInvoker {// 触发对下一个 ChannellnboundHandler 上的 channelRegistered() 方法的调用ChannelInboundInvoker fireChannelRegistered();// 触发对下一个 ChannellnboundHandler 上的 channelUnregistered() 方法的调用ChannelInboundInvoker fireChannelUnregistered();// 触发对下一个 ChannellnboundHandler 上的 channelActive() 方法(已连接)的调用ChannelInboundInvoker fireChannelActive();// 触发对下一个 ChannellnboundHandler 上的 channellnactive() 方法(已关闭)的调用ChannelInboundInvoker fireChannelInactive();// 触发对下一个 ChannellnboundHandler 上的 exceptionCaught (Throwable)方法的调用ChannelInboundInvoker fireExceptionCaught(Throwable cause);// 触发对下一个 ChannellnboundHandler 上的 userEventTriggered (Object evt)方法的调用ChannelInboundInvoker fireUserEventTriggered(Object event);// 触发对下一个 ChannellnboundHandler 上的 channelRead() 方法(已接收的消息)的调用ChannelInboundInvoker fireChannelRead(Object msg);// 触发对下一个 ChannellnboundHandler 上的 channelReadComplete() 方法的调用ChannelInboundInvoker fireChannelReadComplete();// 触发对下一个 ChannellnboundHandler 上的 channelWritabilityChanged() 方法的调用ChannelInboundInvoker fireChannelWritabilityChanged();
}

3.ChannelOutboundInvoker 接口的方法

public interface ChannelOutboundInvoker {// 绑定到给定的 SocketAddress,并返回 ChannelFutureChannelFuture bind(SocketAddress localAddress);// 连接给定的 SocketAddress,并返回 ChannelFutureChannelFuture connect(SocketAddress remoteAddress);// 从之前分配的 EventExecutor 注销,并返回 ChannelFutureChannelFuture deregister();// 从远程节点断开,并返回 ChannelFutureChannelFuture disconnect(ChannelPromise promise);// 将数据从 Channel 读取到第一个入站缓冲区;如果读取成功则触发一个 channelRead 事件// 并在最后一个消息被读取完成后,通知 ChannelInboundHandler 的 channelReadComplete 方法ChannelOutboundInvoker read();// 将写到一个临时队列中ChannelFuture write(Object msg);// 将临时队列中的消息写到 Socket 缓冲区ChannelOutboundInvoker flush();// write + flush = 直接写到 Socket 缓冲区ChannelFuture writeAndFlush(Object msg);//...还有一些不常用的方法就不列出来了
}

PS:关于 write() 和 flush() 的源码分析可以参考这篇文章…

4.ChannelHandlerContext 自己的方法

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {// 获取绑定这个实例的 ChannelChannel channel();// 获取调度事件的 EventExecutorEventExecutor executor();// 获取这个实例的唯一名称String name();// 获取绑定这个实例的 ChannelHandlerChannelHandler handler();// 如果所关联的 ChannelHandler 已被从 ChannelPipeline 中移除,则返回 trueboolean isRemoved();// 获取这个实例所关联的 ChannelPipelineChannelPipeline pipeline();// 获取和这这个实例相关联的 Channel 所配置的 ByteBufAllocatorByteBufAllocator alloc();}

注:在我们平时编写 ChannelHandler 要将数据写到 Socket 中时,有两种方案:

  • ctx.channel().writeAndFlush,将从 Pipeline 的尾部开始往前找 OutboundHandler
  • ctx.writeAndFlush 会从当前 handler 往前找 OutboundHandler

2.ChannelHandlerContext 子类

来看 ChannelHandlerContext 继承类图:

1.AbstractChannelHandlerContext

是 ChannelHandlerContext 的一个抽象实现

  • 定义了链表的关键 – next、prev 指针

  • 定义了很多 ChannelHandlerContext 节点状态

  • 实现了上面列出的所有方法,包括父接口 AttributeMap、ChannelInboundInvoker 、ChannelOutboundInvoker 及 ChannelHandlerContext 自己的方法(见下图)

注:由于 ChannelContextHandler 将 AttributeMap 和 ChannelInboundInvoker 接口的方法 @Override 了,所以我们在上图看到 attr() 和 fireXX() 都显示的是实现自 ChannelContextHandler 。

2.DefaultChannelHandlerContext

是 ChannelHandlerContext 的默认实现类,不过主要功能都在AbstractChannelHandlerContext 中已经实现好了,DefaultChannelHandlerContext 非常简单

3.HeadContext

是 ChannelPipeline 中的头节点,是一个比较特殊的节点,

  • 它是 ChannelHandlerContext 的实现类,因此是一个 ChannelPipeline 内部链表节点
  • 它实现了入站出站接口 ChannelOutboundHandler 和 ChannelInboundHandler,因此是一个双向处理器。

HeadContext 里面有很多方法。通过内部持有的 unsafe 对象来做具体的读、写、连接、绑定端口等IO事件,功能上看 HeadContext 会将事件传播到下一个入站处理器。

4.TailContext

是 ChannelPipeline 中的尾节点,也是一个比较特殊的节点

  • 它是 ChannelHandlerContext 的实现类,因此是一个 ChannelPipeline 内部链表节点
  • 它实现了 ChannelInboundHandler,因此是一个入站事件处理器,可处理入站事件

不过 TailContext 继承自ChannelInboundHandler 的很多入站方法都是空方法。TailContext 大部分情况下是什么都不做,有几个方法会将未处理的异常打印 Warn 日志。

3.事件传播方法 – fireXX() 源码分析

事件传播方法继承自 ChannelInboundInvoker 接口,在 AbstractChannelHandlerContext 抽象类中被实现。我们下面来分析一下 fireChannelRead() 方法的源码:

@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);return this;
}
/**
* 1.向后查找,如果 ChannelHandlerContext 为 inbound 的类型 就返回
*/
private AbstractChannelHandlerContext findContextInbound(int mask) {AbstractChannelHandlerContext ctx = this;do {ctx = ctx.next;} while ((ctx.executionMask & mask) == 0);return ctx;
}/*** 2.触发 ChannelPipeline 中后面一个 ChannelInboundHandler 的 channelRead 方法被调用
*/
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);// 获取 ChannelHandlerContext 的 EventExecutorEventExecutor executor = next.executor();// 如果EventExecutor线程在EventLoop线程中,就直接调用if (executor.inEventLoop()) {// 核心!!next.invokeChannelRead(m);// 反之则递交给executor执行} else {executor.execute(new Runnable() {@Overridepublic void run() {next.invokeChannelRead(m);}});}
}/**
* 3.调用下一个入站处理器的 channelRead 方法,将消息传递过去
*/
private void invokeChannelRead(Object msg) {if (invokeHandler()) {try {// 获取handler对象(ChannelInboundHandler)并且执行该对象的 channelRead 方法......((ChannelInboundHandler) handler()).channelRead(this, msg);} catch (Throwable t) {// 通知 Inbound 事件的传播,发生异常notifyHandlerException(t);}} else {fireChannelRead(msg);}
}

实际上其他的 fireXX() 方法也是类似的,基本思想就是找到下一个 ChannelHandlerContext 节点然后调用其内部的ChannelHandler 对象对应的方法,其他类似方法就不一一分析了。

【Netty】原理分析:ChannelHandlerContext相关推荐

  1. Netty时间轮调度原理分析,再不了解你就out啦

    一.时间轮介绍 之前公司内部搭建的延迟队列服务有用到时间轮,但是一直没有了解过它的实现原理. 最近有个和支付宝对接的项目,支付宝接口有流量控制,一定的时间内只允许 N 次接口调用,针对一些业务我们需要 ...

  2. Netty技术细节源码分析-Recycler对象池原理分析

    本文是该篇的修正版 本文的github地址:点此 该文所涉及的netty源码版本为4.1.6. Netty的对象池Recycler是什么 Recycler是Netty中基于ThreadLocal的轻量 ...

  3. 原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析

    原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析 - 一.大致介绍 1.了解过netty原理的童鞋,其实应该知道工作线程组的每个子线程都维护了一个任 ...

  4. 「Netty系列」使用wireshark对网络通信扑捉,进行三次握手和四次挥手原理分析(Netty前置二)

    正常下班,文章走起.在网络的通信的时候,都有听说过三次握手四次挥手.但是对其原理是否清晰?本篇文章通过使用wireshark对网络通信扑捉,进行原理分析. 1 BIO代码实现 //服务端代码 publ ...

  5. 精尽 Netty 原理与源码专栏( 已经完成 61+ 篇,预计总共 70+ 篇 )

    只更新在笔者的知识星球,欢迎加入一起讨论 Netty 源码与实现. 目前已经有 1000+ 位球友加入- 进度:已经完成 60+ 篇,预计总共 70+ 篇,完成度 90% . 对应 Netty 版本号 ...

  6. (高级)Dubbo 第五章 Dubbo及RocketMQ底层-Netty原理

    Netty原理 Netty 是一个高性能.异步事件驱动的NIO 框架,基于JAVA NIO 提供的API 实现.它提供了对TCP.UDP 和文件传输的支持,作为一个异步NIO 框架,Netty 的所有 ...

  7. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  8. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  9. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

  10. 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...

最新文章

  1. html修改headicon,index.html
  2. 重磅,武汉大学获捐10亿元!
  3. 花果山第一届猿类分级考试实录--Talk is cheap,Show me the code
  4. 三星r750美版刷Android,这是才是全面派 三星space monitor详解
  5. 简述区块链(1)- 也许只有这一篇
  6. leetcode574. 当选者(SQL)
  7. 顺序表中有效元素的长度_图解线性表,启动数据结构的大门,深刻理解链式存储和顺序存储!...
  8. 读书笔记_中国期货市场量化交易(李尉)02
  9. C++程序设计(三:可视化)
  10. maven中引用JDK中的tools jar
  11. 当update语句提交后,数据库做了哪些操作?
  12. PhotoshopCC 2018(19.1.3)绿色精简/增强无需注册安装直接用
  13. openMSP430 介绍
  14. 滴滴裁员赔偿方案,这才是好聚好散!
  15. ios 扇形 按钮_iOS开发教程之扇形动画的实现
  16. 银行信用卡办卡申请进度查询API接口地址
  17. python中文分词之jieba分词的使用
  18. 过拟合原因及解决办法
  19. win7计算机建立无线网络连接不上,Win7笔记本无线网络连接不上如何解决?
  20. 人工智能基础复习1——人工智能概述

热门文章

  1. 【Java自学】搬砖中年人代码自学之路Lesson 1
  2. 纯JS实现左右滑动轮播图
  3. 一个直播弹幕机器人诞生过程,Python制作自动发送弹幕小程序
  4. Unity UMP插件编辑里运行正常,打包出来后报cant load “libvlccore“ library
  5. 如何输入一个二进制:在开头+0b
  6. Notepad++配置c/c++环境
  7. 在已安装好的nginx 添加rtmp模块
  8. 期货CTP接口与程序化(量化交易)的对接(2)——基本概念
  9. java中多重继承_java中的接口实现多重继承
  10. .Net core基于xUnit的单元测试查看测试覆盖率