持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
自定义Sentinel全局异常处理类
Sentinel有两种方式实现自定义异常处理逻辑:
- 添加@ResourceSentinel注解(细粒度,每一个接口对应一个异常处理类) ;
- 实现BlockRequestHandler接口(粗粒度,但简单省事,并且为全局处理) 。
一般对于复杂的系统要使用简单的方法实现,因此推荐次采用第二种全局异常处理类的方式。特殊的接口可以使用 @ResourceSentinel进行错误逻辑处理,但默认全局还是使用一种异常处理器比较好。
1. 添加Sentinel全局异常配置类
但每一个接口声明@ResourceSentinel 注解太麻烦了,并且Sentinel限流后返回的提示不太友好了,我们可以自定义Sentinel的全局异常处理类:
package com.deepinsea.common.handler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
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 com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Created by deepinsea on 2022/7/7.
* sentinel限流、降级和熔断全局异常处理类
*/
@Configuration
public class SentinelExceptionHandler implements BlockRequestHandler {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
JSONObject resultObj = new JSONObject();
if (throwable instanceof FlowException) {
resultObj.put("code", 100);
resultObj.put("msg", "接口限流");
}
if (throwable instanceof DegradeException) {
resultObj.put("code", 101);
resultObj.put("msg", "服务降级");
}
if (throwable instanceof ParamFlowException) {
resultObj.put("code", 102);
resultObj.put("msg", "热点参数限流");
}
if (throwable instanceof SystemBlockException) {
resultObj.put("code", 103);
resultObj.put("msg", "触发系统保护规则");
}
if (throwable instanceof AuthorityException) {
resultObj.put("code", 104);
resultObj.put("msg", "授权规则不通过");
}
return ServerResponse.status(HttpStatus.BAD_GATEWAY)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(resultObj));
}
}
注意:使用BlockException统一异常处理时,不能添加@SentinelResource
在SentinelController中,我们使用之前创建过的limit限流接口,用于测试自定义异常处理限流后的逻辑:
package com.deepinsea.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by deepinsea on 2022/7/4.
*/
@RestController
@RequestMapping("/sentinel")
public class SentinelController {
@GetMapping("/hello")
public String hello() {
return "hi, this is service-sentinel!";
}
@PostMapping("/test")
public String test() {
return "hi, this is service-sentinel[POST]!";
}
//测试流控规则
@GetMapping("/limit")
public String limit() { //路由id限流、自定义异常处理
return "hi, this is service-sentinel-limit test!";
}
//测试降级规则
@GetMapping("/degrade")
public String degrade(){
return "hi, this is service-sentinel-degrade test!";
}
//熔断测试
@GetMapping("/fusing")
public String fusing(){
return "hi, this is service-sentinel-fusing test!";
}
}
重启gateway,这时我们发现Sentinel配置会全部重置,是因为我们没有配置Sentinel持久化的问题。
注意:sentinel的配置默认不会保存,重启gateway服务后sentinel规则配置会丢失,因此需要持久化保存配置。
因为我们在重启服务后配置重置了,因此需要重新添加限流规则:
2. 测试全局异常处理
我们使用curl命令,测试sentinel限流、降级和熔断全局异常处理类的处理异常效果:
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
hi, this is service-sentinel-limit test!
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
{"msg":"热点参数限流","code":102}
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
hi, this is service-sentinel-limit test!
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
{"msg":"热点参数限流","code":102}
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
hi, this is service-sentinel-limit test!
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
{"msg":"热点参数限流","code":102}
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
hi, this is service-sentinel-limit test!
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
{"msg":"热点参数限流","code":102}
C:\Users\deepinsea>curl http://localhost:9080/service-sentinel/sentinel/limit
{"msg":"热点参数限流","code":102}
可以看到,我们自定义的异常处理类生效了,限流规则触发后返回了自定义的错误信息。
并且,我们通过单位时间内正常请求和拒绝请求的比例也可以验证,是在触发Sentinel限流规则后然后自定义异常处理的。
自定义Sentinel全局异常类功能测试成功!
3. 使用yml文件配置默认全局异常处理
在yml配置中添加如下配置
datasource:
# 限流返回的响应
scg:
order: -2147483648 # 过滤器顺序,默认为 -2147483648 最高优先级
# fallback模式,目前有三种:response、redirect、空(可以实现对 fallback 的自定义处理逻辑)
fallback:
# 第一种:response返回文字提示信息
mode: response
response-status: 503 #响应状态码(默认为429)
response-body: 访问过于频繁,请稍后重试! #This request is blocked by service-sentinel.
content-type: application/json #内容类型(默认为 application/json)
# # 第二种:redirect重定向跳转uri
# mode: redirect
# redirect: http://www.baidu.com #跳转的URL(需对应redirect模式)
4. 测试全局异常处理
重启项目,然后在网页访问 http://localhost:9080/service-sentinel/sentinel/hello :
可以看到,第一次访问成功,返回信息和状态码都是正常的。
但是后面快速连续的访问后,触发了限流规则,所以sentinel返回的信息也变成了错误的提示信息并且HTTP状态码也变为了503。
同理,redirect模式触发限流规则后,会直接重定向到配置的url地址
测试成功,基于yml的全局配置成功生效!
欢迎点赞,谢谢大佬了ヾ(◍°∇°◍)ノ゙