这篇文章主要讲解了“netty中pipeline异常事件怎么处理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“netty中pipeline异常事件怎么处理”吧!
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { throw new Exception("throw Exception"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(cause.getMessage()); }
我们在handler
的channelRead
方法中主动抛出异常, 模拟程序中出现异常的场景, 经测试会发现, 程序最终会走到exceptionCaught
方法中, 获取异常对象并打印其信息
那么抛出异常之后, 是如何走到exceptionCaught
方法的呢?
我们回顾之前小节channelRead
事件的传播流程, channelRead
方法是在AbstractChannelHandlerContext
类的invokeChannelRead
方法中被调用
private void invokeChannelRead(Object msg) { if (invokeHandler()) { try { //调用了当前handler的channelRead方法, 其实就是head对象调用自身的channelRead方法 ((ChannelInboundHandler) handler()).channelRead(this, msg); } catch (Throwable t) { //发生异常的时候在这里捕获异常 notifyHandlerException(t); } } else { fireChannelRead(msg); } }
这里不难看出, 当调用户自定义的handler
的channelRead
方法发生异常之后, 会被捕获, 并调用notifyHandlerException
方法, 并传入异常对象, 也就是我们示例中抛出的异常
private void notifyHandlerException(Throwable cause) { //代码省略 invokeExceptionCaught(cause); }
private void invokeExceptionCaught(final Throwable cause) { if (invokeHandler()) { try { //当前handler调用exceptionCaught()方法 handler().exceptionCaught(this, cause); } catch (Throwable error) { //代码省略 } } else { fireExceptionCaught(cause); } }
走到这里一切都明白了, 这里调用了当前handler
的exceptionCaught
方法, 也就是我们重写的exceptionCaught
方法
知道了为什么会走到exceptionCaught
方法之后, 我们再进行剖析异常事件的传播流程
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(cause.getMessage()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //写法1 ctx.fireChannelRead(cause); //写法2 ctx.pipeline().fireExceptionCaught(cause); }
这两种写法我们并不陌生, 可能我们能直接猜到, 第一种写法是从当前节点进行传播, 第二种写法则从头结点或者尾节点进行转播, 那么和传播inbound
事件或outbound
事件有什么区别呢?我们先以第二种写法为例, 剖析异常事件传输的整个流程
public final ChannelPipeline fireExceptionCaught(Throwable cause) { AbstractChannelHandlerContext.invokeExceptionCaught(head, cause); return this; }
我们看到invokeExceptionCaught
传入了head
节点, 我们可以猜测, 异常事件的传播是从head
节点开始的
static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) { ObjectUtil.checkNotNull(cause, "cause"); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { //执行下一个节点的异常方法 next.invokeExceptionCaught(cause); } else { try { executor.execute(new Runnable() { @Override public void run() { next.invokeExceptionCaught(cause); } }); } catch (Throwable t) { //忽略代码 } } }
因为这里是传入的是head
节点, 所以这里的next
指向head
节点。
我们跟到invokeExceptionCaught
方法中, 这里其实是headContext
的父类AbstractChannelHandlerContext
中的方法
private void invokeExceptionCaught(final Throwable cause) { if (invokeHandler()) { try { //当前handler调用exceptionCaught()方法 handler().exceptionCaught(this, cause); } catch (Throwable error) { //代码省略 } } else { fireExceptionCaught(cause); } }
这里又是我们熟悉的逻辑, 调用当前handler
的exceptionCaught
方法, 因为当前handler
是head
, 所以首先会调用headContext
的exceptionCaught
方法
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); }
这里仅仅是继续传播异常事件, 这时候我们发现, 这个写法和我们刚才提到传播异常事件的两种写法的第一种写法一样
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //写法1 ctx.fireChannelRead(cause); //写法2 ctx.pipeline().fireExceptionCaught(cause); }
根据我们之前的学习, 我们知道第一种写法是从当前节点传播, 而第二种写法是从头传播, 并且要求传播事件一定要使用第一种写法, 否则事件到这里会重新从头传播进而引发不可预知错误, 这个结论在异常传播同样适用, 同学们一定要注意这点
我们继续跟fireExceptionCaught
方法, 这里会走到AbstractChannelHandlerContex
类的fireExceptionCaught
方法
public ChannelHandlerContext fireExceptionCaught(final Throwable cause) { //传播异常事件的时候, 直接拿了当前节点的下一个节点 invokeExceptionCaught(next, cause); return this; }
这个时候我们发现, 这里并没有去获取下一个的inbound
节点还是outbound
节点, 而是直接通过next
拿到下一个节点, 这就说明在异常事件传播的过程中是不区分inbound
事件还是outbound
事件的, 都是直接从head
节点按照链表结构往下传播
static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) { ObjectUtil.checkNotNull(cause, "cause"); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeExceptionCaught(cause); } else { try { executor.execute(new Runnable() { @Override public void run() { next.invokeExceptionCaught(cause); } }); } catch (Throwable t) { //代码省略 } } }
这里又是我们熟悉的逻辑, 我们知道invokeExceptionCaught
中执行了next
的exceptionCaught
, 这里的next
, 因为我们是从head
节点开始剖析的, 所以这里很有可能就是用户自定义的handler
, 如果用户没有重写exceptionCaught
方法, 则会交给用户handler
的父类处理
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); }
我们看到这里继续向下传播了异常事件
走到这里我们会知道, 如果我们没有重写exceptionCaught
方法, 异常事件会一直传播到链表的底部, 就是tail
节点
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { onUnhandledInboundException(cause); }
感谢各位的阅读,以上就是“netty中pipeline异常事件怎么处理”的内容了,经过本文的学习后,相信大家对netty中pipeline异常事件怎么处理这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。