微服务:Sentinel之 @SentinelResource 注解

一、@SentinelResource注解

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)

  • entryType:entry 类型,可选项(默认为 EntryType.OUT

  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback/fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

例如:

public class TestService {

    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String hello(long s) {
        return String.format("Hello at %d", s);
    }
    
    // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
    public String helloFallback(long s) {
        return String.format("Halooooo %d", s);
    }

    // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }

    // 这里单独演示 blockHandlerClass 的配置.
    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 public static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }
}

二、blockHandler和fallback

blockHandler和fallback这是最常用的两个注解

1 blockHandler

blockHandler是sentinel限流兜底方法

1.1 按Url限流

业务类:

@GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl()
    {
        return new CommonResult(200,"按url限流测试OK",new Payment(2022L,"serial002"));
    }

给资源名/byResorce配置限流:
微服务:Sentinel之 @SentinelResource 注解_第1张图片
微服务:Sentinel之 @SentinelResource 注解_第2张图片

结果 访问会出现sentinel自带的兜底方法
按Url配置sentinel限流结果

1.2 按资源名称限流

业务类:

@GetMapping("/byResource")
@SentinelResource(value = "byResource")
public CommonResult byResource(){
        
    return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
   
public CommonResult handleException(BlockException exception){
        
    return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}

给资源名byResource配置限流:
微服务:Sentinel之 @SentinelResource 注解_第3张图片
结果 访问直接报错:
微服务:Sentinel之 @SentinelResource 注解_第4张图片
再修改@SentinelResource(value = "byResource")给它一个兜底方法

@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")

此时访问就会发现有自定义的兜底方法
加兜底方法结果

1.3 blockHandlerClass

1.2的配置出现的问题

  • 业务类代码和兜底代码没用解耦
  • 如果每一个业务都需要加一个兜底方法,会导致代码极具膨胀
  • 没用全局统一处理

自定义限流处理类:

public static CommonResult handlerException(BlockException exception){
    
    return new CommonResult(4444,"按客戶自定义,global handlerException----1");
}

public static CommonResult handlerException2(BlockException exception){
  
    return new CommonResult(4444,"按客戶自定义,global handlerException----2");
}

业务类:

/**
 * 自定义通用的限流处理逻辑,
 blockHandlerClass = CustomerBlockHandler.class
 blockHandler = handleException2
 上述配置:找CustomerBlockHandler类里的handleException2方法进行兜底处理
 */
/**
 * 自定义通用的限流处理逻辑
 */
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler(){
    
    return new CommonResult(200,"按客户自定义限流处理逻辑");
}

customerBlockHandler配置sentinel限流:
微服务:Sentinel之 @SentinelResource 注解_第5张图片

结果:
自定义限流处理
进一步说明:
微服务:Sentinel之 @SentinelResource 注解_第6张图片

1.4 总结

可以清晰的发现

  • 按Url配置sentinel限流规则可以触发sentinel默认的兜底方法Blocked by Sentinel(flow limiting)
  • 按资源配置sentinel限流规则不加兜底方法访问就会直接报错
  • 加上blockHandler才会出现我们自定义的兜底方法

2 fallback

fallback是java异常的兜底方法

2.1 不加fallback方法

业务类:

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback") 
     public CommonResult<Payment> fallback(@PathVariable Long id)
    {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

	public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
    	Payment payment = new Payment(id,"null");
    	return new CommonResult<>(444,"fallback,无此流水,exception  "+e.getMessage(),payment);
	}

结果 直接访问报错:
微服务:Sentinel之 @SentinelResource 注解_第7张图片

2.2 加fallback方法

给资源fallback添加fallback:

@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常

图说:
微服务:Sentinel之 @SentinelResource 注解_第8张图片

结果 访问会有自定义的兜底方法:
fallback

2.3 fallbackClass

这个和blockHandlerClass都是一样的用处,指定兜底方法的那个类

3 blockHandler和fallback都加上

业务类:

@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id)
{
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(444,"fallback,无此流水,exception  "+e.getMessage(),payment);
}

public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
}

图说:
微服务:Sentinel之 @SentinelResource 注解_第9张图片
给资源customerBlockHandler配置sentinel限流
微服务:Sentinel之 @SentinelResource 注解_第10张图片
结果 直接访问会出现sentinel的兜底方法
在这里插入图片描述
说明:
若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

三、exceptionsToIgnore

使用exceptionsToIgnore可以忽略掉一个指定的异常
例如:

@RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler"
        ,exceptionsToIgnore = {IllegalArgumentException.class}) 
     public CommonResult<Payment> fallback(@PathVariable Long id)

图说:
微服务:Sentinel之 @SentinelResource 注解_第11张图片
结果 直接访问报错:
微服务:Sentinel之 @SentinelResource 注解_第12张图片
说明:
IllegalArgumentException该异常确实没用进入fallback。

你可能感兴趣的:(微服务:Spring,Cloud,Alibaba,java,spring,cloud,微服务,spring,boot)