Spring Cloud Alibaba Sentinel 是目前主流并开源的流量控制和系统保护组件,它提供了强大的限流、熔断、热点限流、授权限流和系统保护及监控等功能。使用它可以轻松的保护我们微服务,在高并发环境下的正常运行。

那么,当程序触发了限流和熔断规则时,如何自定义返回的异常信息呢?这是我们接下来要解决的问题。

0.概述

Spring Cloud Alibaba Sentinel 有以下 3 种自定义异常的实现方式:

  1. 自定义局部异常
  2. 自定义(Sentinel)全局异常
  3. 自定义系统异常

以上这 3 种实现方式,都可以重新定义 Sentinel 的异常返回信息,它们的具体实现如下。

1.自定义局部异常

自定义局部异常是在使用 @SentinelResource 注解时,直接定义的 blockHandler 异常方法,如下代码所示:

@SentinelResource(value = "/user/getuser",
blockHandler = "myBlockHandler")
@RequestMapping("getuser")
public String getUser(Integer uid) {
return "User:" + uid;
} /**
* 定义限流/熔断等异常
*/
public String myBlockHandler(Integer uid, BlockException e) {
String msg = "未知异常";
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
}
return msg;
}

注意事项

在定义 blockHandler 方法时,需要注意以下 3 个问题:

  1. 自定义的 blockHandler 方法的返回值,必须要和原方法(使用 @SentinelResource 注解修饰的方法)的返回值保持一致。
  2. 自定义的 blockHandler 方法的参数必须和原方法参数保持一致。
  3. 自定义的 blockHandler 方法的方法参数中必须包含 BlockException 参数。

如果不满足以上事项中的任何一项,那么就不能正常匹配到自定义的 blockHandler 方法,并且程序也会报错。

2.自定义全局异常

自定义 Sentinel 全局异常需要实现 BlockExceptionHandler 类,并重写 handle 方法,如下代码所示:

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; @Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = HttpStatus.TOO_MANY_REQUESTS.value();
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = HttpStatus.UNAUTHORIZED.value();
}
response.setContentType("application/json;charset=utf-8");
response.setStatus(status);
response.getWriter().println("{\"msg\": " + msg + ", \"code\": " + status + "}");
}
}

自定义 Sentinel 全局异常是在执行 Sentinel 控制台设置的限流和熔断异常时,执行的全局自定义异常方法。

但是,如果是程序中出现的 Sentinel 报错信息,例如使用热点限流时,因为要配合使用 @SentinelResource 注解时,此时只自定义了 value 属性,未定义局部 blockHandler 方法,此时系统就会报错,但这个时候并不会执行 Sentinel 全局自定义异常,而是程序报错,此时就需要使用系统自定义异常来重新定义异常信息了。

3.自定义系统异常

自定义系统异常需要新建一个异常类,并且使用 @RestControllerAdvice 注解修饰此类,并配合 @ExceptionHandler 注解来完成全局系统异常的获取和定义,具体实现代码如下:

import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap;
import java.util.Map; @RestControllerAdvice
public class CustomExceptionHandler { /**
* 限流全局异常
*/
@ExceptionHandler(FlowException.class)
public Map handlerFlowException(){
return new HashMap(){{
put("code", HttpStatus.TOO_MANY_REQUESTS.value());
put("msg", "被限流");
}};
} /**
* 熔断全局异常
*/
@ExceptionHandler(DegradeException.class)
public Map handlerDegradeException(){
return new HashMap(){{
put("code", HttpStatus.TOO_MANY_REQUESTS.value());
put("msg", "被熔断");
}};
} /**
* 热点限流异常
*/
@ExceptionHandler(ParamFlowException.class)
public Map handlerparamFlowException(){
return new HashMap(){{
put("code", HttpStatus.TOO_MANY_REQUESTS.value());
put("msg", "热点限流");
}};
} /**
* Sentinel 权限拦截全局异常
*/
@ExceptionHandler(AuthorityException.class)
@ResponseBody
public Map handlerAuthorityException(){
return new HashMap(){{
put("code", HttpStatus.UNAUTHORIZED.value());
put("msg", "暂无权限");
}};
}
}

此时,只要是系统中出现的 Sentinel 报错信息,都会被此方法所捕获,并通过自定义的代码完成自定义异常信息的返回。

小结

Sentinel 有 3 种自定义异常的实现:自定义局部异常、自定义(Sentinel)全局异常、自定义系统异常。自定义局部异常作用范围比较小,需要给每个资源单独设置才行;而自定义全局异常作用范围比较大,但如果是程序报错,也不会执行其方法,所以需要配合系统异常同时来完成自定义异常的返回。

PS:如果这 3 种自定义异常同时存在,那么它的执行优先级是:自定义局部异常 > 自定义全局异常 > 自定义系统异常。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

