Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

官网:https://github.com/alibaba/Sentinel

中文官网:https://github.com/alibaba/Sentinel/wiki

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:

  

Sentinel 的开源生态:

  

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

Sentinel 重要概念

定义资源

  资源 是 Sentinel 中的核心概念之一。最常用的资源是我们代码中的 Java 方法。 当然,您也可以更灵活的定义你的资源,例如,把需要控制流量的代码用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包围起来即可。在下面的例子中,我们将 System.out.println("hello world"); 作为资源(被保护的逻辑),用 API 包装起来。参考代码如下:

复制代码
 1 public static void main(String[] args) {
 2     // 配置规则.
 3     initFlowRules();
 4 
 5     while (true) {
 6         // 1.5.0 版本开始可以直接利用 try-with-resources 特性,自动 exit entry
 7         try (Entry entry = SphU.entry("HelloWorld")) {
 8             // 被保护的逻辑
 9             System.out.println("hello world");
10     } catch (BlockException ex) {
11             // 处理被流控的逻辑
12         System.out.println("blocked!");
13     }
14     }
15 }
复制代码

也可以通过我们提供的 注解支持模块,来定义我们的资源,类似于下面的代码:

1 @SentinelResource("HelloWorld")
2 public void helloWorld() {
3     // 资源中的逻辑
4     System.out.println("hello world");
5 }

这样,helloWorld() 方法就成了我们的一个资源。注意注解支持模块需要配合 Spring AOP 或者 AspectJ 一起使用。

定义规则

  接下来,通过流控规则来指定允许该资源通过的请求次数,例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。

复制代码
 1 private static void initFlowRules(){
 2     List<FlowRule> rules = new ArrayList<>();
 3     FlowRule rule = new FlowRule();
 4     rule.setResource("HelloWorld");
 5     rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
 6     // Set limit QPS to 20.
 7     rule.setCount(20);
 8     rules.add(rule);
 9     FlowRuleManager.loadRules(rules);
10 }
复制代码

  完成上面 ,Sentinel 就能够正常工作了。更多的信息可以参考官网。

Sentinel使用

  案例架构图如下:

  

搭建Sentinel控制台

  1、下载sentienl的jar包,本例使用:sentinel-dashboard-1.7.2.jar,地址:https://github.com/alibaba/Sentinel/releases

  2、使用java -jar命令启动Sentinel控制台,注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。

      命令格式:java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

    如若8080端口冲突,可使用 -Dserver.port=新端口 进行设置。

    命令:java -jar sentinel-dashboard-1.7.2.jar

    

  3、访问地址:http://localhost:8080/,8080为Sentinel的默认端口

    

  4、输入默认用户名/密码:sentinel/sentinel,进入首页

    

搭建Sentinel客户端

  1、新建项目Spring Cloud项目(springcloud-sentinel-service8401)

    

  2、编辑pom.xml文件,引入依赖

    <dependencies>

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

    </dependencies>

 

  3、编辑application.yml文件

# 端口
server:
  port: 8401
spring:
  application:
    name: alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.186.128:8848
    sentinel:
      transport:
        # 配置Sentinel DashBoard地址
        dashboard: 192.168.186.128:8998
        # 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
        # 默认8719端口,假如端口被占用,依次+1,直到找到未被占用端口
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: '*'

 

  4、编辑主启动类

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

  5、编辑一个Controller

复制代码
 1 @RestController
 2 public class FlowLimitController {
 3 
 4     @GetMapping("/testA")
 5     public String testA(){
 6         return "--------testA";
 7     }
 8 
 9     @GetMapping("/testB")
10     public String testB(){
11         return "--------testB";
12     }
13 }
复制代码

  6、测试

    1)启动项目,启动Nacos服务(【SpringCloud】Spring Cloud Alibaba 之 Nacos注册中心(二十七)

      启动Sentinel控制台

    2)在浏览器中访问地址:http://localhost:8401/testA

    3)查看Sentinel控制台-〉选择alibaba-sentinel-service服务-〉实时监控-〉可以看到监控详情

      

    4)同时可以查看簇点链路

      

    5)同时可以查看机器列表

      

 

Sentinel流量控制介绍

  流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

  FlowSlot 会根据预设的规则,结合前面 NodeSelectorSlotClusterNodeBuilderSlotStatisticSlot 统计出来的实时信息进行流量控制。

  限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName) 的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉 BlockException 来自定义被限流之后的处理逻辑。

  同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。

  一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

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

   以下是关于流量控制中名词解析

复制代码
资源名:唯一名称、默认请求路径

针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

阀值类型/单机阀值:
	
	QPS(每秒钟的请求数量):当调用该api的QPS达到阀值的时候,进行限流
	线程数:当调用该api的线程数达到阀值的时候,进行限流

是否集群:不需要集群

