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

简介: Netty基础招式——ChannelHandler的最佳实践(二)

3.ChanneIHandler的异常传播机制


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


3.1InboundHandler的异常处理


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


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

98.png


得到输出如下

99.png

可以看到,虽然在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.2OutboundHandler的异常处理


OutboundHandler也是这么操作吗?


我们来做个实验。


  • 在write操作中抛出异常
  • 重写下exceptionCaught方法(这个方法在OutboundHandler中被标记为废弃)

重写组装下channelPipeline,第二个OutboundHandler中抛出异常

100.png

结果得到的输出如下:


101.png


咦?异常被吃掉了!!


不仅没有走进exceptionCaught方法,也没有其他异常抛出。

只是对后续handler的write方法不再执行,而flush方法还是都执行了一遍。


我们从源码找找原因吧。跟一下断点,马上就找到了原因:

102.png

AbstractChannelHandlerContext中,对OutboundHandler的write方法做了异常捕获,然后对ChannelPromise进行了通知。后续源码就不展开了,有兴趣的同学自己打断点跟一下,比较清楚。


那么问题来了,怎么在OutboundHandler中捕获异常呢?很明显就是直接添加ChannelPromise的回调


上代码:

103.png


在前面提到的ExceptionHandler中,复写write方法,然后注册一个ChannelPromise的Listener就行了。


当然,这个ExceptionHandler同样要注册到ChannelPipeline。


千万注意!!这里ExceptionHandler同样是添加到ChannelPipeline的tail方向的最后,而不是添加在head方向。无论是inboundHandler或者是outboundHandler的异常,都是按序向tail方向传递的。


异常就这样抓到了。


104.png


4.ChanneIHanbdler的最佳实践


其实前面已经对ChannelHandler的常用机制做了介绍,这里简单再介绍下两个最佳实践。


4.1不在ChanneIHandler中耗时处理


这一点其实在前一篇《 深入Netty逻辑架构,从Reactor线程模型开始》已经提到过,这里作为自定义ChannelHandler的最佳实践再强调一下,不在ChannelHandler中做耗时处理。


这里包括两点。


  • 不在I/O线程中直接处理耗时操作。
  • 也不把耗时操作放进EventLoop的任务队列中。


由于Netty4的无锁串行化设计,一旦任何耗时操作阻塞了某个EventLoop,那么这个EventLoop上的各个channel都会被阻塞。更详细内容可以参考上一篇《 深入Netty逻辑架构,从Reactor线程模型开始》。


所以,我们对于耗时操作,我们要放在自己的业务线程池中进行处理,如果需要发送response,需要提交任务到EventLoop的任务队列中执行。


给个简单的demo。

105.png


4.2统一的异常处理


在本文的第三节中,讲解了ChannelHandler的异常传播机制。


对于InboundHandler来说,如果你有跟handler特定相关的异常,可以直接在handler里进行exceptionCaught。如果是一些通用的异常,可以自定义ExceptionHandler注册到ChannelPipeline的末尾进行统一拦截。


对于OutboudHandler来说,就是通过自定义ExceptionHandler,重写对应方法,并注册ChannelPromise的Listener。同样的,ExceptionHandler注册到ChannelPipeline的末尾进行统一拦截。


所以,总结下如何添加一个“统一”的异常拦截器呢?


  • 自定义ExceptionHandler继承ChannelDuplexHandler,并注册到 tail节点前(ChannelPipeline的最后一个节点)。
  • 对于Inbound事件,我们需要在exceptionCaught()进行处理。
  • 对于Outbound事件,我们需要对OutboundHandler的不同方法(如write、flush)注册ChannelFutureListener事件。

异常拦截器的注册位置应该在tail方向的最后一个Handler。

106.png


注意,统一异常处理除了更优雅处理通用异常外,也是排查故障的好帮手。比如有时候对于编解码异常,可以在统一处理异常处捕获,快速定位问题。


5.小结


来简单回顾下吧。


本文介绍了什么是ChannelHandler和ChannelPipeline。能厘清InboundChannelHandler、OutboundChannelHandler、ChannelHandlerContext是什么吗?


然后对ChannelHandler的事件传播机制、异常处理机制做了详细介绍。


最后说明了日常开发中ChannelHandler的最佳实践。


希望对大家有所帮助。

目录
相关文章
|
3月前
|
Java 调度
Netty运行原理问题之ChannelHandler在Netty中扮演什么角色
Netty运行原理问题之ChannelHandler在Netty中扮演什么角色
|
6月前
|
网络协议 Java 容器
《跟闪电侠学Netty》阅读笔记 - ChannelHandler 生命周期
《跟闪电侠学Netty》阅读笔记 - ChannelHandler 生命周期
77 0
《跟闪电侠学Netty》阅读笔记 - ChannelHandler 生命周期
|
6月前
|
编解码 开发者
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制
142 0
|
6月前
|
前端开发 UED
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
221 0
|
存储 前端开发 Java
Netty 爱好者必看!一文详解 ChannelHandler 家族,助你快速掌握 Netty 开发技巧!
Netty 爱好者必看!一文详解 ChannelHandler 家族,助你快速掌握 Netty 开发技巧!
399 0
|
存储 安全 Java
Netty实战(六)ChannelHandler和ChannelPipeline
ChannelHandler 的生命周期发生在ChannelHandler被添加到 ChannelPipeline 中或者被从 ChannelPipeline 中移除时。这些方法中的每一个都接受一个 ChannelHandlerContext 参数
300 0
|
移动开发 安全 Java
Netty实战(十二)预置的ChannelHandler和编解码器(二)
HTTPS、WebSocket的添加使用和大型数据写入以及几种常见的序列化)
103 0
|
安全 Java 网络安全
Netty实战(十一)预置的ChannelHandler和编解码器(一)
作为一个通讯框架,通讯数据的安全性也是不可或缺的一部分。一般常见的像TLS/SSL这样的安全协议我们都应该熟悉。 我们在访问安全网站时都遇到过这些协议,但是它们也可用于其他不是基于HTTP的应用程序,如安全SMTP(SMTPS)邮件服务器甚至是关系型数据库系统。
149 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13500 1
|
6月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
131 1