3种Sentinel自定义异常,你用过几种?的更多相关文章

  1. 几种破解MySQL root密码的几种方法:

    几种破解MySQL root密码的几种方法: 方法一 使用phpmyadmin,这是最简单的了,修改mysql库的user表,不过别忘了使用PASSWord函数. 方法二 使用mysqladmin,这 ...

  2. 运算符关键字。数据区别大小写。日期范围。判空的两种写法。NOT IN的两种写法。IN范围可含NULL,但NOT IN值范围不能含NULL。

    比较:>,<,=,>=,<=,<>(!=) 逻辑:AND,OR,NOT 范围:BETWEEN...AND... 范围:IN,NOT IN 判空:IS NULL, I ...

  3. LVS(Linus Virtual Server):三种负载均衡方式比较+另三种负载均衡方式

    还有个姊妹篇也可以参考这个文章:六大Web负载均衡原理与实现 什么是LVS (Linux Virtual Server)?   首先简单介绍一下LVS (Linux Virtual Server)到底 ...

  4. Java反射获取class对象的三种方式,反射创建对象的两种方式

    Java反射获取class对象的三种方式,反射创建对象的两种方式 1.获取Class对象 在 Java API 中,提供了获取 Class 类对象的三种方法: 第一种,使用 Class.forName ...

  5. FMX有两种消息处理的实现方式,一种是用TMessageManager来实现自定义的消息,另外一种象TEdit中的实现,直接声明消息方法(firemonkey messaging)

    看FMX代码,发现有两种消息处理的实现方式,一种是用TMessageManager来实现自定义的消息,另外一种象TEdit中的实现,直接声明消息方法.   早前,看过文章说TMessageManage ...

  6. 【Java面试】Java有几种文件拷贝方式,哪一种效率最高?

    "Java有几种文件拷贝方式,哪一种效率最高?" 这个问题是京东一面的时候,针对4年经验的同学的一个面试题. 大家好,我是Mic,一个工作了14年的Java程序员. 关于这个问题的 ...

  7. 入口点函数的19种消息,AcRxArxApp只处理16种。

    AcRx::AppMsgCode一共有19种消息. 但由IMPLEMENT_ARX_ENTRYPOINT宏实现的App类,只处理了16种消息. 缺: kSuspendMsg = 16,    kIni ...

  8. Delphi一共封装(超类化)了8种Windows基础控件和17种复杂控件

    超类化源码: procedure TWinControl.CreateSubClass(var Params: TCreateParams; ControlClassName: PChar); con ...

  9. java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))

    Java提高篇--对象克隆(复制)(转自:http://www.cnblogs.com/Qian123/p/5710533.html#_label0)   阅读目录 为什么要克隆? 如何实现克隆 浅克 ...

  10. POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)

    一.使用poi解析excel文档 注:全部采用poi接口进行解析,不需要区分xls.xlsx格式,不需要判断文档类型. poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办? ...

随机推荐

  1. 开源.NetCore通用工具库Xmtool使用连载 - XML操作篇

    [Github源码] <上一篇> 介绍了Xmtool工具库中的发送短信类库,今天我们继续为大家介绍其中的XML操作类库. XML操作是软件开发过程中经常会遇到的情况:包括XML内容的遍历解 ...

  2. 基于Prometheus搭建监控平台

    目录 前言 配置server单节点 prometheus.service 配置node节点 配置mysql监控 在数据库中添加exporter账户 修改mysql_exporter的配置 添加serv ...

  3. pandas读取mysql并导出为excel

    前言 业务需要从数据库导出数据为excel,并设置成自动化.这里用pandas写的数据导入导出,还算方便.配合crontab + shell脚本使用,每天晚上自动生成excel,然后cp到指定目录.s ...

  4. 《流畅的python》— 列表推导与生成器表达式

    列表推导是构建列表(list)的快捷方式,而生成器表达式则可以用来创建其他任何类型的序列.如果你的代码里并不经常使用它们,那么很可能你错过了许多写出可读性更好且更高效的代码的机会. 很多Python ...

  5. .NET API 中的 FromRoute、FromQuery、FromBody 用法

    原文链接:https://www.cnblogs.com/ysmc/p/17663663.html 最近技术交流群里,还有不少小伙伴不知道 FromRoute.FromQuery.FromBody 这 ...

  6. OpenLDAP 自助修改密码系统

    一.创建docker-compose文件 root@xx:~# mkdir self-service-password root@xx:~# cd self-service-password root ...

  7. css面试题一

    1.继承 css的继承:就是给父级设置一些属性,子级继承了父级的该属性,这就是我们css中的继承.官方的解释,继承是一种规则,它允许样式不仅应用于特定的html标签元素,而且应用于其后代元素. a.有 ...

  8. SICTF-2023 #Round2-WP-Crypto | Misc

    Crypto 一.[签到]古典大杂烩 附件信息: 很明显可以看出来是base100,密码工具箱一把梭: SICTF{fe853b49-8730-462e-86f5-fc8e9789f077} 二.Ra ...

  9. ES13 中11个令人惊叹的 JavaScript 新特性

    前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...

  10. KRPano JS 场景编辑器源码

    KRPano JS编辑器,可以运行在Node环境中. 源码地址:https://github.com/xxweimei/krpano-editor-js 或者下载zip包:http://pan.bai ...