Sentinel核心库

Sentinel主页 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

Sentinel介绍

image.png
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量
控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度
来帮助开发者保障微服务的稳定性。

Sentinel核心组件

1:核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同
时对 Dubbo / Spring Cloud 等框架也有较好的支持。
2:控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

Sentinel vs Hystrix

对比内容 Sentinel Hystrix
隔离策略 信号量隔离 线程池隔离/信号量隔离
熔断降级策略 基于响应时间或失败比率 基于失败比率
实时指标实现 滑动窗口 滑动窗口(基于 RxJava)
规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解的支
支持 支持
限流 基于 QPS,支持基于调用关系的限流 不支持
流量整形 支持慢启动、匀速器模式 不支持
系统负载保护 支持 不支持
控制台 开箱即用,可配置规则、查看秒级监控、机器
发现等
不完善
常见框架的适
Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet Netflix、Spring Cloud

Sentinel核心概念

  1. 资源:资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服
    务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
  2. 规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以
    动态实时调整。

Sentinel核心功能

流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考
虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处
理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据
需要把随机的请求调整成合适的形状,如下图所示:
image.png
流量控制有以下几个角度:

  1. 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  2. 运行指标,例如 QPS、线程池、系统负载等;
  3. 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

熔断降级

什么是熔断降级

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系
的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误。

image.png
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应
时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资
源而导致级联故障。

Sentinel熔断降级设计

Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的
好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线
程数目过多),还需要预先给各个资源做线程池大小的分配。

Sentinel熔断降级设计:

并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源
对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不
稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源
上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
响应时间降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资
源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之
后才重新恢复。

系统自我保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高
的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把
本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时
候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证
系统在能力范围之内处理最多的请求。

Sentinel熔断限流

Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合Dashboard 可以取得最好的效果。先来学习Sentinel 核心库的使用,后面再学习Dashboard使用。
image.png

在我们项目中,用户请求通过 hailtaxi-gateway 路由到 hailtaxi-driver 或者 hailtaxi-order ,还
有可能在 hailtaxi-order 中使用feign调用 hailtaxi-driver ,所以我们有可能在单个服务中实现熔
断限流,也有可能要集成feign调用实现熔断限流,还有可能在微服务网关中实现熔断限流。我们接下
来一步一步实现每一种熔断限流操作。

入门案例

  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-core</artifactId>
  4. <version>1.8.1</version>
  5. </dependency>
  1. public class SentinelDemo {
  2. public static void main(String[] args) {
  3. //配置规则
  4. initFlowRules();
  5. while (true){
  6. try (Entry entry = SphU.entry("test01")){
  7. // 被保护的逻辑
  8. System.out.println("hello world");
  9. } catch (BlockException e) {
  10. // 处理被流控的逻辑
  11. System.out.println("blocked!");
  12. break;
  13. }
  14. }
  15. }
  16. public static void initFlowRules(){
  17. List<FlowRule> flowRules = new ArrayList<FlowRule>();
  18. FlowRule flowRule = new FlowRule();
  19. //设置qps=5
  20. flowRule.setCount(5);
  21. //针对那个资源设置规则
  22. flowRule.setResource("test01");
  23. //限流维度
  24. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  25. flowRules.add(flowRule);
  26. FlowRuleManager.loadRules(flowRules);
  27. }
  28. }

效果:
image.png

启动 Sentinel 控制台

直接下载源码,导入到idea里面然后启动,看看是否有必要修改端口号
image.png
Sentinel 开源控制台支持实时监控和规则管理。接入控制台的步骤如下:
(1)下载控制台 jar 包并在本地启动:如果不是下载源码启动的,就见此处文档,可以参见 此处文档
(2)客户端接入控制台,需要:

  • 客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信。您可以通过 pom.xml 引入 JAR 包:

    1. <dependency>
    2. <groupId>com.alibaba.csp</groupId>
    3. <artifactId>sentinel-transport-simple-http</artifactId>
    4. <version>1.8.1</version>
    5. </dependency>
  • 启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。更多的参数参见 启动参数文档

    -Dcsp.sentinel.dashboard.server=localhost:8089 image.png

  • 确保应用端有访问量

完成以上步骤后即可在 Sentinel 控制台上看到对应的应用,机器列表页面可以看到对应的机器:
注意:登录密码:sentinel \sentinel
image.png

