注:本文章全由自己想法尝试和记录,若有不对或者考虑不完全的情况是十分正常的,有意见请留言,无任何恶意引导
Sentinel中各种流控,熔断,授权当被拦截的时候,都是统一返回一个异常
Blocked by Sentinel (flow limiting)
(这是只设置流控的情况下)
(这是只设置了授权规则的情况下)
当然 这个返回结果自然对用户来说是体验不好的 因为用户不懂得到底出了什么问题 他的一切返回值都是Blocked by Sentinel (flow limiting) ------>被Sentinel限流拦截 不仅不是返回正确的限制原因,也不符合我们的返回结果格式的统一
所以 为了解决这个问题 Sentinel中存在一个让我们自定义异常返回的方式:
实现BlocakExceptionHandler接口即可 接口中有个方法
void handle(HttpServletRequest request,HttpSwervletResponse response,BlockException e) throws Exception;
通过实现该方法 可以接收所有的来自Sentinel的所有异常BlockException e 然后我们可以通过对该异常进行判断 判断是限流还是降级还是授权拦截
然后我们通过对方法参数 HttpServletResponse response对返回值进行自定义设置
可以看到,该方法的实质可以理解为捕获拦截了BlockException 然后再对返回值进行设置
那么 该异常的捕获逻辑 能通过Spring提供的全局异常处理器来捕获和处理吗?
为了检验 我们来测试测试:
首先 准备一个简单的微服务项目逻辑,我们先使用Sentinel提供的自定义异常结果的方式:
@Component // 注册为Bean 让Spring管理
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = 429;
// 通过判断异常的类型 来判断到底是限流还是熔断降级还是授权限制
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = 401;
}
response.setContentType("application/json;charset=utf-8");
response.setStatus(status);
response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}Sentinel自定义异常");
}
}
重启服务 然后我们给/order/{orderId}依次单独添加限流和权限规则,再次访问查看页面返回
可以发现,均被拦截,而且返回信息很好,是我们的自定义形式(上面为限流 下面为授权)
然后我们再来看看使用Spring提供的全局异常处理器进行捕获处理
先将刚刚定义的Sentinel的自定义异常类的@Component注解去掉 使其不会被注册从而不生效
然后我们定义Spring的全局异常处理器:定义方法如下
/*
全局异常处理器的定义:
定义一个类 并且带有@RestControllerAdvice注解
@ExceptionHandler注解指定要捕获的异常的类型
*/
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(Exception.class)
public String globalException(Exception 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 = "没有权限访问";
}
msg+=" 全局异常处理器判断得到";
return msg;
}
}
然后我们重启动服务 再次分别设置限流和权限规则 取访问查看异常返回
却发现 无论是限流还是授权 成功被拦截了 但是没有按照哦我们想要的进行返回
然后我们对程序设置断点 调式的方式再次运行 发现该异常更不没有被捕获到 断点处根本没有走到
但是我们手动抛一个异常 确实被捕获到了 那么说明我们的全局异常处理器在定义和配置上是没问题的 而且这个返回是要大于压制Sentinel的默认异常返回的
那么现在还有一种可能的情况 就是BlockException类并不是Exception的子类 不是基于Exception父类进行定义的。因为我们的全局异常处理器是捕获Exception及其子类 ,但现在没有被捕获,所以我们有理由猜测这一种情况
要验证这个想法,我们对全局异常处理器进行一下修改即可 即 将捕获的异常类型指定为BlockException就可以的
最后 我们再次尝试 得到的结果还是Sentinel的通用异常返回
而且断点处依旧没有任何的反应
所以 最终我们的到的结论是-----Sentinel的异常处理管理不能使用Spring的全局异常处理器进行捕获和处理