Netty源码分析第四章: pipelinehtml
第6节: 传播异常事件ide
讲完了inbound事件和outbound事件的传输流程, 这一小节剖析异常事件的传输流程oop
首先咱们看一个最最简单的异常处理的场景:源码分析
@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方法中被调用this
咱们跟到invokeChannelRead这个方法:spa
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方法, 并传入异常对象, 也就是咱们示例中抛出的异常code
咱们跟到fireChannelRead方法中:htm
private void notifyHandlerException(Throwable cause) { //代码省略
invokeExceptionCaught(cause); }
再继续跟到invokeExceptionCaught方法中:
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事件有什么区别呢?咱们先以第二种写法为例, 剖析异常事件传输的整个流程
跟到DefualtChannelPipeline的fireExceptionCaught方法中:
public final ChannelPipeline fireExceptionCaught(Throwable cause) { AbstractChannelHandlerContext.invokeExceptionCaught(head, cause); return this; }
咱们看到invokeExceptionCaught传入了head节点, 咱们能够猜想, 异常事件的传播是从head节点开始的
跟进invokeExceptionCaught方法:
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方法
跟进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节点按照链表结构往下传播,
跟到invokeExceptionCaught方法中:
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的父类处理
咱们以ChannelInboundHandlerAdapter为例看它的该方法实现:
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); }
咱们看到这里继续向下传播了异常事件
走到这里咱们会知道, 若是咱们没有重写exceptionCaught方法, 异常事件会一直传播到链表的底部, 就是tail节点
咱们跟到TailConext的exceptionCaught方法:
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { onUnhandledInboundException(cause); }
咱们看到最终这里释放了异常对象
以上就是有关异常事件的传播