注解支持

  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-annotation-aspectj</artifactId>
  4. <version>1.8.1</version>
  5. </dependency>
  1. @SpringBootApplication
  2. public class SentinelDemo {
  3. public static void main(String[] args) {
  4. //配置规则
  5. initFlowRules();
  6. SpringApplication.run(SentinelDemo.class);
  7. }
  8. public static void initFlowRules(){
  9. List<FlowRule> flowRules = new ArrayList<FlowRule>();
  10. FlowRule flowRule = new FlowRule();
  11. //设置qps=5
  12. flowRule.setCount(2);
  13. //针对那个资源设置规则
  14. flowRule.setResource("hello");
  15. //限流维度
  16. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  17. flowRules.add(flowRule);
  18. FlowRuleManager.loadRules(flowRules);
  19. }
  20. }
  1. @Service
  2. public class TestService {
  3. @SentinelResource(value = "hello",blockHandler = "exceptionHandler")
  4. public String hello(long s){
  5. return String.format("Hello at %d", s);
  6. }
  7. @SentinelResource(value = "hello",fallback = "hellof")
  8. public String hellof(long s){
  9. return String.format("Hello at %d", s);
  10. }
  11. //降级 Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
  12. public String helloFallback(long s) {
  13. return String.format("Halooooo %d", s);
  14. }
  15. // 熔断 Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
  16. public String exceptionHandler(long s, BlockException ex) {
  17. // Do some log here.
  18. ex.printStackTrace();
  19. return "Oops, error occurred at " + s;
  20. }
  21. }
  1. @RestController
  2. public class TestController {
  3. @Autowired
  4. private TestService testService;
  5. @RequestMapping("hello")
  6. public String hello(){
  7. return testService.hello(23L);
  8. }
  9. @RequestMapping("hellof")
  10. public String hellof(){
  11. return testService.hellof(23L);
  12. }
  13. }
  1. @Configuration
  2. public class SentinelAspectConfiguration {
  3. @Bean
  4. public SentinelResourceAspect sentinelResourceAspect() {
  5. return new SentinelResourceAspect();
  6. }
  7. }

Spi扩展点

FlowRuleInitFunc

把上面的流控规则写到这个类里面

  1. public class FlowRuleInitFunc implements InitFunc {
  2. @Override
  3. public void init() throws Exception {
  4. initFlowRules();
  5. }
  6. public static void initFlowRules(){
  7. List<FlowRule> flowRules = new ArrayList<FlowRule>();
  8. FlowRule flowRule = new FlowRule();
  9. //设置qps=5
  10. flowRule.setCount(2);
  11. //针对那个资源设置规则
  12. flowRule.setResource("hello");
  13. //限流维度
  14. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  15. flowRules.add(flowRule);
  16. FlowRuleManager.loadRules(flowRules);
  17. }
  18. }

在META-INF/services/com.alibaba.csp.sentinel.init.InitFunc文件中,添 加自定义扩展点的全路径

image.png
启动测试

SpringBoot集成

Maven依赖

  1. <properties>
  2. <java.version>11</java.version>
  3. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  4. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  5. <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
  6. <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
  7. </properties>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-web</artifactId>
  12. </dependency>
  13. <!--Sentinel依赖-->
  14. <dependency>
  15. <groupId>com.alibaba.cloud</groupId>
  16. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.springframework.boot</groupId>
  20. <artifactId>spring-boot-starter-test</artifactId>
  21. <scope>test</scope>
  22. <exclusions>
  23. <exclusion>
  24. <groupId>org.junit.vintage</groupId>
  25. <artifactId>junit-vintage-engine</artifactId>
  26. </exclusion>
  27. </exclusions>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.projectlombok</groupId>
  31. <artifactId>lombok</artifactId>
  32. <version>1.18.16</version>
  33. </dependency>
  34. </dependencies>
  35. <dependencyManagement>
  36. <dependencies>
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-dependencies</artifactId>
  40. <version>${spring-boot.version}</version>
  41. <type>pom</type>
  42. <scope>import</scope>
  43. </dependency>
  44. <dependency>
  45. <groupId>com.alibaba.cloud</groupId>
  46. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  47. <version>${spring-cloud-alibaba.version}</version>
  48. <type>pom</type>
  49. <scope>import</scope>
  50. </dependency>
  51. </dependencies>
  52. </dependencyManagement>

返回值封装

  1. @Data
  2. public class BasicResult<T> {
  3. private String resultCode;
  4. private String errorMsg;
  5. private T data;
  6. }

Controller

  1. @RestController
  2. public class SpringbootsentinelController {
  3. @Autowired
  4. SpringbootsentinelService springbootsentinelService;
  5. @GetMapping("/hello/{name}")
  6. public BasicResult<String> sayHello(@PathVariable("name") String name) throws SystemBlockException {
  7. return springbootsentinelService.doTest(name);
  8. }
  9. }

Service

  1. @Service
  2. public class SpringbootsentinelService {
  3. public BasicResult<String> doTest(String name) throws SystemBlockException {
  4. if ("张三".equals(name)){
  5. throw new SystemBlockException("500","该用户已存在");
  6. }
  7. BasicResult<String> basicResult = new BasicResult<>();
  8. basicResult.setData("hello , "+name);
  9. basicResult.setResultCode("200");
  10. return basicResult;
  11. }
  12. }

启动类

  1. @SpringBootApplication
  2. public class SpringbootsentinelApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringbootsentinelApplication.class, args);
  5. }
  6. }

如果在SpringBoot项目中使用Sentinel,首先需要引入 spring-cloud-starter-alibaba-sentinel 依
赖,并使用 @SentinelResource 标识资源。

@SentinelResource

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

