按资源名称限流+后续处理

启动nacos成功

启动sentinel成功

创建cloudalibaba-sentinel-service8401模块:

pom文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  5. </dependency>
  6. <!-- 引入自己定义的api通用包,可以使用通用返回结果类 -->
  7. <dependency>
  8. <groupId>com.sgy.cloud2020</groupId>
  9. <artifactId>cloud-api-commons</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>com.alibaba.cloud</groupId>
  14. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-web</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-test</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-devtools</artifactId>
  27. <scope>runtime</scope>
  28. <optional>false</optional>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-actuator</artifactId>
  33. </dependency>
  34. </dependencies>

配置:application.yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos注册中心地址
        server-addr: server.com:8848 #nacos
        enabled: true
    sentinel:
      transport:
        dashboard: server.com:8080
        port: 8719  #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
#        clientIp: 192.168.1.102



management:
  endpoints:
    web:
      exposure:
        include: "*"

Controller

package com.sgy.springcloud.controller;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.sgy.springcloud.entities.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handlerException")
    public R<String> byResource() {
        return new R().ok("按资源名称限流测试OK","订单数据");
    }

    public R<String> handlerException(BlockException exception) {
        return new R().error(444L,exception.getClass().getCanonicalName(),"服务不可用");
    }
}

主启动类

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

按资源名称限流设置

1610803443957.png

这里的资源名是根据@SentinelResource(value = “byResource”, blockHandler = “handlerException”)中的value设置的

测试

访问localhost:8401/byResource

1610803443985.png

快速连续访问:触发限流,执行blockHandler

1610803444016.png

关闭微服务8401,看看流控规则还存在吗

此时关闭8401微服务,再次查看Sentinel控制台,流控规则消失了…

该流控规则是临时的,非持久。

按照Url地址限流+后续处理

通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息

业务类RateLimitController

也就是根据@GetMapping(“/rateLimit/byUrl”)来设置限流,并且这里没有写blockHandler也就没有兜底的方法了,它会用系统默认的兜底方法进行显示。这里就是在验证这两个。

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public R<String> byUrl() {
    return new R().ok("按资源名称限流测试OK","订单数据");
}

Sentinel控制台配置

访问一次:http://localhost:8401/rateLimit/byUrl

1610803444147.png

测试

测试:访问:http://localhost:8401/rateLimit/byUrl,正常返回

1610803444257.png

1610803444285.png

快速多次访问:失败,因为没有自定义blockHandler,所以返回系统默认信息

上面兜底方法面临的问题

跟业务代码耦合

如果以后每个方法都要有兜底的方法的话是不是每个方法都要写这个

系统默认的,没有体现我们自己的业务要求。 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。 每一个业务方法都添加一个兜底的,那代码膨胀加剧。 全局统一的处理方法没有体现。

自定义限流处理逻辑

创建CustomerBlockHandler类

用于自定义限流处理逻辑:

package com.sgy.springcloud.handler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.sgy.springcloud.entities.R;

public class CustomerBlockHandler {
    public static R<String> handlerException1(BlockException exception) {
        return new R().error(4444L,"按客户自定义,global handlerException -----1",null);
    }

    public static R<String> handlerException2(BlockException exception) {
        return new R().error(4444L,"按客户自定义,global handlerException -----2",null);
    }
}

Controller

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
    blockHandlerClass = CustomerBlockHandler.class, //指定处理的类
    blockHandler = "handlerException1")  // 指定方法
public R<String> customerBlockHandler() {
    return new R().ok("按资源名称限流测试OK","订单数据");
}

sentinel的控制台配置

访问一次 http://localhost:8401/rateLimit/customerBlockHandler

1610803444322.png

测试

快速访问:http://localhost:8401/rateLimit/customerBlockHandler

1610803444348.png

实现了兜底方法和业务逻辑分离。

更多注解

注意:注解方式埋点不支持 private 方法。也就是@SentinelResource注解修饰的类都是public修饰的方法

@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 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。