编码技巧——Sentinel的blockHandler与fallback

news2024/10/7 6:45:51

本文介绍Sentinel的blockHandler与fallback的区别,背景是:发生限流时,配置的sentinel的blockhandler没有生效而fallback生效了;排查原因,从而给出Sentinel配置异常降级和限流降级的代码写法;

在查看源码前,查阅了相关的技术帖子(1. Sentinel的blockHandler与fallback的区别 2.Sentinel服务熔断[fallBack/blockHandler]),针对同时配置fallback和blockHandler的场景,其中的结论存在不一致,所以决定自己亲手实践下;

1. 未配置fallback和blockHandler

代码如下,仅配置SentinelResource的value,并去sentinel控制台配置单机限流为1;

限流方法:

    /**
     * 测试sentinel的降级方法
     */
    @SentinelResource(value = "testSentinelFallback")
    public String testSentinelFallback() {
        return "返回成功ok";
    }

测试代码:

    @Override
    public String mock() {
        // 异步调用 模拟并发情况
        for (int i = 0; i < 5; i++) {
            CompletableFuture.runAsync(() -> {
                try {
                    final String result = testSentinelService.testSentinelFallback();
                    log.info("调用返回结果 [result={}]", result);
                } catch (Throwable e) {
                    log.warn("调用抛出异常", e);
                }
            });
        }
        return null;
    }

sentinel后台配置:

执行结果:

调用返回结果 [result=返回成功ok]

调用抛出异常
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

调用抛出异常
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
...

2. 仅配置blockHandler

2.1 配置blockHandler时未带上额外参数BlockException
    @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        return "返回成功ok";
    }

    public String myBlockHandler() {
        return "进入myBlockHandler逻辑";
    }

执行结果:未进入限流降级方法

调用返回结果 [result=返回成功ok]

调用抛出异常
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

调用抛出异常
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
...
2.2 正确的配置blockHandler
    @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        return "返回成功ok";
    }

    public String myBlockHandler(BlockException blockException) {
        return "进入myBlockHandler逻辑";
    }

执行结果:成功进入限流降级方法

调用返回结果 [result=返回成功ok]

调用返回结果 [result=进入myBlockHandler逻辑]

调用返回结果 [result=进入myBlockHandler逻辑]
...
2.3 blockHandler能捕获业务异常
    @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        if (Boolean.TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "返回成功ok";
    }

    public String myBlockHandler(BlockException blockException) {
        return "进入myBlockHandler逻辑";
    }

执行结果:接口被限流时,成功进入限流降级方法;接口出现业务异常时,会抛到外层

调用抛出异常
BusinessException

调用返回结果 [result=进入myBlockHandler逻辑]

调用返回结果 [result=进入myBlockHandler逻辑]
...

3. 仅配置fallback

3.1 配置fallback时未带上额外参数Throwable
    @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        if (Boolean.TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "返回成功ok";
    }

    public String myFallback() {
        return "进入myFallback逻辑";
    }

执行结果:接口被限流时,或者接口出现业务异常时,都会进入fallback降级方法

调用返回结果 [result=进入myFallback逻辑]

调用返回结果 [result=进入myFallback逻辑]

调用返回结果 [result=进入myFallback逻辑]
...

3.2 配置fallback时带上额外参数Throwable

    @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        if (Boolean.TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "返回成功ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof BlockException) {
            return "进入myFallback逻辑 限流异常";
        }
        return "进入myFallback逻辑 业务异常";
    }

执行结果:接口被限流时,或者接口出现业务异常时,都会进入fallback降级方法;且可以通过异常类型区分出限流异常和业务异常

调用返回结果 [result=进入myFallback逻辑 业务异常]

调用返回结果 [result=进入myFallback逻辑 限流异常]
调用返回结果 [result=进入myFallback逻辑 限流异常]
...

4. 同时配置fallback和blockHandler

    @SentinelResource(value = "testSentinelFallback", fallback = "myFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        if (Boolean.TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "返回成功ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof BlockException) {
            return "进入myFallback逻辑 限流异常";
        }
        return "进入myFallback逻辑 业务异常";
    }

    public String myBlockHandler(BlockException blockException) {
        return "进入myBlockHandler逻辑";
    }

执行结果:若blockHandler和fallback都进行了配置,在未触发限流进入到方法逻辑抛出业务异常时,会进入fallback方法在触发限流时,进入不到方法逻辑,直接抛出BlockException进入blockHandler方法

5. 结论

5.1 异常捕获逻辑

1. blockHandler

  • blockHandler仅处理限流异常;
  • 使用blockHandler时,方法签名参数与原方法一致,且必须要在参数的最后位置补充BlockException参数;
  • 若未补充BlockException参数则不生效;

2. fallback

  • fallback可以处理所有类型异常,包括限流异常和业务异常;
  • 使用fallback时,方法签名参数可以与原方法完全一致,或者也接受在参数的最后位置补充Throwable参数;
  • 通过对Throwable参数的类型区分是限流异常还是其他异常;
  • 当同时生效blockHandler和fallback时,限流异常会优先被blockHandler处理而不再进入fallback逻辑;
