Netty基础招式——ChannelHandler的最佳实践

如果说线程模型是Netty的 “核心内功”,那么ChannelHandler就是Netty最著名的 “武功招式”,是我们日常使用Netty时接触最多的组件。

Netty基础招式——ChannelHandler的最佳实践

引用《Netty in action》中的一句话

From the appliaction developer’s standpoint, the primary component of Netty is the ChannelHandler.

所以,阿丸尽可能通过 图 和 代码demo,来让大家获得最直观的使用体验。

本文预计阅读时间约 10分钟,将重点围绕以下几个问题展开:

  • 什么是ChannelHandler和ChannelPipeline?

  • ChannelHandler的事件传播机制

  • ChannelHandler的异常处理机制

  • ChannelHandler的最佳实践

1. 什么是ChannelHandler和ChannelPipeline?


ChannelHandler是一个包含所有应用处理逻辑的容器载体,用来对Netty的输入输出数据进行加工处理。

比如数据格式转换、异常处理等

ChannelPipeline 则是 ChannelHandler 的容器载体,负责以链式的形式调度各个注册的ChannelHandler。

我们回顾下之前介绍过的Netty逻辑架构,观察下ChannelPipeline和ChannelHandler的位置。

Netty基础招式——ChannelHandler的最佳实践

再从局部放大,可以更加明确地看到ChannelPipeline和ChannelHandler的作用。

Netty基础招式——ChannelHandler的最佳实践

如上图所示,当EventLoop中监听到事件后,会对I/O事件进行处理。而这个处理,就是交给ChannelPipeline进行,更严格地说,是交给ChannelPipeline中的各个ChannelHandler按照一定的顺序进行处理。

根据数据的流向,Netty把ChannelHandler分为2类,InboundHandler和OutboundHandler。

Netty基础招式——ChannelHandler的最佳实践

如上图所示,Netty接收到数据后,经过若干 InboundHandler 处理后接收成功。如果要输出数据,就需要经过若干个 OutboundHandler 处理完成后发送。

比如,我们经常需要对接收到的数据进行解码,就是在某一个专门decode的InboundHandler中处理的。如果要发送数据,往往需要编码,就是在某一个专门encode的OutBoundHandler中处理的。

值得一提的是,虽然我们在使用Netty时,直接打交道的是ChannelPipeline和ChannelHandler,但是,它们之间有一座“隐形”的桥梁,名字叫做ChannelHandlerContext。

顾名思义,ChannelHanderContext就是ChannelHandler的上下文,每个 ChannelHandler 都对应一个 ChannelHandlerContext。

每一个 ChannelPipeline 都包含多个 ChannelHandlerContext,所有 ChannelHandlerContext 之间组成了双向链表。如下图所示。

Netty基础招式——ChannelHandler的最佳实践

其中,有两个特殊的ChannelHandlerContext,分别是HeadContext和TailContext,表示双向链表的头尾节点。

Netty基础招式——ChannelHandler的最佳实践

从类图上可以看到,HeadContext同时实现了ChannelInboundHandler和ChannelOutboundHandler。因此,HeadContext在读取数据时作为头节点,向后传递InBound事件,同时,在写数据时作为尾节点,处理最后的OutBound事件。

TailContext只实现了ChannelInboundHandler。它在InBound事件传递的末尾,负责处理一些资源释放的工作。在OutBound事件传递的第一个节点,不做任何处理,仅仅传递OutBound事件给prev节点。

而我们平时自定义的ChannelHandler,就是插在这两个头尾节点之间的。

至此,我们对ChannelHandler和ChannelPipeline有了基本的认识。具体到实践上,我们该如何正确地使用ChannelHandler呢?

对ChannelHandler的使用,必须先了解ChannelHandler的事件传播机制和异常处理机制。

2. ChannelHandler的事件传播机制


前面我们提到了Netty中的两种事件类型,Inbound事件和Outbound事件,分别对应InboundHandler和OutbountHandler进行处理。

当我们使用Netty进行开发的时候,必须了解Inbound事件和Outbound事件在ChannelPipeline中如何进行“事件传播”,注册InboundHandler和OutboundHandler的顺序有什么影响。

话不多说,我们先来一个demo直观地感受一下。

自定义一个ChannelInboundHandler

Netty基础招式——ChannelHandler的最佳实践

自定义一个ChannelOutboundHandler

Netty基础招式——ChannelHandler的最佳实践

简单组装一下EchoPipelineServer,特别注意一下 6个handler 的注册顺序。

Netty基础招式——ChannelHandler的最佳实践

然后我们通过命令行简单访问一下这个Netty Server

curl localhost:8081

可以看到控制台的如下输出

Netty基础招式——ChannelHandler的最佳实践

这样就清楚了事件传播顺序:

- 对于Inbound事件,InboundHandler的处理顺序是和注册顺序一致

- 对于Outbound事件,OutboundHandler的处理顺序和注册顺序相反

结合上一节说的HeadContext和TailContext,我们画个图来更直观地看一下这个ChannelPipeline中的handler构建顺序是怎样的。

Netty基础招式——ChannelHandler的最佳实践

在上面的ChannelInitializer中,我们按需添加了3个InboundHandler和3个OutboundHandler。所以,在头节点HeadContext和TailContext之间,有序构成了双向链表。

而InboundHandler3中,通过调用 ctx.channel.writeAndFlush( msg ) 方法,将消息从TailContext开始,依据OutboundHandler的路径向HeadContext方向传播出去。具体可以看下DefaultChannelPipeline类中的实现

Netty基础招式——ChannelHandler的最佳实践

虽然这里是双向链表,但是无论是Inbound事件还是Outbound事件,在按序访问链表节点时,会根据事件类型进行过滤。

3. ChannelHandler的异常传播机制


我们已经了解了ChannelPipeline的链式传递规则,如果双向链表中任意一个handler抛出了异常,那么应该怎么处理呢?

3.1 InboundHandler的异常处理

我们修改下示例中的TestInboudHandler进行模拟。

  • channelRead方法中抛出异常

  • 重写exceptionCaught方法,打印当前节点捕获异常情况

Netty基础招式——ChannelHandler的最佳实践

得到输出如下

Netty基础招式——ChannelHandler的最佳实践

可以看到,虽然在InboundHander1中抛出了异常,但是仍然会被3个InboundHandler都捕获一次,并按序向tail节点方向传递,然后抛出异常。

我们也看到了,Netty给出了会警告,在最后的节点没有进行异常处理。

An exceptionCaught() event was fired, and it reached at the tail of the pipeline.

It usually means the last handler in the pipeline did not handle the exception.

3.2 OutboundHandler的异常处理

OutboundHandler也是这么操作吗?

我们来做个实验。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

腾讯T3大牛总结的500页MySQL实战笔记意外爆火,P8看了直呼内行

腾讯T3大牛总结的500页MySQL实战笔记意外爆火,P8看了直呼内行
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
img src=“https://i-blog.csdnimg.cn/blog_migrate/97c99f5ff71a4a5e1bda62e3e399b3cc.jpeg” alt=“img” style=“zoom: 33%;” />

最后

[外链图片转存中…(img-GRNBrUwL-1712052711382)]

[外链图片转存中…(img-aeKbAext-1712052711383)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值