SpringCloud-Alibaba学习(十二):Sentinel异常处理
异常处理
@SentinelResource注解
使用 Sentinel 时每个方法都可以看作是一个资源,但是默认只会将 Controller 的方法自动解析问资源,资源名为 uri。
对于服务层的方法就需要使用 @SentinelResource 注解手动标记为资源,并指定资源名。
对于默认解析的 uri 资源,Sentinel 在触发流控后会自行处理异常。而标记了 @SentinelResource 注解后,Sentinel 就只管抛出异常了,需要自定义异常处理器去处理异常。
@RestController
@RequestMapping("/anno")
public class AnnoResourceControoler {
@SentinelResource(value = "annotest")
@GetMapping("/test")
public String test() {
return "AnnoResource";
}
}
流控异常处理
@SentinelResource 注解提供了一些参数可以处理异常
- blockHandler:异常处理方法,方法需要与 @SentinelResource 标记方法在同一个类
- blockHandlerClass:可以指定单独的异常处理类,定义一个静态的处理方法,配合 blockHandler 参数指定方法
blockHandler 处理
@RestController
@RequestMapping("/anno")
public class AnnoResourceControoler {
@SentinelResource(value = "annotest", blockHandler = "handleException")
@GetMapping("/test")
public String test() {
return "AnnoResource";
}
/**
* 异常处理
* 处理器方法修饰必须为 public
* @param e 参数需要使用 BlockException,是 Sentinel 提供的异常类
* @return 返回数据会作为 Controller 方法的返回
*/
public String handleException(BlockException e) {
e.printStackTrace();
System.out.println(e.getClass());
return "系统繁忙,稍后重试";
}
}
请求参数可以传递给异常处理方法,有几个就添加几个参数,但是 BlockException 要放最后
@SentinelResource(value = "annotest2", blockHandler = "handleException2")
@GetMapping("/test2")
public String test2(String name) {
return "AnnoResource test2";
}
/**
* 参数列表除 BlockException 外,要和资源方法保持一致
*/
public String handleException2(String name, BlockException e) {
System.out.println("请求参数:" + name);
e.printStackTrace();
System.out.println(e.getClass());
return "系统繁忙,稍后重试";
}
blockHandlerClass
@SentinelResource(value = "annotest3", blockHandlerClass = BlockExceptionHandler.class, blockHandler = "handleException3")
@GetMapping("/test3")
public String test3(String name) {
return "AnnoResource test3";
}
public class BlockExceptionHandler {
/**
* 必须要 public 静态方法
* 参数列表除 BlockException 外,要和资源方法保持一致
* @param name
* @param e
* @return
*/
public static String handleException3(String name, BlockException e) {
e.printStackTrace();
System.out.println(e.getClass());
return "公共处理:系统繁忙,稍后重试 ";
}
}
熔断降级异常处理
熔断降级抛出的是异常,捕获 Throwale 即可
熔断降级相关的参数:
- fallback:指定异常处理方法,需要和资源方法同一个类
- fallbackClass:指定异常类,配合 fallback 指定处理方法,处理方法是静态的
- defaultFallback:
fallback
要求异常处理方法签名中除了 Throwable 参数外,其他需要与资源方法一致
@RestController
@RequestMapping("/break")
public class BreakController {
@GetMapping("/test1")
@SentinelResource(value = "breakTest1", fallback = "breakHandler")
public String test1(Integer type) throws Exception{
System.out.println("test1");
if (type == null || type == 0) {
System.out.println("正常接口调用");
} else {
if (type == 1) {
System.out.println("慢调用");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (type == 2) {
System.out.println("异常调用");
throw new IllegalArgumentException("接口发生异常");
}
}
return "test";
}
/**
* 熔断降级异常处理
* @param type
* @param throwable 熔断处理器方法参数需要用 Throwable
* @return
*/
public String breakHandler(Integer type, Throwable throwable) throws Exception{
System.out.println(throwable.getClass());
if (throwable instanceof DegradeException ) {
System.out.println("熔断降级异常处理");
return "服务开小差,请稍后重试";
} else {
return "非sentinel异常";
}
}
}
fallbackClass
@GetMapping("/test1")
@SentinelResource(value = "breakTest1", fallbackClass = BreakExceptionHandler.class, fallback = "breakHandler2")
public String test1(Integer type) throws Exception{
if (type == 2) {
System.out.println("异常调用");
throw new IllegalArgumentException("接口发生异常");
}
return "test";
}
public class BreakExceptionHandler {
/**
* 必须是静态方法
* @param type 与资源方法参数一致
* @param throwable 必须使用 Throwable
* @return
*/
public static String breakHandler2(Integer type, Throwable throwable) throws Exception{
System.out.println(throwable.getClass());
if (throwable instanceof DegradeException) {
System.out.println("熔断降级异常处理");
return "服务开小差,请稍后重试";
} else {
return "非sentinel异常";
}
}
}
统一异常处理
上述的流控异常处理和熔断降级异常处理,需要为每个资源编写并指定异常处理方法,太麻烦且冗余,且异常类型有限,可以使用 Spring 的全局异常处理。
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 流控规则全局异常处理
* 资源方法签名上需要有异常抛出声明 throws Exception 细粒度的 BlockException 及子类也可以
* @return
*/
@ExceptionHandler(FlowException.class)
public HttpResponse<Void> handleFlowException() {
return HttpResponse.error("系统繁忙,请稍后重试");
}
/**
* 熔断降级规则全局异常处理
* 资源方法签名上需要有异常抛出声明 throws Exception 细粒度的 BlockException 及子类也可以
* @return
*/
@ExceptionHandler(DegradeException.class)
public HttpResponse<Void> handleDegradeException() {
return HttpResponse.error("系统开小差,请稍后重试");
}
/**
* 权限规则全局异常处理
* @return
*/
@ExceptionHandler(AuthorityException.class)
public HttpResponse<Void> handleAuthorityException() {
return HttpResponse.error("没有权限访问");
}
/**
* 未知异常处理
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public HttpResponse<Void> handleException(Exception e) {
e.printStackTrace();
return HttpResponse.error("全局异常");
}
}