一、前言
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel 的历史:
- 2012 年,Sentinel 诞生,主要功能为入口流量控制。
- 2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景。Sentinel 也因此积累了大量的流量归整场景以及生产实践。
- 2018 年,Sentinel 开源,并持续演进。
- 2019 年,Sentinel 朝着多语言扩展的方向不断探索,推出 C++ 原生版本,同时针对 Service Mesh 场景也推出了 Envoy 集群流量控制支持,以解决 Service Mesh 架构下多语言限流的问题。
- 2020 年,推出 Sentinel Go 版本,继续朝着云原生方向演进。
- 2021 年,Sentinel 正在朝着 2.0 云原生高可用决策中心组件进行演进;同时推出了 Sentinel Rust 原生版本。同时我们也在 Rust 社区进行了 Envoy WASM extension 及 eBPF extension 等场景探索。
- 2022 年,Sentinel 品牌升级为流量治理,领域涵盖流量路由/调度、流量染色、流控降级、过载保护/实例摘除等;同时社区将流量治理相关标准抽出到 OpenSergo 标准中,Sentinel 作为流量治理标准实现。
--以上来自Sentinel官方文档;introduction | Sentinel
二、Sentinel 基本概念
1、资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
2、规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
三、在Linux下安装 Sentinel
1、安装包下载
2、拷贝到Linux服务器上并启动服务
启动命令:(指定端口号 8849)
nohup java -Dserver.port=8849 -Dcsp.sentinel.dashboard.server=localhost:8849 -Dproject.name=sentinel-dashboard -jar /home/springcloud/sentinel-dashboard-1.7.0.jar &
浏览器登录sentinel前端页面,地址:IP:8849
【注】访问失败的解决方法:
- 查看 8849 端口号是否开放:firewall-cmd --query-port=8849/tcp
- 打开端口:firewall-cmd --zone=public --add-port=8849/tcp --permanent
- 重启防火墙:firewall-cmd --reload
3、重新登录,用户名密码都是 sentinel
四、Springcloud 整合 Sentinel
1、pom.xml中引入依赖包
java
<properties>
<java.version>8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>2.3.2.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR9</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version>
</properties>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
2、在application.yml中添加配置,指定Sentinel的服务地址
java
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.1.233:8849
3、创建测试接口
java
package com.example.springclouduser.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/queryUserInfoById/{userId}")
public String queryUserInfoById(@PathVariable Long userId) {
return "接口名:[queryUserInfoById] 调用成功,用户ID:" + userId;
}
}
4、浏览器调用接口,在 Sentienl 的簇点链路中就会显示
到此,证明本地已经和 Sentinel 服务连接上了。
五、Sentinel各个功能的使用
1、流控规则
- 资源名:唯一名称,默认请求路径。
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。
- 阈值类型/单机阈值:
- QPS :每秒钟的请求数量,当调用该API的QPS达到阈值的时候,进行限流。 线程数:当调用该API的线程数达到阈值的时候,进行限流。
- 是否集群:不需要集群
- 流控模式:
- 直接 :API达到限流条件时,直接限流。
关联 :当关联的资源达到阈值时,就限流当前资源。
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】。 - 流控效果:
- 快速失败 :直接失败,抛出异常
Warm up :根据Code Factor的值(冷加载因子,默认3),从阈值/code Factor,经过预热时长,才达到设置的QPS阈值。
排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效。
1.1 QPS + 直接 + 快速失败
当接口 /user/queryUserInfoById 每秒访问数超过3个,直接报错。
测试结果:
当 QPS 超过阈值,报错:
1.2 QPS + 关联 + 快速失败
当关联的资源 /user/secondMethod 接口每秒访问数超过3个,会使 /user/queryUserInfoById 接口限流。
测试流程:
(1)使用 Jmeter 压测工具,每秒钟并发发送四个请求
此时 接口 /user/secondMethod 的 QPS 阈值超过 3,再访问接口 /user/queryUserInfoById 时被限流:
1.3 QPS + 链路 + 快速失败
只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)[api级别的针对来源]
/user/queryUserInfoById 接口属于 sentinel_spring_web_context 链路下,因为入口资源填写 sentinel_spring_web_context。
再次访问 /user/queryUserInfoById 接口发现被限流:
1.4 QPS + 直接 + Warm Up
请求QPS从设定的阈值/3开始,经预热时长逐渐升至设定的QPS阈值
如下图所示,QPS开始时为 3/coldFactor = 3/3 = 1 (冷因子coldFactor默认为3)
经过5秒后,QPS 达到最终设定的阈值3。
1.5 QPS + 直接 + 排队等待
接口 queryUserInfoById 每秒访问1次,多余的请求排队等待,之后匀速访问接口,超时时间为2秒。
为接口queryUserInfoById加上日志,查看日志打印,是否相隔1秒后调用。
java
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/queryUserInfoById/{userId}")
public String queryUserInfoById(@PathVariable Long userId) {
String message = "接口名:[queryUserInfoById] 调用成功,用户ID:" + userId;
log.info("当前线程:[{}]", Thread.currentThread().getName());
return message;
}
}
测试结果:
以上每种模式的例子只找了一种,感兴趣的同学可以试试其他的模式;
2、降级规则
2.1 RT:平均响应时间
当资源的平均响应时间超过阈值(DegradeRule 中的 count,以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求(即 QPS >= 5),它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。
**【注】**RT默认最大4900ms,超过此阈值的也会算作 4900ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx
来配置
测试过程:
(1)创建降级规则:当RT= 3000ms,时间窗口为 5s 。
(2)修改接口,添加 6s的响应时间
java
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/queryUserInfoById/{userId}")
public String queryUserInfoById(@PathVariable Long userId) {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = "接口名:[queryUserInfoById] 调用成功,用户ID:" + userId;
log.info("当前线程:[{}]", Thread.currentThread().getName());
return message;
}
}
(3)jmeter模拟每秒10个请求的并发环境,调用接口,发现接口返回报错:Blocked by Sentinel(flow limiting)
(4)注释掉响应时间6s,同样使用jmeter调用接口,发现接口返回正常。
2.2 异常比例
当资源的每秒异常总数占通过量的比值超过阈值(阈值范围是 [0.0, 1.0],代表 0% - 100%。)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。
2.3 异常数
当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
3、热点规则
热点规则限流只支持 QPS 模式,也只有 QPS模式下才叫热点;
参数索引:调用方法所传参数的索引,0代表第一个参数,1代表第二个参数,以此类推;
单机阈值以及统计窗口时长表示在此窗口时间内,访问数超过阈值数量就触发限流。
4、系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效 。入口流量指的是进入应用的流量(EntryType.IN
),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
- Load 自适应 (仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。