• 关于异常的方方面面


    一. 业务中异常的处理

    在开发业务代码的时候,异常是主流程之外的非正常流程,是主流程进行不下去的标志,是需要进行补偿处理的。
    这里表述的异常,包括非正常的 http 状态码,和异构的外部系统返回的业务异常状态码。
    针对这些异常, 站在完善流程的视角上,可以进行异常归类。

    第一类是网络异常:针对这类异常,我们可以进行重试,但是要注意三方系统是否幂等,幂等接口可以重试。不幂等的接口,避免重复请求,造成资损,可以再请求一遍数据前校验一下状态。

    第二类是业务异常:在网络顺畅的情况下,可能由于三方原因,有些流程进行不下去,这时,三方会返回状态码,标志是否请求成功,针对三方业务异常状态码,还可以进行一个归类。

    1. 需要人工介入的异常,这种异常无法判断是否成功,需要人工介入。
    2. 可以重试的异常,这种异常可能由于三方系统瞬时不可用,所以短时间无法成功,可以择机重试。
    3. 和三方系统失去信任异常。和三方系统进行交互式,可能需要账号、签名等校验,如果是校验失败,可以直接放弃并且此后不修改的话无须再请求三方系统,因为和三方系统之间交互的信任失去了。

    基于上述分类,我们完善流程时,就有了多种条件,可以针对性的进行三方下架、重试、标记异常请求等。

    如果是有多个相同能力的三方系统,还可以对三方系统的稳定性等进行打标,针对指标进行运营。

    在写流程代码时,可以使用 微内核加插件化,平等对待第三方的设计思想。或者更进行一步,使用状态机进行流程编排,把固定的内核流程做成可以插拨的形式。

    二. 虚拟机对异常的处理

    首先 java.lang.Throwable 就是一个java类,没有什么特别的地方,之所以异常能够跳出链式调用流程,直接抛出,还是因为虚拟机的原因,因为 throw 关键字。

    public static void main(String[] args) {
      try {
        mayThrowException();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    // 对应的 Java 字节码
    public static void main(java.lang.String[]);
      Code:
        0: invokestatic mayThrowException:()V
        3: goto 11
        6: astore_1
        7: aload_1
        8: invokevirtual java.lang.Exception.printStackTrace
       11: return
      Exception table:
        from  to target type
          0   3   6  Class java/lang/Exception  // 异常表条目
    12345678910111213141516171819
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    附上一个编译后的带有 try catch 的字节码表,可以看出,编译后的字节码,每个方法都附带一个异常表,并且由 from、to、target 指针构成,这些指针的值,是 字节码索引。

    from to 代表了监控的范围,targe 代表异常处理器的起始位置。

    如果遍历完所有的一场表条目,仍未找到正确的异常处理器,那么会弹出当前方法的栈帧,并且在调用者中重复此操作。

    虚拟机除了找到正常的异常处理流程外,还会构造生成该异常的栈轨迹。该操会逐一访问当前线程的 java 栈帧,并记录调试信息,包括 栈帧指向方法的名字、方法所在的类名、文件名、以及在代码中的第几行触发了该异常。所以异常实例的构建十分昂贵。

    虚拟机异常其实就是同构系统对异常的处理。

    三. rpc 中异常的处理

    rpc 的基本流程 序列化-》网络传输-》反序列化。这是基本的流程,如果出现异常了,那么经过网络传输,然后经过反序列化,还会生成原来的对象吗?

    Throwable 实现了 Serializable 接口,表明异常也是可以被序列化的。

    不同的 rpc 框架有不同的处理方式。

    dubbo 就是 com.alibaba.dubbo.rpc.filter.ExceptionFilter,这个类中对不同的异常有不同的处理方式,受检异常直接抛出,其他异常,原则上如果调用方是可以进行反序列化为 java对象 的就直接抛出,如果不能,则使用 RuntimeException 包裹后再抛出。

    feign 默认是通过 http 协议进行网络传输。所以如果被调用方抛异常了,返回给调用方的,就不是异常对象,而是异常信息。springboot web 项目,异常时返回的信息就是 status、timestamp、message、error、path,仅通过这些信息,是没法反序列化成 java 对象的。所以 feign 的异常处理,是获取 message 信息,然后包装成 FeignException 返回出去。

    因为 dubbo 框架抛出的异常信息中,有类信息,所以存在反序列化的可能。一般网络传输框架为了多语言通用,基本没有 java 的类信息,所以反序列化不回来。

  • 相关阅读:
    SpringSecurity入门
    扫雷小游戏(简单详细)(内附完整代码)
    3. 安装lombok & maven镜像设置
    【JavaWeb】之文件上传与下载
    高防IP:构建网络安全的重要防线
    Spelling sentences
    当下IT测试技术员的求职困境
    .Net 472&6.0 Razor编译时的小差异
    JavaScript与HTML交互
    DataOps 不是工具,而是帮助企业实现数据价值的最佳实践
  • 原文地址:https://blog.csdn.net/qq_37804737/article/details/127932887