SpringCloud Alibaba Sentinel中@SentinelResource使用实践总结

发布于:2024-06-27 ⋅ 阅读:(160) ⋅ 点赞:(0)

Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。

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

官网地址:注解埋点支持

【1】资源名称限流

① controller方法

@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
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 服务不可用");
}

这里 blockHandler 指定了超出规则限制后的处理方法handleException。

② 添加流控规则

如下所示,我们对 byResource 这个资源添加规则设置QPS/1。那么当请求QPS>1时,就会触发我们的 blockHandler = handleException 方法,返回错误信息。

在这里插入图片描述

在这里插入图片描述

其默认也支持根据URL进行限流,如下图所示,当我们访问了http://localhost:8401/byResource 时,簇点链路界面自动会有两个资源:/byResourcebyResource

不建议同时对URL和自定义命名资源添加规则,会导致混乱。

在这里插入图片描述

URL流控限制不会使用我们自定义的blockHandler 方法,会返回默认的 Blocked by Sentinel (flow limiting)

【2】抽离blockHandler

也就是将blockHandler处理从业务类抽离出来,单独放在一个异常处理类比如CustomerBlockHandler。

如下所示修改业务方法:

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
         blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
 public CommonResult customerBlockHandler()
 {
     return new CommonResult(200,"按客户自定义限流处理逻辑");
 }

述配置:找CustomerBlockHandler类里的handleException方法进行兜底处理。

创建CustomerBlockHandler ,定义handleException方法:

public class CustomerBlockHandler {
    public static CommonResult handleException(BlockException exception){
        return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
    }
}

抽离也是符合单一职责原则(Single Responsibility Principle)-每一个类(接口)应该专注于做一件事情。常常可见接口多继承现象

【3】注解属性说明

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

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT)
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback: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 直接抛出。

【4】服务熔断实践

如下所示,有四种配置实例:

  • ① @SentinelResource(value = “fallback”) 只定义了资源
  • ② @SentinelResource(value = “fallback”,fallback = “handlerFallback”)定义了服务熔断处理,也就是针对运行时异常
  • ③ @SentinelResource(value = “fallback”,blockHandler = “blockHandler”)定义了违背规则处理,也就是违背了Sentinel定义规则比如QPS/1
  • ④ @SentinelResource(value = “fallback”,fallback = “handlerFallback”,blockHandler = “blockHandler”)同时定义了规则处理和服务熔断处理
public static final String SERVICE_URL = "http://nacos-payment-provider";

 @Resource
 private RestTemplate restTemplate;

 @RequestMapping("/consumer/fallback/{id}")
//    @SentinelResource(value = "fallback")   // 没有配置
//    @SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback
//    @SentinelResource(value = "fallback",blockHandler = "blockHandler")//blockhandler
//    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")//fallback+blockhandler
 @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",exceptionsToIgnore = RuntimeException.class)
 public String fallback(@PathVariable("id") Long id){

     String result = restTemplate.getForObject(SERVICE_URL+"/payment/"+id,String.class,id);
     if (id==4){
         throw new RuntimeException("非法参数异常");
     }

     return result;
 }

 public String handlerFallback(@PathVariable Long id){
     return id+"异常";
 }

 public String blockHandler(Long id, BlockException e){
     return "blockHandler异常";
 }

exceptionsToIgnore = RuntimeException.class 表示忽略处理的异常,那么异常将会直接被原样抛出。

对于④,blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。


那么同样出于单一职责原则,我们可以定义blockHandlerClass 和fallbackClass 来讲异常处理逻辑抽离出来。

@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
exceptionsToIgnore = RuntimeException.class,
blockHandlerClass = XXXXX,
fallbackClass = XXXXXX)

【5】服务熔断与Feign的实践

主启动类:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain84 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain84.class,args);
    }
}

yml配置:

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider

feign:
  sentinel:
    enabled: true

pom依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 后续做持久化用到 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

定义feign接口:

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFailService.class)
public interface PaymentService {
    @GetMapping("/payment/{id}")
    public String payment(@PathVariable("id") Long id);
}

这里fallback = PaymentFailService.class 将服务降级处理方法抽离了出来:

@Component
public class PaymentFailService implements PaymentService {
    @Override
    public String payment(Long id) {
        return "feign失败调用";
    }
}

业务controller:

@Resource
private PaymentService paymentService;

@GetMapping("consumer/payment/{id}")
public String payment(@PathVariable("id") Long id){
    return paymentService.payment(id);
}

可以看到与Feign整合这里我们只能定义fallback,不能像【4】中那样进行多样自定义配置。那么我们可以在Feign接口方法上定义@SentinelResource注解吗?

答案是不可以,如下所示,会抛异常:java.lang.IllegalStateException: Wrong state for SentinelResource annotation

在这里插入图片描述

【6】熔断框架比较

在这里插入图片描述