value 资源名称,必需项(不能为空)
blockHandler /
blockHandlerClass
blockHandler 对应处理 BlockException 的函数名称,可选项。♞
blockHandler 函数访问范围需要是 public;♞ 返回类型需要与原
方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的
参数,类型为 BlockException。♞ blockHandler 函数默认需要和
原方法在同一个类中。若希望使用其他类的函数,则可以指定
blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必
需为 static 函数,否则无法解析。
fallback / fallbackClass fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback
处理逻辑。fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback
函数签名和位置要求:♞ 返回值类型必须与原函数返回值类型一
致;♞ 方法参数列表需要和原函数一致,或者可以额外多一个
Throwable 类型的参数用于接收对应的异常。♞ fallback 函数默认
需要和原方法在同一个类中。若希望使用其他类的函数,则可以指
定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需
为 static 函数,否则无法解析。
defaultFallback(1.6.0 开
始)
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑
(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类
型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处
理。若同时配置了 fallback 和 defaultFallback,则只有 fallback
会生效。defaultFallback 函数签名要求:♞ 返回值类型必须与原
函数返回值类型一致;♞ 方法参数列表需要为空,或者可以额外多
一个 Throwable 类型的参数用于接收对应的异常。♞
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用
其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对
象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(1.6.0
开始)
用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入
fallback 逻辑中,而是会原样抛出。
entryType entry 类型,可选项(默认为 EntryType.OUT)


blockHandler

当name等于张三的时候,此时会报错,代码如下
image.png
如果此时访问 http://localhost:8081/hello/张三, 查询司机信息,如果没有name为张三的司机信息,会报如下错误,这种体验非常查,我们可以集成Sentinel使用 @Sentinesource 的blockHandler 返回默认错误信息。
image.png
在工程中引入工程中引入 spring-cloud-starter-alibaba-sentinel 依赖,依赖如下

  1. <!--Sentinel依赖-->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. </dependency>

我们先添加一个方法 blockExHandler ()用来处理程序发生 BlockException 异常的时候,执行默认操
作,代码如下:

  1. /***
  2. * blockExHandler
  3. * 1)方法入参和被降级方法保持一致,最多多一个BlockException
  4. * 2)返回值要和被降级方法保持一致
  5. */
  6. public BasicResult blockExHandler(String name, BlockException blockException){
  7. BasicResult basicResult = new BasicResult();
  8. SystemBlockException blockException1 = (SystemBlockException) blockException;
  9. basicResult.setResultCode(blockException1.getResourceName());
  10. basicResult.setErrorMsg(blockException1.getRuleLimitApp());
  11. return basicResult;
  12. }

我们为 doTest() 方法添加一个 @SentinelResource 注解,用来标注资源,表示当前方法需要执行限
流、降级,在注解中添加value属性,用来标注资源,说白了就是给当前资源起个名字,blockHandler
用来表示当前方法发生 BlockException 异常的时候,将处理流程交给指定的方法 blockExHandler()
处理,此时 blockExHandler() 方法必须和抛出异常的方法在同一个类中,这是一种降级操作,代码如
下:

image.png
如果此时不在同一个类中,我们可以在 @SentinelResource 中添加 blockHandlerClass 属性,指定
降级处理类的方法所在的类,代码如下:

  1. @SentinelResource(value = "info",blockHandler =
  2. "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx"

访问http://localhost:8081/hello/张三 测出出错效果如下:
image.png

fallback

如果我们希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是 BlockException 异常才调
用,此时可以使用 @SentinelResource 的 fallback 属性,代码如下:
image.png
访问http://localhost:8081/hello/张三 测出出错效果如下:
image.png
如果发生异常执行的方法和当前发生异常的方法不在同一个类中,可以使用 @SentinelResource 注解
的 fallbackClass 实现,代码如下:

  1. @SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass =
  2. "xx.xxx.xxx.xx.Xxx")

defaultFallback

上面无论是 blockHandler 还是 fallback ,每个方法发生异常,都要为方法独立创建一个处理异常的
方法,效率非常低,我们可以使用 @SentinelResource 注解的 defaultFallback 属性,为一个类指定
一个全局的处理错误的方法,代码如下:

  1. @Service
  2. @SentinelResource(defaultFallback = "defaultFallback")
  3. public class SpringbootsentinelService {
  4. /**
  5. * @param name
  6. * @return 一定要指定
  7. */
  8. @SentinelResource(value = "info")
  9. public BasicResult<String> doTest(String name) throws RuntimeException {
  10. if ("张三".equals(name)){
  11. throw new RuntimeException("该用户已存在");
  12. }
  13. BasicResult<String> basicResult = new BasicResult<>();
  14. basicResult.setData("hello , "+name);
  15. basicResult.setResultCode("200");
  16. return basicResult;
  17. }
  18. /**
  19. * 异常处理
  20. * @return
  21. */
  22. public BasicResult<String> defaultFallback(){
  23. BasicResult<String> basicResult = new BasicResult();
  24. basicResult.setResultCode("500");
  25. basicResult.setErrorMsg("系统繁忙,请稍后再试-defaultFallback");
  26. return basicResult;
  27. }
  28. }

此时需要注意, defaultFallback 属性指定的方法入参必须为空,最多可以增加一个异常对象。
我们访问http://localhost:8081/hello/张三 效果如下:
image.png

限流降级规则

Sentinel支持多种限流规则,规则我们可以在代码中直接定义,规则属性如下:

Field 说明 默认值
resource 资源名,资源名是限流规则的作用对象
count 限流阈值
grade 限流阈值类型,QPS 模式(1)或并发线程数模式
(0)
QPS 模式
limitApp 流控针对的调用来源 default ,代表不区
分调用来源
strategy 调用关系限流策略:直接、链路、关联 根据资源本身(直
接)
controlBehavior 流控效果(直接拒绝/WarmUp/匀速+排队等
待),不支持按调用关系限流
直接拒绝
clusterMode 是否集群限流

流量控制

理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方
式定义流量控制规则。

QPS流量控制


先实现流量基于QPS控制,代码如下:

  1. @Configuration
  2. public class SentinelLiuLiang {
  3. /**
  4. * 初始化规则
  5. */
  6. @PostConstruct
  7. public void initFlowQpsRule(){
  8. //规则集合
  9. List<FlowRule> rulesList = new ArrayList<>();
  10. //定义一个规则 info是资源名称
  11. FlowRule rule = new FlowRule("info");
  12. //设置阀值
  13. rule.setCount(2);
  14. //设置限流类型
  15. rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
  16. //default,代表不区分调用来源
  17. rule.setLimitApp("default");
  18. //定义规则到集合
  19. rulesList.add(rule);
  20. //加载规则
  21. FlowRuleManager.loadRules(rulesList);
  22. }
  23. }
  24. @Service
  25. @SentinelResource(defaultFallback = "defaultFallback")
  26. public class SpringbootsentinelService {
  27. /**
  28. * @param name
  29. * @return 一定要指定
  30. */
  31. @SentinelResource("info")
  32. public BasicResult<String> doTest(String name) throws RuntimeException {
  33. BasicResult<String> basicResult = new BasicResult<>();
  34. basicResult.setData("hello , "+name);
  35. basicResult.setResultCode("200");
  36. return basicResult;
  37. }
  38. /**
  39. * 异常处理
  40. * @return
  41. */
  42. public BasicResult<String> defaultFallback(){
  43. BasicResult<String> basicResult = new BasicResult();
  44. basicResult.setResultCode("500");
  45. basicResult.setErrorMsg("系统繁忙,请稍后再试-defaultFallback");
  46. return basicResult;
  47. }
  48. }

访问http://localhost:8081/hello/张三 此时不会抛出异常,但是频繁刷新,则会调用降
级方法,这个是用Jmter压测的效果如下:
image.png

QPs流量控制详解

当 QPS 超过某个阈值的时候,则采取措施进行流量控制行为(类似于我们前面说过的限流算
法上的差异),Sentinel提供了四种流量控制行为

  1. 直接拒绝(CONTROL_BEHAVIOR_DEFAULT)
  2. Warm Up(CONTROL_BEHAVIOR_WARM_UP)
  3. 匀速排队(CONTROL_BEHAVIOR_RATE_LIMITER,漏桶算法 )
  4. 冷启动+匀速器(CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER),除了让流量缓慢增加,还控制的了请求的间隔时间,让请求已均匀速度通过。这种策略是1.4.0版本新增 的。

这四个行为,是通过FlowRule中的controlBehavior属性来控制,默认是直接拒绝。

直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。代码案例

  1. public static void main(String[] args) {
  2. initFlowRule();
  3. while (true){
  4. try (Entry entry = SphU.entry("CONTROL_BEHAVIOR_DEFAULT")){
  5. System.out.println("CONTROL_BEHAVIOR_DEFAULT");
  6. }catch (Exception e) {
  7. // 处理被流控的逻辑
  8. System.out.println("blocked!");
  9. break;
  10. }
  11. }
  12. }
  13. private static void initFlowRule(){
  14. List<FlowRule> flowRules = new ArrayList<>();
  15. FlowRule flowRule = new FlowRule();
  16. flowRule.setCount(2);//控制流量
  17. flowRule.setResource("CONTROL_BEHAVIOR_DEFAULT");//资源
  18. flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);//限流行为
  19. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流类型
  20. flowRules.add(flowRule);
  21. FlowRuleManager.loadRules(flowRules);
  22. }

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过”冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
Sentinel应用 - 图18

  1. public static void main(String[] args) {
  2. initFlowRule();
  3. while (true){
  4. try (Entry entry = SphU.entry("CONTROL_BEHAVIOR_WARM_UP")){
  5. System.out.println("CONTROL_BEHAVIOR_WARM_UP");
  6. }catch (Exception e) {
  7. // 处理被流控的逻辑
  8. System.out.println("blocked!");
  9. break;
  10. }
  11. }
  12. }
  13. private static void initFlowRule(){
  14. List<FlowRule> flowRules = new ArrayList<>();
  15. FlowRule flowRule = new FlowRule();
  16. flowRule.setCount(20);//控制流量
  17. flowRule.setResource("CONTROL_BEHAVIOR_WARM_UP");//资源
  18. flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);//限流行为
  19. flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//限流类型
  20. flowRule.setWarmUpPeriodSec(10);//代表期待系统进入稳定状态的时间(即预热时长).
  21. flowRules.add(flowRule);
  22. FlowRuleManager.loadRules(flowRules);
  23. }
  24. }

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

