Sentinel自定义异常的三种实现方式
作者:Java中文社群
Spring Cloud Alibaba Sentinel 是目前主流并开源的流量控制和系统保护组件,它提供了强大的限流、熔断、热点限流、授权限流和系统保护及监控等功能。使用它可以轻松的保护我们微服务,在高并发环境下的正常运行。
那么,当程序触发了限流和熔断规则时,如何自定义返回的异常信息呢?这是我们接下来要解决的问题。
0.概述
Spring Cloud Alibaba Sentinel 有以下 3 种自定义异常的实现方式:
- 自定义局部异常
- 自定义(Sentinel)全局异常
- 自定义系统异常
以上这 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 个问题:
- 自定义的 blockHandler 方法的返回值,必须要和原方法(使用 @SentinelResource 注解修饰的方法)的返回值保持一致。
- 自定义的 blockHandler 方法的参数必须和原方法参数保持一致。
- 自定义的 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 种自定义异常同时存在,那么它的执行优先级是:自定义局部异常 > 自定义全局异常 > 自定义系统异常。
以上就是Sentinel自定义异常的三种实现方式的详细内容,更多关于Sentinel自定义异常的资料请关注脚本之家其它相关文章!