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

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

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

0.概述

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

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

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

1.自定义局部异常

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

java 复制代码
@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 方法,如下代码所示:

java 复制代码
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 注解来完成全局系统异常的获取和定义,具体实现代码如下:

java 复制代码
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、设计模式、消息队列等模块。

相关推荐
_oP_i16 分钟前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx19 分钟前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康1 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘2 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意2 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
刘大辉在路上2 小时前
突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
git·后端·gitlab·版本管理·源代码管理
FF在路上3 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人3 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.3 小时前
Mybatis-Plus
java·开发语言