写点什么

3 种 Sentinel 自定义异常,你用过几种?

作者:王磊
  • 2023-11-22
    陕西
  • 本文字数:3129 字

    阅读完需:约 10 分钟

3种Sentinel自定义异常,你用过几种?

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;
@Componentpublic 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;

@RestControllerAdvicepublic 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、设计模式、消息队列等模块。

用户头像

王磊

关注

公众号:Java中文社群 2018-08-25 加入

公众号:Java中文社群

评论

发布
暂无评论
3种Sentinel自定义异常,你用过几种?_Java_王磊_InfoQ写作社区