一、Sentinel介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
官网:https://github.com/alibaba/Sentinel
中文文档:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
Sentinel 分为两个部分:
核心库(Java 客户端) 不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard) 基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
主要用途:围绕资源的实时状态设定规则,所有规则都可以进行动态实时调整。如服务降级熔断、系统流量控制、系统自适应过载保护等。
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
sentinel整合ribbon+openFeign+fallback
ribbon讲解:https://blog.csdn.net/weixin_46204877/article/details/126860738
二、Sentinel安装
官网下载:https://github.com/alibaba/Sentinel/releases
官网有时候下载慢,下面是我放在百度网盘里面的jar包
链接:https://pan.baidu.com/s/1J_RK2znVaYyNPQ7oBdxK9Q
提取码:jkda
我下载的版本是1.7.2
在该文件目录上直接敲cmd,然后命令 java -jar sentinel-dashboard-1.7.2.jar 启动后访问 http://localhost:8080/ 会出现Sentinel的登录界面,用户名和密码都是sentinel,登录成功调转欢迎界面。
三、Sentinel使用
使用中用到了Nacos,需了解看 https://blog.csdn.net/weixin_46204877/article/details/127309975
创建Module
在pom.xml文件中添加依赖
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.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扫描。直至找到未被占用的端口
management:
endpoints:
web:
exposure:
include: '*'
主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class AlibabaSentinelService8401Application {
public static void main(String[] args) {
SpringApplication.run(AlibabaSentinelService8401Application.class, args);
}
}
FlowLimitController类
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "------testA";
}
// 测试QPS阈值类型
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
启动sentinel,然后启动8401,访问 http://localhost:8401/testA 出现返回值则成功。
3.1流控
这里要注意,服务启动后,sentinel不会自动获取该服务,需要在浏览器调用几次才会出现在sentinel控制界面。
当8401启动成功并出现在sentinel界面,就可以设置服务的对应配置。先测试流控下的功能。
点击触点链路–>/testA后面选择流控或者点击侧边栏的流控规则–>右上角新增流控规则
参数说明
资源名: 一般是 /+方法名。
**针对来源:**一般默认(default)
阈值类型: QPS(指每秒请求数) 线程数(指每秒线程请求数) 单机阈值(配合阈值类型使用,比如下图阈值类型为QPS,单机阈值为1,表示每秒的承载请求数最大为1,超过请求数为1则报错)。
是否集群: 有就勾上,没有就不勾。
流控模式: 直接(直接抛出异常或报错)、关联(选择关联会配置一个关联的资源,一般就是对应的另一个请求)、链路。
流控效果: 快速失败(直接打印异常或错误)、Warm UP(预热)、排队等待。一般和流控模式一起使用。
直接(默认)
直接->快速失败(默认的流控处理)
配置说明:当每秒请求为1次时,不会有问题。当每秒请求超过1次,会直接报错并在页面打印错误信息。
直接->Warm Up(预热)
默认coldFactor为 3,即请求QPS从(threshold / 3)开始,经多少预热时长才逐渐升至设定的QPS阈值。
配置说明: 阀值为10 + 预热时长设置5秒。系统初始化的阀值为10/3约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10。
场景:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。
直接->排队等待
匀速排队( RuleConstant .CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接节来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒真接拒绝多余的请求。
配置说明: 每秒请求为1次不会触发,当每秒请求超过1次,超过的请求被等待处理。
关联
当关联的资源达到阈值时,就限流自己。比如testA关联testB,当testB达到阈值,那testA是不能被访问的。
这里通过postman对testB进行密集访问,也可以通过Jmeter密集访问。
3.2降级
Sentinel的断路器是没有半开状态的,半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。
基本介绍
RT(平均响应时间,秒级)
平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级窗口期过后关i闭断路器,RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
异常比列(秒级)
QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级。
RT(平均响应时间,秒级)
平均响应时间( DEGRADE_GRADE_RT):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值( count,以ms为单位),那么在接下的时间窗口( DegradeRule中的timewindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException )。注意Sentinel默认统计的RT上限是4900 ms,超出此阈值的都会算作4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置。
controller添加测试方法
@GetMapping("/testD")
public String testD() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("testD 测试RT");
return "------testD";
}
添加sentinel降级配置
这里通过postman对testD进行密集访问,也可以通过Jmeter密集访问。
然后访问 http://localhost:8401/testD,快速刷新超过几次后会报错,等待时间窗口时间过后,灰恢复正常访问。
按照上述配置,
循环一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了,后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复正常访问。
异常比例(秒级)
异常比例(DEGRADE_GRADE_EXCEPTION_RATIo ):当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule中的 timewindow,以s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0% -100%。
在controller添加测试方法
@GetMapping("/testE")
public String testE() {
int age = 10 / 0;
return "------testE 测试异常数";
}
sentinel降级中配置testE方法规则
访问 http://localhost:8401/testE 会出现int age = 10 / 0;报错页面,多次刷新后达到配置条件,断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了。
异常数(分钟级)
异常数(DEGRADE_GRADE_EXCEPTION COUNT ): 当资源近1分钟的异常数目超过值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timewindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
注:时间窗口一定要大于60s,异常数是按照分钟统计的
测试代码还是用testE方法
sentinel添加testE方法降级配置
配置说明:当请求超过完次错误后,熔断器打开,进行服务降级。
访问 http://localhost:8401/testE 页面会显示错误,因为除数不能为零,我们看到error窗口,但是达到5次报错后,进入降级方法,当过了设置的时间窗口期,就恢复正常访问(int age = 10 / 0;错误页面)。
3.3热点key限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
添加controller测试方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
int age = 10 / 0;
return "------testHotKey";
}
//降级兜底方法
public String deal_testHotKey(String p1, String p2, BlockException exception) {
//sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
return "------deal_testHotKey,o(╥﹏╥)o";
}
sentinel添加 testHotKey 热点限流规则
配置说明:为testHotKey 请求的第一个参数设置限流,超过每秒一个请求就触发服务降级,窗口时间为1秒。
访问 http://localhost:8401/testHotKey?p1=abc 多刷新几次会出现降级方法。
如果访问 http://localhost:8401/testHotKey?p2=abc 可以正常访问(int age = 10 / 0;错误页面)。
参数例外项
热点限流这有个参数例外项,可以实现比如让第一个参数的值等于5的时候才进行降级。
testHotKey 热点限流规则中添加参数例外项。
在去访问http://localhost:8401/testHotKey?p1=abc时,不会出现降级,但访问http://localhost:8401/testHotKey?p1=5时,会出现服务降级。
3.4系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU使用率、平均RT、入口QPS和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流星(EntryType.IN ),比如 Web服务或Dubbo服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护。当系统 load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护((BBR阶段)。系统容量由系统的maxQps * minRt估算得出。设定参考值一般是CPU cores * 2.5。
cpu使用率(1.5.0+版本):当系统cpu使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较灵敏。
平均RT:当单台机器上所有入口流星的平均RT达到阈值即触发系统保护,单位是毫秒。
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。
四、@SentinelResource 注解
Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandler 和 fallback 函数来进行限流之后的处理。
@SentinelResource 注解是 Sentinel 提供的最重要的注解之一,它还包含了多个属性,如下表:
属性 | 说明 | 必填与否 | 使用要求 |
---|---|---|---|
value | 用于指定资源的名称 | 必填 | - |
entryType | entry 类型 | 可选项(默认为 EntryType.OUT) | - |
blockHandler | 服务限流后会抛出 BlockException 异常,而 blockHandler 则是用来指定一个函数来处理 BlockException 异常的。 简单点说,该属性用于指定服务限流后的后续处理逻辑。 | 可选项 | 1.blockHandler 函数访问范围需要是 public 2.返回类型需要与原方法相匹配; 3.参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException; 4.blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
blockHandlerClass | 若 blockHandler 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 | 1.不能单独使用,必须与 blockHandler 属性配合使用; 2.该属性指定的类中的 blockHandler 函数必须为 static 函数,否则无法解析。 |
fallback | 用于在抛出异常(包括 BlockException)时,提供 fallback 处理逻辑。 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 | 可选项 | 1.返回值类型必须与原函数返回值类型一致; 2.方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常; 3.fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
fallbackClass | 若 fallback 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 | 可选项 | 1.不能单独使用,必须与 fallback 或 defaultFallback 属性配合使用; 2.该属性指定的类中的 fallback 函数必须为 static 函数,否则无法解析。 |
defaultFallback | 默认的 fallback 函数名称,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。 默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 | 可选项 | 1.返回值类型必须与原函数返回值类型一致; 2.方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常; 3.defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
exceptionsToIgnore | 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 | 可选项 | - |
注:在 Sentinel 1.6.0 之前,fallback 函数只针对降级异常(DegradeException)进行处理,不能处理业务异常。 |
---|
@SentinelResource(value = “fallback”, fallback= “handlerFallback”) //fallback只负责业务异常,value可以是任意值。
@SentinelResource(value = “fallback”,blockHandler = “blockHandler”) //blockHandler只负责sentinel控制台配置违规,value可以是任意值。
服务熔断测试
在controller添加测试方法
CommonResult这是自定义返回类,换成自己的就行了。
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handlerException2")
public CommonResult customerBlockHandler() {
return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
}
这里自定义业务类CustomerBlockHandler
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----2");
}
}
sentinel设置改方法降级规则
注:设置到最后的那个方法名
访问 http://localhost:8401/rateLimit/customerBlockHandler 多刷新几次会看到自定义的错误提示。
如果将 blockHandler = "handlerException2"改为 blockHandler = "handlerException"刷新访问。
五、规则持久化
我们发现一旦重启应用,Sentinel规则将消失,如果想使用,就需要重新配置sentinel规则,很麻烦,所以生产环境需要将配置规则进行持久化。
1.在application.yml中添加Nacos数据源配置
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr:localhost:8848
dataid:${spring.application.name}
groupid:DEFAULT_GROUP
data-type:json
rule-type:flow
2.在nacos中为cloudalibaba-sentinel-service(可以在配置文件自定义)服务添加对应配置。
参数解析
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource: 资源名称;
imitApp: 来源应用;
grade: 闻值类型,0表示线程数,1表示QPS;count: 单机闻值;
strategy: 流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior: 流控效果,0表示快速失败,1表示。Warm Up,2表示排队等待。
clusterMode: 是否集群。
然后重启nacos发现该服务sentinel规则存在。