5.2 合理的代码写法

(1)同时配置生效blockHandler和fallback分别处理限流异常和业务异常

    @SentinelResource(value = "testSentinelFallback", fallback = "myFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        // ...
        return "返回成功ok";
    }

    public String myFallback(Throwable throwable) {
        return "进入myFallback逻辑 业务异常";
    }

    public String myBlockHandler(BlockException blockException) {
        return "进入myBlockHandler逻辑";
    }

(2)仅配置fallback并通过Throwable类型区分限流异常和业务异常

    @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        // ...
        return "返回成功ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof com.alibaba.csp.sentinel.slots.block.flow.FlowException) {
            final FlowRule rule = ((FlowException) throwable).getRule();
            final double count = rule.getCount();
            final String resource = rule.getResource();
            // 打印限流规则信息
            log.warn("testSentinelFallback触发限流降级 [sentinelResource={} QpsLimit={}]]", resource, count);
            return null;
        } else {
            log.warn("testSentinelFallback触发异常降级 抛出异常", throwable);
            throw new RuntimeException("testSentinelFallback业务异常");
        }
    }
5.3 注解参数释义及注意事项

1. @SentinelResource注解参数说明

属性默认值说明
blockHandler

用于在抛出限流/熔断/系统保护等异常的降级处理逻辑,blockHandler 针对BlockException类型的异常,优先级比fallback高

blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;

blockHandler 函数默认需要和原方法在同一个类中;

blockHandlerClass若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析;
defaultFallback默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法);

默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理;

若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效

entryTypeEntryType.OUT资源调用的流量类型,是入口流量(EntryType.IN)还是出口流量(EntryType.OUT),注意系统保护规则只对 IN 生效
exceptionsToIgnore用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出; 优先级高于exceptionsToTrace
exceptionsToTraceThrowable.class用于指定哪些异常不被排除掉;如果属于该类型,则会计入异常统计中,也会进入 fallback 逻辑中,不会原样抛出;不建议修改默认值;
fallback

用于在抛出异常的时候提供 fallback 处理逻辑;fallback 针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)

方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常(注意和blockHandler添加的BlockException不一样)

fallbackClass类似blockHandlerClass参数
resourceType资源类型,默认0
value资源名称,必需项

2. 区分限流异常和熔断异常

限流状态会抛异常:FlowException(继承BlockException)

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.csp.sentinel.slots.block.flow.FlowException] with root cause
 
com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

熔断状态会抛异常:DegradeException(继承BlockException)

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.csp.sentinel.slots.block.degrade.DegradeException] with root cause
 
com.alibaba.csp.sentinel.slots.block.degrade.DegradeException: null

 

参考:annotation-support | Sentinel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/851497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“尚硅谷”Mysql数据库DDL补充

数据类型 原则&#xff1a;所选择的类型越简单越好&#xff0c;能保存数值的类型越小越好&#xff0c;节省空间。 数值型 整型 特点 1、如果不设置无符号还是有符号&#xff0c;默认是有符号&#xff0c;如果(ˇ?ˇ) 想设置无符号我&#xff0c;需要添加unsigned关键字。 2…

Netty框架自带类DefaultEventExecutorGroup的作用,用来做业务的并发

一、DefaultEventExecutorGroup的用途 DefaultEventExecutorGroup 是 Netty 框架中的一个类&#xff0c;用于管理和调度事件处理器&#xff08;EventExecutor&#xff09;的组。在 Netty 中&#xff0c;事件处理是通过多线程来完成的&#xff0c;EventExecutor 是处理事件的基…

【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)

你可以使用 Jersey 和 CXF 这些来写一个 Rest 或 SOAP 服务的java客服端。 你也可以直接使用 Apache HttpClient 来实现。但是 Feign 的目的是尽量的减少资源和代码来实现和 HTTP API 的连接。 *通过自定义的编码解码器以及错误处理&#xff0c;你可以编写任何基于文本的 HTT…

day22-513.找树左下角的值

513.找树左下角的值 力扣题目链接(opens new window) 给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例 1: 示例 2: 思路 迭代 迭代的思路最简单&#xff0c;只需层序记录每一层的第一个结点即可&#xff0c;代码如下&#xff1a; class Solution { publi…

HR专家:未来零代码开发将成求职热门,你准备好了吗?

一名五十五岁的农民&#xff0c;毫无程式设计经验&#xff0c;靠著自己自学零码开发&#xff0c;竟为他的家乡建起了六个数位资讯系统&#xff0c;其中一个更是带动了乡村“厕所革命”。阿里云说&#xff0c;“也许10-15年以前&#xff0c;公司的招聘会要求员工能够使用 WORD, …

【沁恒蓝牙mesh】CH58x USB功能开发记录(三)

本博文主要记录 &#xff0c;【沁恒蓝牙mesh】CH58x USB功能开发记录&#xff08;三&#xff09;&#xff0c;数据收发基于寄存器级别解释 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的小菜鸟。&#x1f60e;&#x1f4dd; 个人主页&#xf…

