Netty异常传播机制

java 复制代码
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
        throws Exception {
    ctx.fireExceptionCaught(cause);
}

调用ctx.fireExceptionCaught(cause); 来进行异常的向下传播,ctx是ChannelHandlerContext。因为pipeline中的每个节点是以ChannelHandlerContext形式构成的双向链表。

AbstractChannelHandlerContext 是顶级抽象,每个ChannelHandlerContext内部都持有一个指针指向下一个节点和上一个节点。

java 复制代码
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {
    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;
    }

ctx.fireExceptionCaught(cause);这个方法在把异常向下传播。这个方法显然需要定义在抽象类AbstractChannelHandlerContext中。因为每一个节点都有权利来决定是否向下传播,现在这个设计定义在父级抽象类中更合适。

父类中定义了如下代码:

java 复制代码
@Override
public ChannelHandlerContext fireExceptionCaught(final Throwable cause) {
   invokeExceptionCaught(next, cause);
   return this;
}

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) {
           if (logger.isWarnEnabled()) {
               logger.warn("Failed to submit an exceptionCaught() event.", t);
               logger.warn("The exceptionCaught() event that was failed to submit was:", cause);
           }
       }
   }
}

上面的代码看出,实际调用是父级的静态抽象方法:

static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) 这个方法接收两个参数 第一个 就是:下一个要传播的节点next, 第二个就是:传播的异常。显然next的取值来自于双向链表的下一个指针。也就是最上面的volatile AbstractChannelHandlerContext next;

最终都会调用到:每一个节点的invokeExceptionCaught

invokeExceptionCaught也是定义在抽象类中的,并且是private方法。

java 复制代码
private void invokeExceptionCaught(final Throwable cause) {
    if (invokeHandler()) {
        try {
            handler().exceptionCaught(this, cause);
        } catch (Throwable error) {
            if (logger.isDebugEnabled()) {
                logger.debug(
                    "An exception {}" +
                    "was thrown by a user handler's exceptionCaught() " +
                    "method while handling the following exception:",
                    ThrowableUtil.stackTraceToString(error), cause);
            } else if (logger.isWarnEnabled()) {
                logger.warn(
                    "An exception '{}' [enable DEBUG level for full stacktrace] " +
                    "was thrown by a user handler's exceptionCaught() " +
                    "method while handling the following exception:", error, cause);
            }
        }
    } else {
        fireExceptionCaught(cause);
    }
}

整个这个private中的逻辑 invokeHandler()是先检查是否handler被移除了,是否还有效,如果有效就调用handler的handler().exceptionCaught(this, cause)。

权限收口: 将核心执行逻辑设为 private,是为了防止子类破坏"检查-执行-保护"的固定流水线流程。

抽象类处理通用的框架逻辑,不让用户自己私自修改,防止流程上被破坏,只是给用户留了handler中的exceptionCaught用来决定怎么处理异常。

异常的传播,牢牢的掌握在AbstractChannelHandlerContext这个类中。AbstractChannelHandlerContext负责整个流程的契约。防止 "子类破坏了父类的契约"

总结

1.异常从pipeline中的当前节点,一直向后传播,一直到tail节点。不区分inboundHandler还是outboundHandler

2.如果handler不重写 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception

ChannelInboundHandlerAdapter的默认实现就是直接向下传播。

java 复制代码
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
        throws Exception {
    ctx.fireExceptionCaught(cause);
}

3.整个流程都封装在抽象类中了,防止开发者破坏整个pipeline的流转流程

相关推荐
niucloud-admin4 小时前
NIUCLOUD PHP SAAS上门家政使用文档:服务保障
java·开发语言·php
小雉系统5 小时前
BcoreOS实战:用一个升级包,让Rocky Linux跑进Debian服务器
linux·运维·服务器·debian
IT枫斗者枫哥5 小时前
CompletableFuture失败后返回空列表:异常没了,业务含义也变了
java·后端
知识搬运临时工5 小时前
一个模板把 AEM 拖进 Full GC 死亡螺旋:300% CPU、7 GB 堆、310 万行堆栈
java·aws
熊猫钓鱼>_>5 小时前
鸿蒙 PC 三方库适配复盘:libfvad 1.0 —— 把 WebRTC VAD 做成能装进采集链路的静态库
服务器·开发语言·华为·harmonyos·openharmony·适配·鸿蒙pc
用户432935928055 小时前
CoAP还是MQTT:1万只Cat.1水表三个月带宽续航实测
java
知守观5 小时前
多模块架构演进:横向分层 vs 纵向领域,13 个真实项目的切分对照
java·后端·微服务
Java成神之路-5 小时前
Java String 为什么不可变?和 HashMap 到底有什么关系?
java
paopaokaka_luck5 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
Ruiery5 小时前
Linux 6.6内核内存管理深度解析(十七):vmalloc — 内核要“虚拟连续“就够:物理页逐个凑,页表来连
linux·运维·服务器