当请求数量远远大于阈值时,这些请求会排队等待,这个等待时间可以设置,如果超过等待时
间,那这个请求会被拒绝。
如下图所示,假设qps=5,表示请求每200ms才能通过1个,多处的请求排队等待,超时时间达
标最大排队时间,超过最大排队时间则直接拒绝。
image.png

基于调用关系的流量控制

线程数流量控制

我们修改限流阈值类型,代码如下:
image.png
此时再来访问http://localhost:8081/hello/张三 发现用浏览器无论怎么访问都不会出现
降级现象,但是如果用Jmeter模拟多个线程,效果就不一样了,效果如下:
image.png

image.png
image.png
效果不是很好验证,需要多循环几次

熔断降级

熔断降级规则包含下面几个重要的属性:

Field 说明 默认
resource 资源名,即规则的作用对象
grade 熔断策略,支持慢调用比例/异常比例/异常数策略 慢调
用比
count 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调
用);异常比例/异常数模式下为对应的阈值
timeWindow 熔断时长,单位为 s
minRequestAmount 熔断触发的最小请求数,请求数小于该值时即使异常比率超出
阈值也不会熔断(1.7.0 引入)
5
statIntervalMs 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引
入)
1000
ms
slowRatioThreshold 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

同一个资源可以同时有多个降级规则。理解上面规则的定义之后,我们可以通过调用
DegradeRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则,在SentinelLiuLiang规则定义如下:

  1. /**
  2. * 熔断降级规则
  3. */
  4. @PostConstruct
  5. public void initDegradeRule(){
  6. //降级规则集合
  7. List<DegradeRule> ruleList = new ArrayList<>();
  8. DegradeRule degradeRule = new DegradeRule("info");
  9. //设置触发降级阀值
  10. degradeRule.setCount(2);
  11. degradeRule.setMinRequestAmount(1);
  12. //熔断降级策略,支持慢调用比例/异常比例/异常数策略
  13. //DEGRADE_GRADE_RT:平均响应时间
  14. //DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
  15. //DEGRADE_GRADE_EXCEPTION_COUNT:异常数
  16. degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
  17. //熔断窗口时长 单位s
  18. degradeRule.setTimeWindow(30);
  19. //设置统计时间
  20. degradeRule.setStatIntervalMs(1000);
  21. //将规则添加到集合
  22. ruleList.add(degradeRule);
  23. //加载规则
  24. DegradeRuleManager.loadRules(ruleList);
  25. }