阿秀思考了很久,最后还是决定啦!

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 小伙伴们大家好&#xff0c;我是阿秀。 经常看我文章的应该知道今年我过得很是颠簸&#xff0c;从今年二月份到现在我经历了很多事。 从《关于我组长跑路我被顶上来了这件事》、《天啦撸&#x…

再谈领导力和组织能力

&#xff08;1&#xff09;一个笑话 说的是一个人去买鸟&#xff1a; 问左边的鸟多少钱&#xff0c;卖鸟的人说十块钱。问为啥这么贵&#xff1f;卖鸟的人说它长的好看。 问右边的鸟多少钱&#xff0c;卖鸟的人说二十块钱。问为啥这么贵&#xff1f;卖鸟的人说它会学人讲话。 问…

JVM如何调优

一、JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范&#xff0c;JVM将内存划分为&#xff1a; New&#xff08;年轻代&#xff09; Tenured&#xff08;年老代&#xff09; 永久代&#xff08;Perm&#xff09; 其中New和Tenured属于堆内存&#xff0c;堆内存会从JVM启动参…

面试热题(LRU缓存)

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0c;否则返回 -1 …

实现5*5正方形网格x轴和y轴显示对应数值组件封装

实现5*5正方形网格x轴和y轴显示对应数值组件封装 需求&#xff1a;按5*5的正方形网格&#xff0c;根据目标数据的x和y轴值显示对应的文字&#xff0c;实现效果图如下&#xff1a;&#xff08;当前目标数值&#xff1a;x2&#xff0c;y2&#xff09; 代码如下&#xff1a; <…

华为运动健康,十年创新天地宽

我听一位朋友讲过这样一个故事。某天早上&#xff0c;急诊科的医生迎来了一位患者&#xff0c;患者进来后直接说&#xff1a;“大夫&#xff0c;我房颤了。” 这位医生非常诧异&#xff0c;因为心脏房颤确实非常危急&#xff0c;但很多时候并没有明显的生理体征&#xff0c;患者…

源码下载安装nginx

文章目录 下载安装nginx1、准备工作2、下载源码包3、安装4、启动nginx5、网页访问首页 下载安装nginx 1、准备工作 Nginx的安装需要提前安装Linux的几个库&#xff0c;避免编译和配置错误 安装gcc编译器 yum install gcc -y安装openssl库 yum install openssl openssl-dev…

CSS 盒模型是什么?它包含哪些属性?标准盒模型/怪异盒模型

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 盒模型⭐ 标准盒模型⭐ 怪异盒模型⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感…

SQL必知必会读书笔记

文章目录 **不同的DB语法格式不尽相同**第一课 了解SQL术语 第二课 检索数据语法格式检索列检索唯一不同值限制结果&#xff08;数量&#xff09; 第三课 排序检索数据使用说明 第四课 过滤数据WHERE子句操作符 第五课 高级数据过滤1、组合WHERE子句2、IN操作符3、NOT操作符 第…

kube-prometheus 使用 blackbox-exporter 进行icmp 监控

安装kube-prometheus 后默认在monitoring namespace中有创建 blackbox-exporter deployment。但默认没有icmp的module配置&#xff0c;无法执行ping探测。因为即使有icmp module&#xff0c;默认配置也是无法执行ping探测的&#xff08;这篇文章要解决的就是这个问题&#xff0…

尚品汇总结十:秒杀模块(面试专用)

1、需求分析 所谓“秒杀”&#xff0c;就是商家发布一些超低价格的商品&#xff0c;所有买家在同一时间网上抢购的一种销售方式。通俗一点讲就是商家为促销等目的组织的网上限时抢购活动。由于商品价格低廉&#xff0c;往往一上架就被抢购一空&#xff0c;有时只用一秒钟。 秒…

数据结构----结构--线性结构--链式存储--链表

数据结构----结构–线性结构–链式存储–链表 1.链表的特点 空间可以不连续&#xff0c;长度不固定&#xff0c;相对于数组灵活自由 搜索&#xff1a; 时间复杂度O(n) 增删: 头增头删时间复杂度O(1) 其他时间复杂度为O(n) 扩展&#xff1a;单向循环链表的特性 从任意节…

【数据分享】2023年我国省市县三级的上市公司数量(Excel/Shp格式)

企业是经济活动的参与主体&#xff0c;一个城市的企业数量决定了这个城市的经济发展水平&#xff01;在众多公司企业中&#xff0c;上市公司堪称明珠&#xff0c;上市公司通常经济规模大、影响力强、员工多。哪个城市的上市公司更多&#xff0c;往往这个城市的经济实力越强&…

Flowable-结束事件-错误结束事件

目录 定义图形标记XML内容界面操作使用示例视频教程 定义 错误结束事件会在流程到达错误结束事件的时候抛出错误&#xff0c;并结束当前的流程分支。异常结束 事件是有抛出结果的&#xff0c;它需要定义抛出错误码&#xff0c;如果找到了异常开始事件错误码&#xff0c;就会触…