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

相关推荐
Ciderw5 分钟前
MySQL日志undo log、redo log和binlog详解
数据库·c++·redis·后端·mysql·面试·golang
m0_7482359516 分钟前
SpringBoot:解决前后端请求跨域问题(详细教程)
java·spring boot·后端
LUCIAZZZ31 分钟前
简单说一下什么是RPC
java·网络·网络协议·计算机网络·spring cloud·rpc
嘵奇34 分钟前
最新版IDEA下载安装教程
java·intellij-idea
s_fox_1 小时前
Nginx Embedded Variables 嵌入式变量解析(4)
java·网络·nginx
一个 00 后的码农1 小时前
25旅游管理研究生复试面试问题汇总 旅游管理专业知识问题很全! 旅游管理复试全流程攻略 旅游管理考研复试真题汇总
考研·面试·面试问题·考研复试·旅游管理·复试调剂·面试真题
Jelena157795857921 小时前
使用Java爬虫获取1688 item_get_company 接口的公司档案信息
java·开发语言·爬虫
数据小小爬虫1 小时前
Jsoup解析商品详情时,如何确保数据准确性?
java·爬虫
V+zmm101341 小时前
自驾游拼团小程序的设计与实现(ssm论文源码调试讲解)
java·数据库·微信小程序·小程序·毕业设计
坚定信念,勇往无前2 小时前
springboot单机支持1w并发,需要做哪些优化
java·spring boot·后端