目录
概述
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
一句话简单概括,Sentinel是一种流量治理的组件,作用等价于Spring Cloud Circurk Breaker。
特征
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
基本概念
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。(Sentinel控制台、yml配置、java代码都可设定规则)
安装Sentinel
Sentinel 的使用可以分为两个部分:
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。
下载路径:https://github.com/alibaba/Sentinel/releases
下载完后找到jar包,运行命令启动Sentinel控制台
java -jar sentinel-dashboard-xxx.jar
本地环境必须要jdk8版本以上,且8080端口未被占用(Sentinel 控制台端口)
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel,访问网址:http://localhost:8080
微服务引入Sentinel案例
新建一个微服务,引入Nacos和Sentinel,将服务注册进Nacos,对服务进行流量监控和熔断降级
引入依赖
<!--SpringCloud alibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改yml配置
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
启动类
@EnableDiscoveryClient
@SpringBootApplication
public class Main8401
{
public static void main(String[] args)
{
SpringApplication.run(Main8401.class,args);
}
}
业务类
@RestController
public class FlowLimitController
{
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
return "------testB";
}
}
启动8401服务,这时候查看Sentinel控制台发现什么都没有,因为实际接口并未访问,Sentinel采用的是懒加载,不访问不监控,所以请求接口,http://localhost8401/testA,http://localhost:8401/testB,效果如下图
流控规则(流量控制)
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性,参数如下
参数 | 含义 |
---|---|
资源名 | 资源的唯一名称,默认就是请求的接口路径,可以自行修改,但是要保证唯一。 |
针对来源 | 具体针对某个微服务进行限流,默认值为default,表示不区分来源,全部限流。 |
阈值类型 | QPS表示通过QPS进行限流,并发线程数表示通过并发线程数限流。 |
单机阈值 | 与阈值类型组合使用。如果阈值类型选择的是QPS,表示当调用接口的QPS达到阈值时,进行限流操作。如果阈值类型选择的是并发线程数,则表示当调用接口的并发线程数达到阈值时,进行限流操作。 |
是否集群 | 选中则表示集群环境,不选中则表示非集群环境。 |
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS,其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制
流控模式有三种:直接、关联、链路
流控模式-直接
默认的流控模式,当接口达到限流条件时,直接开启限流功能
表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
快速多次调用http://localhost8401/testA,会出现Blocked by Sentinel (flow limiting),限流成功
流控模式-关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。
简单来说就是:B惹事,A挂了
控制台演示配置:当配置关联资源B的qps阈值超过1时,就限流A的访问地址
使用jmeter设置并发3秒内疯狂访问http://localhost:8401/testB,在不启动jmeter之前访问http://localhost:8401/testA,返回正常,这时候启动jmeter
这时候再访问/testA,会发现返回Blocked by Sentinel (flow limiting),等到jmeter调用结束后再访问/testA服务限流结束可正常访问,效果正确则配置成功
在这里我理解的是,AB服务之间,会优先确保B服务正常的一种限流策略,实际使用场景根据业务要求来设置。
流控模式-链路
来自不同链路请求对同一个目标访问时,实施针对性的限流措施,比如C来访问就限流,D来访问不限流
修改8401微服务的yml,关键配置【web-context-unify】默认true,设置为false
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service #8401微服务提供者后续将会被纳入阿里巴巴sentinel监管
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard控制台服务地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
新建service
@Service
public class FlowLimitService
{
@SentinelResource(value = "common")
public void common()
{
System.out.println("------FlowLimitService come in");
}
}
controller新增方法
/**流控-链路演示demo
* C和D两个请求都访问flowLimitService.common()方法,阈值到达后对C限流,对D不管
*/
@Resource private FlowLimitService flowLimitService;
@GetMapping("/testC")
public String testC()
{
flowLimitService.common();
return "------testC";
}
@GetMapping("/testD")
public String testD()
{
flowLimitService.common();
return "------testD";
}
控制台新增配置,C和D两个请求都访问flowLimitService.common()方法,对C限流,对D不管
访问http://localhost:8401/testC,超过一秒钟一次后,就发生限流
http://localhost:8401/testD,正常访问
流控效果-快速失败
快速失败也是直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式,是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
上面几个流控规则演示的就是快速失败案例。
流控效果-预热WarmUp
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
它的实现是在 Guava 的算法的基础上实现的。默认 coldFactor(冷却因子) 为 3(默认值),即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
源码
案例,单机阈值为10,预热时长设置5秒。
系统初始化的阈值为10 / 3 约等于3,即单机阈值刚开始为3(我们人工设定单机阈值是10,sentinel计算后QPS判定为3开始);
然后过了5秒后阀值才慢慢升高恢复到设置的单机阈值10,也就是说5秒钟内QPS为3,过了保护期5秒后QPS为10
控制台配置
多次点击http://localhost:8401/testB,刚开始不行(被限流了),后面慢慢恢复就ok了
应用场景如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。
流控效果-排队等候
它的中心思想是,以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。
这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,而不是拒绝所有请求。
例如,如果系统使用 Apache RocketMQ 来收发消息,系统在某个时间突然收到大量消息。我们希望以固定的速率来处理消息,而不是一下子拒绝这些消息。这个时候可以使用匀速器,也就是给消息排队。效果如下所示:
Sentinel 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
8401服务controller代码新增
@GetMapping("/testE")
public String testE()
{
System