测试一下平均响应时间,在程序中休眠10秒中,再执行访问,代码如下:
image.png
测试效果如下:
image.png
我们可以发现只有2个访问是成功的,并且熔断降级10秒钟之后才可接着访问该方法。
image.png

系统自我保护

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体
平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量
和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则包含下面几个重要的属性

Field 说明 默认值
highestSystemLoad load1 触发值,用于触发自适应控制阶段 -1 (不生效)
avgRt 所有入口流量的平均响应时间 -1 (不生效)
maxThread 入口流量的最大并发数 -1 (不生效)
qps 所有入口资源的 QPS -1 (不生效)
highestCpuUsage 当前系统的 CPU 使用率(0.0-1.0) -1 (不生效)

理解上面规则的定义之后,我们可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的
方式定义流量控制规则。
在 配置类中创建如下方法,代码如下:

  1. /**
  2. * 系统自我保护
  3. */
  4. @PostConstruct
  5. private void initSystemRule() {
  6. //系统自我保护集合
  7. List<SystemRule> rules = new ArrayList<>();
  8. //创建系统自我保护规则
  9. SystemRule rule = new SystemRule();
  10. //CPU使用率 值为0-1,-1 (不生效)
  11. rule.setHighestCpuUsage(0.2);
  12. //所有入口资源的 QPS,-1 (不生效)
  13. rule.setQps(10);
  14. //入口流量的最大并发数,-1 (不生效)
  15. rule.setMaxThread(5);
  16. //所有入口流量的平均响应时间,单位:秒,-1 (不生效)
  17. rule.setAvgRt(5);
  18. //load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
  19. rule.setHighestSystemLoad(20);
  20. //将规则加入到集合
  21. rules.add(rule);
  22. SystemRuleManager.loadRules(rules);
  23. }

我们可以测试CPU使用率自我保护,如下效果:
image.png
https://www.jianshu.com/p/3668cc1bf24a

热点数据

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数
据,并对其访问进行限制。比如:

  1. 1:商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  2. 2:用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调
用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效 。
image.png
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点
参数限流支持集群模式。
要使用热点参数限流功能,需要引入以下依赖:

  1. <!--热点参数-->
  2. <dependency>
  3. <groupId>com.alibaba.csp</groupId>
  4. <artifactId>sentinel-parameter-flow-control</artifactId>
  5. <version>1.8.1</version>
  6. </dependency>

然后为对应的资源配置热点参数限流规则,并在 entry 的时候传入相应的参数,即可使热点参数限流
生效。热点参数规则( ParamFlowRule )类似于流量控制规则( FlowRule ):

属性 说明 默认值
resource 资源名,必填
count 限流阈值,必填
grade 限流模式 QPS 模
durationInSec 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 1s
controlBehavior 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开
始支持
快速失
maxQueueingTimeMs 最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本
开始支持
0ms
paramIdx 热点参数的索引,必填,对应 SphU.entry(xxx, args)
中的参数索引位置
paramFlowItemList 参数例外项,可以针对指定的参数值单独设置限流阈值,
不受前面 count 阈值的限制。仅支持基本类型和字符串
类型
clusterMode 说明 是否是集群参数流控规则 默认值 false
clusterConfig 集群流控相关配置

我们可以创建一个司机筛选方法,比如根据城市来筛选,在 DriverController 中创建一个方法:


我们访问 http://localhost:8081/driver/search/shenzhen 的时候,连续执行5次,才会限流,
效果如下:
image.png
我们访问 http://localhost:18081/driver/search/tj 的时候,连续执行2次,就会限流,效果如
下:
image.png


OpenFeign支持

Sentinel 适配了 Feign 组件。如果想使用,除了引入 spring-cloud-starter-alibaba-sentinel 的依
赖外还需要 2 个步骤:

  1. 1:配置文件打开 Sentinel Feign 的支持:feign.sentinel.enabled=true
  2. 2:加入 spring-cloud-starter-openfeign 依赖使 Sentinel starter 中的自动化配置类生效

image.png
在上面案例中,我们可以实现用户打车成功调用 hailtaxi-order 执行下单,并且通过feign调用
hailtaxi-driver 修改司机状态,此时我们可以使用Sentinel实现Feign调用降级、限流。把之前的案例中 @SentinelResource 相关注解全部注释掉,再实现Feign集成。
在 hailtaxi-driver 中引入 OpenFeign 依赖,配置如下:

  1. <!--sentinel-->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  5. <version>2.2.5.RELEASE</version>
  6. </dependency>
  7. <!--Openfeign-->
  8. <dependency>
  9. <groupId>org.springframework.cloud</groupId>
  10. <artifactId>spring-cloud-starter-openfeign</artifactId>
  11. </dependency>

还需要在Feign调用客户端(也就是 hailtaxi-order )中开启Feign的支持,配置如下:

  1. feign:
  2. #开启Sentinel对Feign的支持
  3. sentinel:
  4. enabled: true

为了测试程序异常能实现降级操作,我们在 hailtaxi-order 中将 OrderInfoController.add() 方法
的司机ID改成一个不存在的司机ID,让程序报错,测试降级处理,代码如下:
image.png

fallback

可以为Feign接口创建一个实现类,在实现类中处理程序异常降级处理方法,代码如下:

  1. public class DriverFeignFallback implements DriverFeign{
  2. /**
  3. * status()降级处理方法
  4. */
  5. @Override
  6. public Driver status(String id, Integer status) {
  7. Driver driver = new Driver();
  8. driver.setId(id);
  9. driver.setStatus(status);
  10. driver.setName("系统比较繁忙,请您稍后再试!");
  11. return driver;
  12. }
  13. }

我们还需要在Feign接口上添加 fallback 属性指定讲解处理的类,代码如下:

  1. FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)

此时测试下效果如下:
image.png

fallbackFactory

可以为Feign接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,代码
如下:

  1. @Component
  2. public class DriverFeignFallback implements FallbackFactory<DriverFeign>{
  3. @Override
  4. public DriverFeign create(Throwable throwable) {
  5. return new DriverFeign() {
  6. /**
  7. * status()降级处理方法
  8. */
  9. @Override
  10. public Driver status(String id, Integer status) {
  11. Driver driver = new Driver();
  12. driver.setId(id);
  13. driver.setStatus(status);
  14. driver.setName("系统比较繁忙,请您稍后再试!");
  15. return driver;
  16. }
  17. };
  18. }
  19. }

我们还需要在Feign接口上添加 fallbackFactory 属性指定讲解处理的类,代码如下:

  1. @FeignClient(value = "hailtaxi-driver",fallbackFactory =
  2. DriverFeignFallbackFactory.class)

测试效果如下
image.png

Sentinel集成Gateway

Sentinel对网关支持

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。
image.png
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规
则和自定义 API 的实体和管理逻辑:

  1. GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  2. ApiDefinition : 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api ,请求 path 模式为 /foo/ 和 /baz/ 的都归到 my_api 这个 API分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

其中网关限流规则 GatewayFlowRule 的字段解释如下:

  1. resource :资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
  2. resourceMode :规则是针对 API Gateway 的 route( RESOURCE_MODE_ROUTE_ID )还是用户在

Sentinel 中定义的 API 分组( RESOURCE_MODE_CUSTOM_API_NAME ),默认是 route。

  1. grade :限流指标维度,同限流规则的 grade 字段。
  2. count :限流阈值
  3. intervalSec :统计时间窗口,单位是秒,默认是 1 秒。
  4. controlBehavior :流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快
    速失败和匀速排队两种模式,默认是快速失败。
  5. burst :应对突发请求时额外允许的请求数目。
  6. maxQueueingTimeoutMs :匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下
    生效。
  7. paramItem :参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规
    则;否则会转换成热点规则。其中的字段:
    1. parseStrategy :从请求中提取参数的策略,目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP )、Host( PARAM_PARSE_STRATEGY_HOST )、任意 Header( PARAM_PARSE_STRATEGY_HEADER )和任意 URL 参数 ( PARAM_PARSE_STRATEGY_URL_PARAM )四种模式。
    2. fieldName :若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
    3. pattern :参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则
      统计该请求属性的所有值。(1.6.2 版本开始支持)
    4. matchStrategy :参数值的匹配策略,目前支持精确匹配( PARAM_MATCH_STRATEGY_EXACT )、子串匹配( PARAM_MATCH_STRATEGY_CONTAINS )和正则匹配( PARAM_MATCH_STRATEGY_REGEX )。(1.6.2 版本开始支持)
    5. 用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)。

