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

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

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

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

一、概述

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

  • 自定义局部异常
  • 自定义(Sentinel)全局异常
  • 自定义系统异常

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

二、自定义局部异常

自定义局部异常是在使用 @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 方法,并且程序也会报错。

三、自定义全局异常

自定义 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 全局自定义异常,而是程序报错,此时就需要使用系统自定义异常来重新定义异常信息了。

四、自定义系统异常

自定义系统异常需要新建一个异常类,并且使用 @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 种自定义异常同时存在,那么它的执行优先级是:自定义局部异常 > 自定义全局异常 > 自定义系统异常。

责任编辑:姜华 来源: Java中文社群
相关推荐

2011-09-05 12:43:23

Sencha Touc事件

2011-12-16 14:23:51

Java

2009-12-03 10:49:32

PHP自定义异常处理器

2024-10-30 16:39:45

2024-06-12 07:54:05

2009-08-04 09:09:56

Java常见异常

2022-02-14 22:04:50

浏览器Observer监听

2022-03-28 20:57:31

私有属性class属性和方法

2021-06-17 06:52:37

C#自定义异常

2013-05-07 09:39:14

软件定义网络SDNOpenFlow

2021-01-06 05:25:56

项目Springboot应用

2009-08-06 15:26:18

C#异常类型

2021-12-15 23:10:34

JS Debugger 前端开发

2024-01-17 13:58:00

算法C#冒泡排序

2021-10-10 12:29:27

机器人AI人工智能

2015-02-12 15:33:43

微信SDK

2010-02-04 10:33:40

C++异常传递

2009-08-03 16:37:49

C#异常类

2009-08-05 18:01:20

C#自定义异常处理

2015-02-12 15:38:26

微信SDK
点赞
收藏

51CTO技术栈公众号