流控模式:
	
	直接:api达到限流条件时,直接限流
	关联:当关联的资源达到阀值时,就限流自己
	链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阀值,就限流)【api级别的针对来源】

流控效果:

	快速失败:直接失败,抛异常
	Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阀值/codeFactor,经过预热时长,才达到设置的QPS阀值
	排队等待:匀速排队,让请求以均匀的速度通过,阀值类型必须设置为QPS,否则无效
复制代码

基于QPS的流量控制

  当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。对应 FlowRule 中的 controlBehavior 字段。

  1、搭建项目,Controller内容如下:

复制代码
 1 @RestController
 2 public class FlowLimitController {
 3 
 4     @GetMapping("/testA")
 5     public String testA(){
 6         return "--------testA";
 7     }
 8 
 9     @GetMapping("/testB")
10     public String testB(){
11         return "--------testB";
12     }
13 }
复制代码

 

   2、将项目启动,以及Sentinel启动,添加流控规则,如下:

    

  3、设置流控规则,每秒钟的请求数量为1,流控效果为快速失败,如下,保存后,即生效

    

  4、测试

    1)浏览器访问地址:http://localhost:8401/testA,正常访问

      

    2)快速点击浏览器刷新按钮,刷新界面,页面显示:Blocked by Sentinel (flow limiting)

      

基于并发数的流量控制

  1、继续使用以上项目,修改testA方法,如下:

复制代码
1 @GetMapping("/testA")
2 public String testA(){
3     try {
4         Thread.sleep(800);
5     } catch (InterruptedException e) {
6         e.printStackTrace();
7     }
8     return "--------testA";
9 }
复制代码

 

  2、编辑流量规则,将流控规则改为当调用该api的线程数达到1的时候,进行限流,如下:

    

  3、测试

    1)重新启动项目

    2)使用浏览器单次请求,http://localhost:8401/testA,正常显示

    2)使用JMeter进行并发测试请求:http://localhost:8401/testA,响应内容为:Blocked by Sentinel (flow limiting)

流控模式:直接

  上面2个例子,流控模式都是直接,对自己本身资源的限制。 

流控模式:关联

  当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。像对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。

  示例

  1、项目代码还是以上的项目代码

  2、编辑流量规则,设置资源testA关联的testB,当testB达到阀值时,testA被限流。如下:

    

  3、测试

    1)启动项目

    2)使用浏览器请求,http://localhost:8401/testA,正常显示

    3)使用JMeter不停的循环请求:http://localhost:8401/testB,testB请求结果都正确

    4)然后使用浏览器请求,http://localhost:8401/testA,无法正常显示,资源限制

流控模式:链路

  1、项目代码还是以上的项目代码,Controllerh中testB方法

1 @GetMapping("/testB")
2 public String testB(){
3     return "--------testB";
4 }

 

  2、查看簇点链路,发现/test,资源入口是sentinel_web_servlet_context

    

  3、删除其他流控规则,新增流量规则,设置资源testB链路限流,如下:、

    

  4、测试

    1)启动项目

    2)使用浏览器请求,http://localhost:8401/testB,正常显示

    3)快速点击浏览器刷新按钮,刷新界面,页面显示:Blocked by Sentinel (flow limiting) 

流控效果:快速失败

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

流控效果:Warm Up(预热)

  Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

  应用场景:

   如:秒杀系统在开启瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值

  案例:

    阀值为10 + 预热时长设置5秒

    系统初始化的阀值为10/3 约等于 3,即阀值刚开始为3;然后过了5秒阀值才慢慢升高恢复到10

  1、项目代码还是以上的项目代码,Controllerh中testC方法

1 @GetMapping("/testC")
2 public String testC(){
3     return "--------testC";
4 }

  2、新增流量规则,设置资源testC,单机阀值为10,流控效果为Warm Up,预热时长为5秒,如下:

    

  3、测试

    1)启动项目

    2)使用浏览器访问地址,http://localhost:8401/testC,正常显示

    3)以每秒5次的刷新,一直刷新界面,效果,可以看到部分请求返回错误

    4)5秒钟过后,还是以以每秒5次的刷新速度,刷新界面,请求正常

流控效果:排队等待

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

  这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

  案例

  1、项目代码还是以上的项目代码,Controllerh中testA方法

复制代码
1 @GetMapping("/testA")
2 public String testA(){
3     try {
4         Thread.sleep(800);
5     } catch (InterruptedException e) {
6         e.printStackTrace();
7     }
8     return "--------testA";
9 }
复制代码

  2、新增流量规则,设置资源testA,流控效果为排队等待,超时时间为20000毫秒,如下:

    

  3、测试

    1)启动项目

    2)使用浏览器访问地址,http://localhost:8401/testA,正常显示

    3)使用JMeter并发10个请求,执行一次,观察10次执行结果

    4)所有请求都成功了,请求响应时间逐渐增加