创建Gateway网关项目

maven依赖

  1. <properties>
  2. <java.version>11</java.version>
  3. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  4. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  5. <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
  6. <spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
  7. </properties>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-gateway</artifactId>
  12. </dependency>
  13. <dependency>
  14. <groupId>com.alibaba.cloud</groupId>
  15. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  16. </dependency>
  17. <!--Sentinel-->
  18. <dependency>
  19. <groupId>com.alibaba.csp</groupId>
  20. <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
  21. <version>1.8.1</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>com.alibaba.cloud</groupId>
  25. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-test</artifactId>
  30. <scope>test</scope>
  31. <exclusions>
  32. <exclusion>
  33. <groupId>org.junit.vintage</groupId>
  34. <artifactId>junit-vintage-engine</artifactId>
  35. </exclusion>
  36. </exclusions>
  37. </dependency>
  38. </dependencies>
  39. <dependencyManagement>
  40. <dependencies>
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-dependencies</artifactId>
  44. <version>${spring-boot.version}</version>
  45. <type>pom</type>
  46. <scope>import</scope>
  47. </dependency>
  48. <dependency>
  49. <groupId>com.alibaba.cloud</groupId>
  50. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  51. <version>${spring-cloud-alibaba.version}</version>
  52. <type>pom</type>
  53. <scope>import</scope>
  54. </dependency>
  55. <dependency>
  56. <groupId>org.springframework.cloud</groupId>
  57. <artifactId>spring-cloud-dependencies</artifactId>
  58. <version>Hoxton.SR9</version>
  59. <type>pom</type>
  60. <scope>import</scope>
  61. </dependency>
  62. <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
  63. <dependency>
  64. <groupId>com.alibaba</groupId>
  65. <artifactId>fastjson</artifactId>
  66. <version>1.2.75</version>
  67. </dependency>
  68. </dependencies>
  69. </dependencyManagement>

启动类

  1. @SpringBootApplication
  2. @EnableDiscoveryClient
  3. public class SpringbootsentinelgatewayApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringbootsentinelgatewayApplication.class, args);
  6. }
  7. }

bootstrap.properties

  1. # Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
  2. # Nacos认证信息
  3. spring.cloud.nacos.config.username=nacos
  4. spring.cloud.nacos.config.password=nacos
  5. spring.cloud.nacos.config.contextPath=/nacos
  6. # 设置配置中心服务端地址
  7. spring.cloud.nacos.config.server-addr=localhost:8848
  8. # Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
  9. spring.cloud.nacos.discovery.server-addr=localhost:8848
  10. # 注册到 nacos 的指定 namespace,默认为 public
  11. spring.cloud.gateway.routes[0].id=springbootsentinel
  12. spring.cloud.gateway.routes[0].uri=lb://springbootsentinel
  13. spring.cloud.gateway.routes[0].predicates[0]=Path=/hello/**,/search/**
  14. server.port=8083

application.properties

  1. # 应用名称
  2. spring.application.name=springbootsentinelgateway

集成Sentinel

image.png
如果想要让微服务网关集成Sentinel,需要引入依赖包,使用时只需注入对应的SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。首先在 gateway 中引入如下依赖:

  1. <!--Sentinel-->
  2. <dependency>
  3. <groupId>com.alibaba.csp</groupId>
  4. <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
  5. <version>1.8.1</version>
  6. </dependency>

实例引入:创建配置类 com.itheima.config.GatewayConfiguration :

  1. @Configuration
  2. public class GatewayConfiguration {
  3. private final List<ViewResolver> viewResolvers;
  4. private final ServerCodecConfigurer serverCodecConfigurer;
  5. public GatewayConfiguration(ObjectProvider<List<ViewResolver>>
  6. viewResolversProvider,
  7. ServerCodecConfigurer serverCodecConfigurer) {
  8. this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
  9. this.serverCodecConfigurer = serverCodecConfigurer;
  10. }
  11. /***限流的异常处理器
  12. * @return
  13. */
  14. @Bean
  15. @Order(Ordered.HIGHEST_PRECEDENCE)
  16. public SentinelGatewayBlockExceptionHandler
  17. sentinelGatewayBlockExceptionHandler() {
  18. return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
  19. }
  20. /**
  21. * Sentinel路由处理核心过滤器
  22. * @return
  23. */
  24. @Bean
  25. @Order(-1)
  26. public GlobalFilter sentinelGatewayFilter() {
  27. return new SentinelGatewayFilter();
  28. }
  29. }

此时集成就完成了。

配置/API定义

API定义

ApiDefinition 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如可以定义一个 API 叫 my_api ,请求 path 模式为 /foo/ 和 /baz/ 的都归到 my_api 这个API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
在配置类 com.itheima.config.GatewayConfiguration 创建Api:

  1. /**
  2. * Api定义
  3. */
  4. private void initCustomizedApis() {
  5. Set<ApiDefinition> definitions = new HashSet<>();
  6. ApiDefinition cartApi = new ApiDefinition()
  7. .setPredicateItems(new HashSet<>(){{
  8. add(new ApiPathPredicateItem().setPattern("/hello/**"));
  9. add(new ApiPathPredicateItem().setPattern("/car/model/**"));
  10. add(new ApiPathPredicateItem().setPattern("/trip/message")
  11. .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
  12. //参数值的匹配策略
  13. // 精确匹配(PARAM_MATCH_STRATEGY_EXACT)
  14. // 子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)
  15. // 正则匹配(PARAM_MATCH_STRATEGY_REGEX)
  16. }});
  17. definitions.add(cartApi);
  18. //加载API
  19. GatewayApiDefinitionManager.loadApiDefinitions(definitions);
  20. }

规则创建

GatewayFlowRule 网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route
或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
在配置类 com.itheima.config.GatewayConfiguration 创建配置:

  1. /**
  2. * 规则定义
  3. */
  4. private void initGatewayRules(){
  5. //网关限流规则
  6. Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();
  7. //资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
  8. rules.add(new GatewayFlowRule("springbootsentinel")
  9. //限流阈值
  10. .setCount(2)
  11. //应对突发请求时额外允许的请求数目。
  12. .setBurst(2)
  13. //统计时间窗口,单位是秒,默认是 1 秒。
  14. .setIntervalSec(1)
  15. //限流行为
  16. //CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
  17. //CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
  18. .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
  19. //匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
  20. .setMaxQueueingTimeoutMs(1000)
  21. );
  22. //API 【mall_cart_api】限流配置
  23. rules.add(new GatewayFlowRule("hailtaxi_driver_api")
  24. //限流阈值
  25. .setCount(3)
  26. //统计时间窗口,单位是秒,默认是 1 秒。
  27. .setIntervalSec(1)
  28. );
  29. //加载网关规则
  30. GatewayRuleManager.loadRules(rules);
  31. }

配置/API加载

上面两个方法创建了API并且创建了配置,但并没有在程序启动加载,可以采用 @PostConstruct
注解实现加载调用,在 com.itheima.config.GatewayConfiguration 中创建方法:

  1. /***
  2. *初始化加载Api和规则
  3. */
  4. @PostConstruct
  5. public void doInit() {
  6. initCustomizedApis();
  7. initGatewayRules();
  8. }

Sentinel控制台

Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点
链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效
果。

Sentinel控制台的安装

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规
则管理和推送的功能。
Sentinel 控制台包含如下功能:

  1. 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
  2. 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,
    最终可以实现秒级的实时监控。
  3. 规则管理和推送:统一管理推送规则。
  4. 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装,为了方便操作,我们这里
采用docker安装方式:

  1. docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=always
  2. bladex/sentinel-dashboard

安装好了后,可以直接访问 http://192.168.211.145:8858 访问控制台,默认用户名和密码都是
sentinel :
image.png
登录后,效果如下:
image.png
本地安装,下载jar包,sentinel-dashboard-1.8.0.jar,下载1.8.0
启动

  1. java -jar -Dserver.port=8858 sentinel-dashboard-1.8.1.jar

登录效果如下:
image.png
客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信,可以通过 pom.xml 引入 JAR 包:

  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-transport-simple-http</artifactId>
  4. <version>x.y.z</version>
  5. </dependency>

如果是SpringBoot工程接入Sentinel,可以直接引入如下依赖包:

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. <version>2.2.6.RELEASE</version>
  5. </dependency>

接入控制台

将Gateway微服务网关接入Sentinel控制台,首先引入依赖包:

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. <version>2.2.6.RELEASE</version>
  5. </dependency>

在核心配置文件中配置Sentinel服务地址

  1. # Sentinel 控制台地址
  2. spring.cloud.sentinel.transport.dashboard=localhost:8858
  3. #sentinel会开启一个端口号 默认为8719
  4. spring.cloud.sentinel.transport.port=8719

这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 HttpServer,该 Server 会与 Sentinel 控制台做交互,比如限流规则拉取。此时出发一些请求操作,再看Sentinel控制台会多一个服务控:
image.png

可视化管理

实时监控

同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在”实时监控”下。注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
image.png
如果要获取监控数据,直接调用 http://localhost:8719/clusterNode 即可获取,效果如下:
image.png

流控规则

可以在【流控规则】页面中新增,点击【流控规则】进入页面新增页面,如下图:
image.png
资源名:其实可以和请求路径保持一致,这里的流控模式为QPS,触发流控执行阈值为1,流控模式为让当前请求的资源快速直白。
测试效果如下:
image.png
这里的参数和我们程序中的参数其实是一样的,如下说明

  1. resource:资源名,即限流规则的作用对象
  2. count: 限流阈值
  3. grade: 限流阈值类型(QPS 或并发线程数)
  4. limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  5. strategy: 调用关系限流策略
  6. controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

流控效果和程序中也是一样的:

  1. 快速失败:
  2. QPS超过任何规则的阈值后,新的请求就会立即拒绝,拒绝方式为抛出FlowException . 这种方
  3. 式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
  4. Warm Up
  5. 当系统长期处理低水平的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压
  6. 垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值的上限,给系统一个预热的时
  7. 间,避免冷系统被压垮。
  8. 排队等待:
  9. 匀速排队严格控制请求通过的时间间隔,也即是让请求以均匀的速度通过,对应的是漏桶算法。

降级规则

可以选择 降级规则>新增降级规则 ,如下图:
image.png
降级规则的熔断策略有3种,分别是慢调用比例、异常比例、异常数,和程序中是一样的。

热点数据

热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访
问进行限制。
image.png