Sentinel
Sentinel 基础
官网
Github: https://github.com/alibaba/Sentinel
快速开始: https://sentinelguard.io/zh-cn/docs/quick-start.html
中文: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
使用手册
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
Sentinel 是什么
- Sentinel 是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
- Sentinel 的主要特性

sentinel 可以完成的功能: 绿色方框列出的部分
- Sentinel 的开源生态

一句话: Sentinel: 分布式系统的流量防卫兵, 保护你的微服务
Sentinel 核心功能
流量控制
拿旅游景点举个示例,每个旅游景点通常都会有最大的接待量,不可能无限制的放游 客进入,比如长城每天只卖八万张票,超过八万的游客,无法买票进入,因为如果超过 八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情, 并且还会有安全隐患;只卖 N 张票,这就是一种限流的手段
熔断降级
在调用系统的时候,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生 堆积,如下图

熔断降级可以解决这个问题,所谓的熔断降级就是当检测到调用链路中某个资源出现不 稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限 制,让请求快速失败,避免影响到其它的资源而导致级联故障
系统负载保护
根据系统能够处理的请求,和允许进来的请求,来做平衡,追求的目标是在系统不被拖垮的情况下, 提高系统的吞吐率
消息削峰填谷
- 某瞬时来了大流量的请求, 而如果此时要处理所有请求,很可能会导致系统负载过高, 影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没有充分利用系统处理消息的能力
- Sentinel 的 Rate Limiter 模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性
Sentinel 两个组成部分
- 核心库:(Java 客户端)不依赖任何框架/库,能够运行在所有 Java 运行时环境, 对 Spring Cloud 有较好的支持
- 控制台:(Dashboard)基于 Spring Boot 开发,打包后可以直接运行, 不需要额外的 Tomcat 等应用容器
Sentinel 控制台
需求分析/图解
需求: 搭建 Sentinel 控制台,用于显示各个微服务的使用情况
下载
https://github.com/alibaba/Sentinel/releases/tag/v1.8.0

运行
指令:
java -jar sentinel-dashboard-1.8.0.jar
注意: Sentinel 控制台 默认端口是 8080
访问
浏览器: http://localhost:8080
用户/密码都是 sentinel
登录成功后的页面, 目前是空的,因为 sentinel 还没有进行流量监控

注意事项和细节
更改 Sentinel 控制台的端口
java -jar sentinel-dashboard-1.8.0.jar --server.port=9999
Sentinel 监控微服务
需求分析/图解
需求: 使用 Sentinel 控制台对 member-service-nacos-provider-10004 微服务 进行实时监控

当调用了 member-service-nacos-provider-10004 微服务时, 可以监控到请求的 url/QPS/ 响应时间/流量

代码/配置实现
修改 member-service-nacos-provider-10004 的 pom.xml, 引入 alibaba-sentinel
<!--引入alibaba-sentinel starter 即场景启动器 这里我们使用版本仲裁-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 在微服务模块引入 nacos-discovery starter -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改 member-service-nacos-provider-10004 的 application.yml
server:
port: 10004
spring:
application:
name: member-service-nacos-provider #配置应用的名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
# 配置nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos Server的地址
sentinel:
transport:
dashboard: localhost:8080 # 指定 sentinel 控制台的地址
#1. transport.port 端口配置会在被监控的微服务对应主机上启动 Http Server
#2. 该Http Server 会与Sentinel控制台进行交互
#3. 比如sentinel 控制台添加了一个限流规则, 会把规则数据push 给这个Http Server 接收
# Http Server 再将这个规则注册到Sentinel 中
#4. 简单的讲: transport.port 指定被监控的微服务应用于sentinel 控制台交互的端口.
#5. 默认端口是 8719, 假如被占用了, 就会自动的从8719开始依次+1扫描,直到找到一个没有被占用的端口
port: 8719 # 指定端口
# 配置暴露所有的监控点
management:
endpoints:
web:
exposure:
include: '*'
mybatis:
mapper-locations: classpath:mapper/*.xml # 指定mapper.xml文件配置
type-aliases-package: com.lzw.springcloud.entity # 实体类所在包,这样通过类名就可以引用
测试
启动 Nacos Server 8848
启动 Sentinel8080 控制台/Sentinel dashboard
启动 member-service-nacos-provider-10004
浏览器: localhost:10004/member/get/1

进入到 Sentinel 查看实时监控效果, http://localhost:8080/#/dashboard

注意事项和细节
QPS: Queries Per Second(每秒查询率),是服务器每秒响应的查询次数
Sentinel 采用的是懒加载, 只有调用了某个接口/服务,才能看到监控数据
Sentinel 流量控制
规则
普通选项

- 资源名∶唯一名称,默认请求路径
- 针对来源∶Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值∶
QPS(每秒钟的请求数量)∶当调用该 api 的 QPS 达到阈值的时候,进行限流
线程数∶当调用该 api 的线程数达到阈值的时候,进行限流
QPS 和线程数的区别
(1) 对 QPS 而言, 如果在 1 秒内, 客户端发出了 2 次请求, 就达到阈值, 从而限流
(2) 对线程数而言, 如果在 1 秒内, 客户端发出了 2 次请求, 不一定达到线程限制的阈值, 为什么呢? 假设我们 1 次请求后台会创建一个线程, 但是这个请求完成时间是 0.1 秒(可以 视为该请求对应的线程存活 0.1 秒), 所以当客户端第 2 次请求时(比如客户端是在 0.3 秒发 出的), 这时第 1 个请求的线程就已经结束了, 因此就没有达到线程的阈值, 也不会限流
(3)如果 1 个请求对应的线程平均执行时间为 0.1 那么, 就相当于 QPS 为 10
- 是否集群∶不需要集群
高级选项
- 流控模式∶
直接∶ api 达到限流条件时,直接限流
关联∶ 当关联的资源达到阈值时,就限流自己
链路∶ 当从某个接口过来的资源达到限流条件时,开启限流
- 流控效果∶
快速失败∶直接失败,抛异常
Warm Up∶根据 codeFactor(冷加载因子,默认 3)的值,从阈值/codeFactor,经过预热时长,才达到设置的 QPS 阈值
排队等待∶匀速排队,让请求以匀速的速度通过,阈值类型必须设置为 QPS,否则无效
流量控制实例-QPS
需求分析
- 需求: 通过 Sentinel 实现 流量控制
- 当调用 member-service-nacos-provider-10004 的 /member/get/ 接口/API 时,限制 1 秒内最多访问 1 次,否则直接失败,抛异常
配置实现步骤
为/member/get/1 增加流控规则


在流控规则菜单,可以看到新增的流控规则

测试
浏览器输入: http://localhost:10004/member/get/1 , 1 秒钟内访问次数超过 1 次, 页面会出现错误提示

注意事项和细节
- 流量规则改动,实时生效,不需重启微服务 , Sentine 控制台
- 在 sentinel 配置流量规则时,如何配置通配符问题, 比如
/member/get/1
/member/get/2
统一使用一个规则
方案1: 在sentinel中 /member/get?id=1 和 /member/get?id=2 被统一认为是 /member/get 所以只要对/member/get 限流就OK了
//@GetMapping("/member/get/{id}")
//public Result getMemberById(@PathVariable("id") Long id, HttpServletRequest request) {
@GetMapping(value = "/member/get",params = "id")
public Result getMemberById(Long id){
Member member = memberService.queryMemberById(id);
//使用Result把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功 member-service-nacos-provider-10004" , member);
} else {
return Result.error("402","ID= " + id + "不存在");
}
}
然后重新设置规则

方案2: URL资源清洗
可以通过 UrlCleaner 接口来实现资源清洗,也就是对于/member/get/{id}这个 URL,我们可以统一归集到/member/get/*资源下,具体配置代码如下,实现 UrlCleaner 接口, 并重写 clean 方法即可
package com.lzw.springcloud.controller;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
/**
* @author LiAng
*/
@Component
public class CustomUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) { //资源清理 /member/get/1
//isBlank方法就是判断 originUrl!=null && 有长度 && originUrl 不是全部为空格
if(StringUtils.isBlank(originUrl)){
return originUrl;
}
if(originUrl.startsWith("/member/get")){//如果得到url是以/member/get开头,进行处理
//1. 如果请求的接口 是 /member/get 开头的, 比如 /member/get/1 , /member/get/10...
//2. 给sentinel 放回资源名为 /member/get/*
//3. 在sentinel 对 /member/get/* 添加流控规则即可
return "/member/get/*";
}
return originUrl;
}
}
然后重新设置规则,和上文一样
- 如果 sentinel 流控规则没有持久化,当我们重启调用 API 所在微服务模块后,规则会丢失,需要重新加入
流量控制实例-线程数
需求分析
- 需求: 通过 Sentinel 实现 流量控制
- 当调用 member-service-nacos-provider-10004 的 /member/get/* 接口/API 时,限制只有一个工作线程,否则直接失败,抛异常
配置实现步骤
- 为/member/get/* 增加流控规则


- 在流控规则菜单,可以看到新增的流控规则
测试
- 浏览器输入: http://localhost:10004/member/get/1, 快速刷新, 页面显示正常(原因 是服务执行时间很短,刷新下一次的时候,启动的工作线程,已经完成)
- 为了看到效果,修改下MemberController.java
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id, HttpServletRequest request) {
//让线程休眠 1s, 模拟执行时间
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Member member = memberService.queryMemberById(id);
//使用Result把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功 member-service-nacos-provider-10004" , member);
} else {
return Result.error("402","ID= " + id + "不存在");
}
}
- 重启 member-service-nacos-provider-10004 , 注意需要重新加入流控规则
- 浏览器输入: http://localhost:10004/member/get/1 , 快速刷新, 页面出现异常

注意事项和细节
- 当我们请求一次微服务的 API 接口时,后台会启动一个线程
//输出线程的情况
log.info("enter 10004 getMemberById 当前线程id={}",Thread.currentThread().getId(), new Date());

- 阈值类型 QPS 和 线程数的区别讨论
- 如果一个线程平均执行时间为0.05秒,就说明在1秒钟,可以执行20次(相当于 QPS为 20)
- 如果一个线程平均执行时间为1秒, 说明1秒钟,可以执行1次数(相当于 QPS为1)
- 如果一个线程平均执行时间为2秒, 说明2秒钟内,才能执行1次请求
流量控制实例-关联
关联的含义
当关联的资源达到阈值时,就限流自己
需求分析
需求: 通过 Sentinel 实现 流量控制
当调用 member-service-nacos-provider-10004 的 /t2 API 接口时,如果 QPS 超过 1,这 时调用 /t1 API 接口 直接接失败,抛异常
/t2
是关联的资源 , 限流的资源是/t1
修改 MemberController.java
@GetMapping("/t1")
public Result t1(){
return Result.success("t1()执行");
}
@GetMapping("/t2")
public Result t2(){
return Result.success("t2()执行");
}
配置实现步骤
为/t1 增加流控规则


在流控规则菜单,可以看到新增的流控规则
测试
Postman 模拟高并发访问/t2
- 创建新的 http request

- 保存 request 到 一个新的 collection 中

- 设置 run collection 参数, 并运行


在postman执行 Run sentinel
,然后去浏览器访问 http://localhost:10004/t1

注意事项和细节
在 postman 执行 高并发访问 /t2 没有结束时, 去访问 /t1 才能看到流控异常出现
流量控制实例-Warm up
Warm up 介绍
概述
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长 一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的 增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动, 预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等
一张图

- 通常冷启动的过程系统允许通过的 QPS 曲线图(上图)
- 默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设 定的 QPS 阈值
- 这里的threshold 就是最终要达到的QPS阈值
文档
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值
Warm up 称为 冷启动/预热
应用场景: 秒杀在开启瞬间,大流量很容易造成冲垮系统,Warmup 可慢慢的把流量放入,最终将阀值增长到设置阀值
需求分析
- 需求: 通过 Sentinel 实现 流量控制,演示 Warm up
- 调用 member-service-nacos-provider-10004 的 /t2 API 接口,将 QPS 设置为 9, 设置 Warm up 值为 3
- 含义为 请求 /t2 的 QPS 从 threshold / 3( 9 /3 = 3) 开始,经预热时长(3 秒)逐渐升至 设定的 QPS 阈值(9)
- 为什么是 9 / 3, 这个是 3 就是默认冷启动启动加载因子 coldFactor=3
- 测试预期效果: 在前 3 秒,如果访问 /t2 的 QPS 超过 3, 会直接报错,在 3 秒后 访问 /t2 的 QPS 超过 3, 小于等于 9, 是正常访问
配置实现步骤
- 为/t2 增加流控规则

- 在流控规则菜单,可以看到新增的流控规则
测试
浏览器访问 http://localhost:10004/t2 快速刷新页面,在前 3 秒,会出现流控异常,
后 3 秒就正常了(如果你刷新非常快 QPS>9 , 仍然会出现流控异常
注意事项和细节
测试 Warm up 效果不是很好测,如果出不来可以尝试,调整 流控规则: 比如 QPS 为 11, Warm up 预热时间 6 秒
如果请求停止(即: 一段时间没有达到阈值), Warm up 过程将重复, 小伙伴可以理解是一个弹 性过程
流量控制实例-排队
排队介绍
排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法

这种方式主要用于处理间隔性突发的流量,例如消息队列。比如这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的 空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。- 类似前面说的削峰填谷
匀速排队,阈值必须设置为 QPS
需求分析
- 需求: 通过 Sentinel 实现 流量控制-排队
- 调用 member-service-nacos-provider-10004 的 /t2 API 接口,将 QPS 设置为 1
- 当调用 /t2 的 QPS 超过 1 时,不拒绝请求,而是排队等待, 依次执行
- 当等待时间超过 10 秒,则为等待超时
修改业务类
MemberController.java
@GetMapping(value = "/t2")
public Result t2() {
//让线程休眠 1s, 模拟执行时间
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("执行 t2() 线程 id= " + Thread.currentThread().getId());
return Result.success("t2 执行");
}
配置实现步骤
- 为/t2 增加流控规则

- 在流控规则菜单,可以看到新增的流控规则

测试
- 浏览器访问 http://localhost:10004/t2 快速刷新页面 9 次,观察前台/后台输出的情况
2022-11-12 22:43:07.197 INFO 14388 --- [io-10004-exec-3] c.l.s.controller.MemberController : 执行 t2() 线程 id= 51
2022-11-12 22:43:08.196 INFO 14388 --- [io-10004-exec-5] c.l.s.controller.MemberController : 执行 t2() 线程 id= 53
2022-11-12 22:43:09.197 INFO 14388 --- [io-10004-exec-6] c.l.s.controller.MemberController : 执行 t2() 线程 id= 54
2022-11-12 22:43:10.198 INFO 14388 --- [io-10004-exec-7] c.l.s.controller.MemberController : 执行 t2() 线程 id= 55
2022-11-12 22:43:11.196 INFO 14388 --- [io-10004-exec-8] c.l.s.controller.MemberController : 执行 t2() 线程 id= 56
2022-11-12 22:43:12.196 INFO 14388 --- [io-10004-exec-9] c.l.s.controller.MemberController : 执行 t2() 线程 id= 57
2022-11-12 22:43:13.196 INFO 14388 --- [o-10004-exec-10] c.l.s.controller.MemberController : 执行 t2() 线程 id= 58
2022-11-12 22:43:14.195 INFO 14388 --- [io-10004-exec-1] c.l.s.controller.MemberController : 执行 t2() 线程 id= 49
2022-11-12 22:43:15.197 INFO 14388 --- [io-10004-exec-2] c.l.s.controller.MemberController : 执行 t2() 线程 id= 50
没有报错误,后台请求排队执行,每隔1s 匀速执行
- 浏览器访问 http://localhost:10004/t2 快速刷新页面 20 次,当请求等待时间超过 10S, 仍然出现流控异常
Sentinel 熔断降级
线程堆积引出熔断降级
- 一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等
- 例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需 要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现 了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
- 这时,我们对不稳定的服务进行熔断降级,让其快速返回结果,不要造成线程堆积
文档地址
https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
基本介绍

- 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成 复杂的调用链路
- 链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最 终导致整个链路都不可用
- 因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部 不稳定因素导致整体的雪崩
熔断,降级,限流三者的关系
熔断强调的是服务之间的调用能实现自我恢复的状态
限流是从系统的流量入口考虑, 从进入的流量上进行限制, 达到保护系统的作用
降级, 是从系统业务的维度考虑,流量大了或者频繁异常, 可以牺牲一些非核心业务,保 护核心流程正常使用
梳理:
-熔断是降级方式的一种
-降级又是限流的一种方式
-三者都是为了通过一定的方式在流量过大或者出现异常时, 保护系统的手段
熔断策略
慢调用比例
1、慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用
2、当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
3、熔断时长后, 熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
4、配置参考

异常比例
1、异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最 小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
2、经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态) 3、若接下来的一个请求成功完成(没有错误)则结束熔断, 否则会再次被熔断
3、若接下来的一个请求成功完成(没有错误)则结束熔断, 否则会再次被熔断
4、异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%
5、配置参数

6、工作示意图

异常数
1、异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断
2、经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)
3、若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断
4、配置参考

熔断降级实例-慢调用比例
需求分析
需求: 通过 Sentinel 实现 熔断降级控制-慢调用比例
当调用 member-service-nacos-provider-10004 的 /t3 API 接口时,如果在 1s 内持续进入了 5 个请求,并且请求的平均响应时间超过 200ms, 那么就在未来 10 秒钟内,断路器打开, 让 /t3 API 接口 微服务不可用
后面对/t3 API 接口 访问降到 1S 内 1 个请求,降低访问量了,断路器关闭,微服务恢 复
修改业务类
修改 MemberController.java 增加方法 t3()
@GetMapping("/t3")
public Result t3(){
//让线程休眠 300ms
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.success("t3()执行");
}
配置实现步骤
- 为/t3 增加降级规则


- 在流控规则菜单,可以看到新增的降级规则

测试
浏览器访问 http://localhost:10004/t3 快速刷新页面 若干次

出现该页面后,等待大约10s,可以继续成功访问
注意事项和细节
平均响应时间超出阈值 且 在 1s 内通过的请求>=5, 两个条件同时满足后触发降级
熔断时间过后,关闭断路器,访问恢复正常
熔断降级实例-异常比例
需求分析
- 需求: 通过 Sentinel 实现熔断降级控制
- 当调用 member-service-nacos-provider-10004 的 /t4 API 接口时,当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过 20%(即异常比例到 20%), 断路器打开(即: 进入降级状态), 让 /t4 API 接口 微服务不可用
- 当对/t4 API 接口 访问降到 1S 内 1 个请求,降低访问量了,断路器关闭,5 秒后微服务恢复
修改业务类
修改 MemberController.java 增加方法 t4()
private static int num = 0;//执行的计数器-static静态
@GetMapping("/t4")
public Result t4(){
//设计异常比例达到 50%
if(++num % 2 == 0){
//制造异常
System.out.println(3 / 0);
}
log.info("熔断降级测试[异常比例] 执行t4() 线程id={}", Thread.currentThread().getId());
return Result.success("t4()执行");
}
配置实现步骤
- 为/t4 增加降级规则

- 在流控规则菜单,可以看到新增的降级规则

测试
postman测试
先创建给 collection , 也可以在已经存在的 collection 进行修改, 一定确保更新成功


点击 Run sentinel
浏览器访问: http://localhost:10004/t4

停止 Postman
浏览器访问: http://localhost:10004/t4 , 结果正常了
注意事项和细节
当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值,资源进入降级状态, 需要两个条件都满足
测试时,如果熔断降级和恢复服务两个状态切换不明显,将时间窗口值调整大一点比如 60, 就OK 了
熔断降级实例-异常数
需求分析
- 需求:通过 Sentinel 实现 熔断降级控制
- 当调用 member-service-nacos-provider-10004 的 /t5 API 接口时,当资源的每分钟请求量>=5,并且每分钟异常总数>=5 , 断路器打开(即: 进入降级状态), 让 /t5 API 接口微服务不可用
- 当熔断时间(比如 20S)结束后,断路器关闭, 微服务恢复
修改业务类
修改 MemberController.java 增加方法 t5()
private static int num = 0;//执行的计数器-static静态
@GetMapping("/t5")
public Result t5(){
// 出现 10次异常, 这里设置大于 6, 需要留出几次做测试和加入簇点链路
if(++num <= 10){
System.out.println(3 / 0);
}
log.info("熔断降级测试[异常比例] 执行t5() 线程id={}", Thread.currentThread().getId());
return Result.success("t5()执行");
}
配置实现步骤
- 为/t5 增加降级规则

- 在流控规则菜单,可以看到新增的降级规则

测试
- http://localhost:10004/t5 , 访问 5 次,出现 5 次异常(1 分钟内完成)
5次异常后,出现熔断降级

2.20秒后,再次访问,就返回正常结果了

注意事项和细节
- 资源在 1 分钟的异常数目超过阈值之后会进行熔断降级
- 异常数统计是分钟级别的,若 设置的时间窗口 小于 60s,则结束熔断状态后仍可能再进入熔 断状态, 测试时,最好将时间窗口设置超过 60S
Sentinel 热点规则
一个问题引出热点key限流
- 热点: 热点即经常访问的数据。很多时候我们希望统计热点数据中, 访问频次最高的Top K 数据,并对其访问进行限制。
- 比如某条新闻上热搜 ,在某段时间内高频访问, 为了防止系统雪崩, 可以对该条新闻进行热点限流
文档地址
https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
基本介绍
1. 热点参数限流会统计传入1.参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。 2.热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效 3.Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控 https://blog.csdn.net/qq_34416331/article/details/106668747 4.热点参数限流支持集群模式
热点Key限流-实例
需求
- 通过 Sentinel 实现 热点 Key 限流
- 对 member-service-nacos-provider-10004 的 /news?id=x&type=x API 接口进行热点限流
- 假定 id=10 这一条新闻是当前的热点新闻, 当查询新闻时,对通常的 id(非热点新闻)请求 QPS 限定为 2, 如果 id=10 QPS 限定为 100
- 如果访问超出了规定的 QPS, 触发热点限流机制, 调用自定义的方法,给出提示信息
- 当对 /news?id=x&type=x API 接口 降低访问量,QPS 达到规定范围, 服务恢复
修改业务类
修 改 MemberController.java 增 加 方 法queryNews()
/**
* 1.@SentinelResource : 指定sentinel限流资源
* 2.value = "news" 表示sentinel限流资源 名称,由程序员指定
* 3. blockHandler = "newsBlockHandler": 当出现限流时,由newsBlockHandler方法进行处理
*/
@GetMapping("/news")
@SentinelResource(value = "news", blockHandler = "newBlockHandler")
public Result queryNews(@RequestParam(value = "id",required = false)String id,@RequestParam(value = "type",required = false)String type){
//在实际开发中, 新闻应该到DB或者缓存获取,这里就模拟
log.info("到DB查询数据库");
return Result.success("返回id = " + id + "新闻fromDB");
}
// 热点key限制/限流异常处理方法
public Result newBlockHandler(String id, String type, BlockException blockException){
return Result.success("查询 id= " + id + " 新闻 触发热点 key 限制保护.. sorry");
}
配置实现步骤
1.为资源news增加热点规则,注意不是 /news


- 在热点参数限流规则菜单,可以看到新增规则

测试
浏览器输入:http://localhost:10004/news?id=1&type=%E6%95%99%E8%82%B2
QPS没有超过2,返回正确结果

QPS超过2,会返回自定义的信息

独立设置热点 id=10 的 QPS 阈值(即添加例外)



再次测试
浏览器输入:http://localhost:10004/news?id=10&type=%E6%95%99%E8%82%B2
QPS只要不超过100都返回正确结果
浏览器访问的 id 不是 10 的,仍然遵守 QPS 不能超过 2 的热点限制
注意事项和细节
热点参数类型是(byte/int/long/float/double/char/String)
热点参数值,可以配置多个
热点规则只对指定的参数生效 (比如本实例对 id 生效, 对 type 不生效)
系统规则
一个问题引出系统规则
- 如我们系统最大性能能抗 100QPS, 如何分配 /t1 /t2 的 QPS?
- 方案 1: /t1 分配 QPS=50 /t2 分配 QPS=50 , 问题: 如果/t1 当前 QPS 达到 50 , 而/t2 的 QPS 才 10, 会造成没有充分利用服务器性能.
- 方案 2: /t1 分配 QPS=100 /t2 分配 QPS=100 , 问题:容易造成 系统没有流量保护,造成请求线程堆积,形成雪崩.
- 有没有对各个 资源请求的 QPS弹性设置, 只要总数不超过系统最大QPS的流量保护规则? ==> 系统规则
文档地址
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
系统规则作用, 在系统稳定的前提下,保持系统的吞吐量
基本介绍

系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;
反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间
系统规则
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 达到阈值即触发系统保护。
实例
需求
- 需求: 通过 Sentinel 实现 系统规则-入口 QPS
- 对 member-service-nacos-provider-10004 的 所有 API 接口进行流量保护,不管访问哪个 API 接口, 系统入口总的 QPS 不能大于 2, 大于 2,就进行限流控制
- 提示: 上面的 QPS 是为了方便看效果, 设置的很小
配置实现步骤
增加入口 QPS 系统规则



测试
浏览器访问:http://localhost:10004/t1
QPS超过2,就会打开断路器,返回流控信息

@SentinelResource
自定义全局限流处理类
需求分析
- 先看前面的一段代码
@GetMapping("/news")
@SentinelResource(value = "news", blockHandler = "newBlockHandler")
public Result queryNews(@RequestParam(value = "id",required = false)String id,@RequestParam(value = "type",required = false)String type){
//在实际开发中, 新闻应该到DB或者缓存获取,这里就模拟
log.info("到DB查询数据库");
return Result.success("返回id = " + id + "新闻fromDB");
}
// 热点key限制/限流异常处理方法
public Result newBlockHandler(String id, String type, BlockException blockException){
return Result.success("查询 id= " + id + " 新闻 触发热点 key 限制保护.. sorry");
}
说明:当配置的资源名 news 触发限流机制时,会调用 newsBlockHandler 方法
- 上面的处理方案存在一些问题
每个@SentinelResource 对应一个异常处理方法,会造成方法很多
异常处理方法和资源请求方法在一起,不利于业务逻辑的分离
解决方案-> 自定义全局限流处理类.
- 需求:请编写一个自定义全局限流处理类,完成对异常处理.
代码实现
增加异常处理类 CustomGlobalBlockHandler
package com.lzw.springcloud.handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.lzw.springcloud.entity.Result;
/**
* @author LiAng
* 1. CustomGlobalBlockHandler: 全局限流处理类
* 2. 在CustomGlobalBlockHandler类中,可以编写限流处理方法,但是要求方法是static
*/
public class CustomGlobalBlockHandler {
public static Result handlerMethod1(BlockException blockException){
return Result.error("400", "客户自定义异常/限流处理方法handlerMethod1() ");
}
public static Result handlerMethod2(BlockException blockException) {
return Result.error("401", "客户自定义异常/限流处理方法handlerMethod2() ");
}
}
修 改 MemberController.java 增 加 方 法t6()
/**
* value = "t6" 表示 sentinel限流资源的名字
* blockHandlerClass = CustomGlobalBlockHandler.class : 全局限流处理类
* blockHandler = "handlerMethod1": 指定使用全局限流处理类哪个方法,来处理限流信息
* @return
*/
//这里我们使用全局限流处理类,显示限流信息
@GetMapping("/t6")
@SentinelResource(value = "t6",
blockHandlerClass = CustomGlobalBlockHandler.class,
blockHandler = "handlerMethod1")
public Result t6() {
log.info("执行t6() 线程id={}", Thread.currentThread().getId());
return Result.success("200", "t6()执行OK~~");
}
配置实现步骤


测试
浏览器访问:http://localhost:10004/t6 , 如果 QPS 没有超过 1, 返回正常结果
超过时就会返回:

fallBack
修改MemberController.java
@GetMapping("/t6")
@SentinelResource(value = "t6",
blockHandlerClass = CustomGlobalBlockHandler.class,
blockHandler = "handlerMethod1")
public Result t6() {
//假定: 当访问t6资源次数是5的倍数时,就出现java异常
if (++num % 5 == 0) {
throw new NullPointerException("null指针异常 num=" + num);
}
log.info("执行t6() 线程id={}", Thread.currentThread().getId());
return Result.success("200", "t6()执行OK~~");
}
浏览器: http://localhost:10004/t6 , 看效果当 num 为 5 的整数时,返回的是 error 页面,不友好.

这种就需要使用fallback
基本介绍
blockHandler 只负责 sentinel 控制台配置违规
fallback 负责 Java 异常/业务异常
需求
请编写一个自定义全局 fallback 处理类, 处理 java 异常/业务异常
代码实现
在 member-service-nacos-provider-10004 创建 CustomGlobalFallbackHandler.java
package com.lzw.springcloud.handler;
import com.lzw.springcloud.entity.Result;
/**
* @author LiAng
* CustomGlobalFallbackHandler: 全局fallback处理类
* 在CustomGlobalFallbackHandler类中,可以去编写处理java异常/业务异常方法-static
*/
public class CustomGlobalFallbackHandler {
public static Result fallbackHandlerMethod1(Throwable e){
return Result.error("400","java 异常信息="+e.getMessage());
}
public static Result fallbackHandlerMethod2(Throwable e){
return Result.error("400","java 异常信息="+e.getMessage());
}
}
修 改 MemberController.java
/**
* value = "t6" 表示 sentinel限流资源的名字
* blockHandlerClass = CustomGlobalBlockHandler.class : 全局限流处理类
* blockHandler = "handlerMethod1": 指定使用全局限流处理类哪个方法,来处理限流信息
* fallbackClass = CustomGlobalFallbackHandler.class: 全局fallback处理类
* fallback = "fallbackHandlerMethod1": 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
* @return
*/
//这里我们使用全局限流处理类,显示限流信息
@GetMapping("/t6")
@SentinelResource(value = "t6",
fallbackClass = CustomGlobalFallbackHandler.class,
fallback = "fallbackHandlerMethod1",
blockHandlerClass = CustomGlobalBlockHandler.class,
blockHandler = "handlerMethod1")
public Result t6() {
//假定: 当访问t6资源次数是5的倍数时,就出现java异常
if (++num % 5 == 0) {
throw new NullPointerException("null指针异常 num=" + num);
}
log.info("执行t6() 线程id={}", Thread.currentThread().getId());
return Result.success("200", "t6()执行OK~~");
}
测试
浏览器输入: http://localhost:10004/t6 , 访问次数不是 5 的倍数, 返回正常结果
浏览器输入: http://localhost:10004/t6 , 访问次数是 5 的倍数, 返回 fallback 指定方法信息

为资源 /t6 增加流控规则,方便测试

浏览器输入: http://localhost:10004/t6 , 如果访问 QPS 大于 1 , 由 blockHandler 指定的方法处理,
访问次数是 5 的倍数, 由 fallback 指定方法处理, 其它情况返回正常的结果.



exceptionsToIgnore
如果希望忽略某个异常,可以使用 exceptionsToIgnore
应用实例
/**
* value = "t6" 表示 sentinel限流资源的名字
* blockHandlerClass = CustomGlobalBlockHandler.class : 全局限流处理类
* blockHandler = "handlerMethod1": 指定使用全局限流处理类哪个方法,来处理限流信息
* fallbackClass = CustomGlobalFallbackHandler.class: 全局fallback处理类
* fallback = "fallbackHandlerMethod1": 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
* exceptionsToIgnore = {RuntimeException.class}: 表示如果t6()抛出RuntimeException, 就使用系统默认方式处理
*
* @return
*/
//这里我们使用全局限流处理类,显示限流信息
@GetMapping("/t6")
@SentinelResource(value = "t6",
fallbackClass = CustomGlobalFallbackHandler.class,
fallback = "fallbackHandlerMethod1",
blockHandlerClass = CustomGlobalBlockHandler.class,
blockHandler = "handlerMethod1",
exceptionsToIgnore = {RuntimeException.class}
)
public Result t6() {
//假定: 当访问t6资源次数是5的倍数时,就出现java异常
if (++num % 5 == 0) {
throw new NullPointerException("null指针异常 num=" + num);
}
log.info("执行t6() 线程id={}", Thread.currentThread().getId());
return Result.success("200", "t6()执行OK~~");
}
浏览器输入: http://localhost:10004/t6 , 你会发现访问次数为 5 的倍数时,不再调用fallback 指定方法处理

接入 Sentinel 的方式
代码方式(硬编码,侵入性强, 不推荐)
文档地址
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
注解使用(低侵入性,前面用过,推荐)
https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
1.注解方式埋点不支持private方法
https://xue.baidu.com/okam/pages/strategy-tp/index?strategyId=136707206360879&source=natural
2.@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。
3.@SentinelResource 注解包含以下属性
value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
(1)返回值类型必须与原函数返回值类型一致;
(2)方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
(3)fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
(1)返回值类型必须与原函数返回值类型一致;
(2)方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
(3)defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
openFeign+sentinel 对远程调用熔断降级
当前微服务基础环境
当前架构图

测试
浏览器输出 http://localhost/member/nacos/consumer/get/1,目前是Ribbon+RestTemplate
服务消费者整合 Openfeign
需求
在 member-service-nacos-consumer-80 整合 Openfeign 实现远程调用

代码实现
修改 member-service-nacos-consumer-80 的 pom.xml 加入 openfeign 依赖
<!--引入openfeign starter 即场景启动器starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
将 member-service-nacos-provider-10004/10006中的MemberController中方法签名修改下
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
在 member-service-nacos-consumer-80 添加service层,增加MemberOpenFeignService.java
package com.lzw.springcloud.service;
import com.lzw.springcloud.entity.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author LiAng
*/
@FeignClient(value = "member-service-nacos-provider")
public interface MemberOpenFeignService {
/**
* 1. 远程调用方式是 get
* 2. 远程调用的url 为 http://member-service-nacos-provider/member/get/{id}
* 3. member-service-nacos-provider是nacos注册中心服务名
* 4. openfeign会根据负载均衡算法来决定调用的是 10004/10006,默认是轮询算法
* 5. openfeign是通过接口方式调用服务
*/
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id);
}
修改 member-service-nacos-consumer-80 的 MemberNacosConsumerController,增加
//装配MemberOpenFeignService
@Resource
private MemberOpenFeignService memberOpenFeignService;
//编写方法通过openfeign实现远程调用
@GetMapping("/member/openfeign/consumer/get/{id}")
public Result<Member> getMemberOpenfeignById(@PathVariable("id") Long id) {
//这里我们使用openfeign接口方式远程调用
log.info("调用方式是 openfeign..");
return memberOpenFeignService.getMemberById(id);
}
修改 member-service-nacos-consumer-80 的主启动类,增加注解 @EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class MemberNacosConsumerApplication80 {
public static void main(String[] args) {
SpringApplication.run(MemberNacosConsumerApplication80.class, args);
}
}
测试
浏览器输入: http://localhost/member/openfeign/consumer/get/1,目前是Openfeign调用(负载均衡)


服务消费者整合 Sentinel
需求
在 member-service-nacos-consumer-80 整合 Sentinel 能被 Sentinel 监控

代码实现
修改 member-service-nacos-consumer-80 的 pom.xml 加入 sentinel 依赖
<!--引入 sentinel starter 即 场景启动器starter-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改 member-service-nacos-consumer-80 的 application.yml 配置 sentinel
server:
port: 80
spring:
application:
name: member-service-nacos-consumer-80
# 配置nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos server的地址
sentinel:
transport:
dashboard: localhost:8080 # 指定sentinel控制台地址(dash board)
port: 8719 #默认 8719,假如被占用了, 会自动从 8719 开始依次+1 扫描。直至找到未被占用的端口
# 暴露所有监控点
management:
endpoints:
web:
exposure:
include: "*"
测试
先请求下 http://localhost/member/openfeign/consumer/get/1
,然后打开sentinel控制台

openFeign+sentinel 对远程调用熔断降级
需求
在 member-service-nacos-consumer-80 调用某个无效服务时,启动 Sentinel的熔断降级机制 , 能够快速返回响应,而不是使用默认的超时机制(因为超时机制容易线程堆积, 从而导致雪崩)

先测试一下,关闭 10004/10006, 这时 openfeign 去调用会怎么样? (返回 time out)

还可以测试一下,让 10004 服务对应的 API 执行时间很长(比如休眠 2 秒), 这时 openfeign 去调用会怎么样?
openfeign+sentinel整合后,会自动选择响应时间短的服务,这时总调用10006的API

代码实现
修改 member-service-nacos-consumer-80,增加MemberOpenFeignFallbackService
package com.lzw.springcloud.service;
import com.lzw.springcloud.entity.Result;
import org.springframework.stereotype.Component;
/**
* @author LiAng
*/
@Component
public class MemberOpenFeignFallbackService implements MemberOpenFeignService{
@Override
public Result getMemberById(Long id) {
return Result.error("500","被调用服务异常,熔断降级,快速返回结果,防止线程堆积...");
}
}
修改 member-service-nacos-consumer-80的 修改 member-service-nacos-consumer-80,加入fallback的处理类
package com.lzw.springcloud.service;
import com.lzw.springcloud.entity.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author LiAng
*/
@FeignClient(value = "member-service-nacos-provider",fallback = MemberOpenFeignFallbackService.class)
public interface MemberOpenFeignService {
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id);
}
修改 member-service-nacos-consumer-80的 application.yml,加入openfeign和sentinel整合配置
server:
port: 80
spring:
application:
name: member-service-nacos-consumer-80
# 配置nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos server的地址
sentinel:
transport:
dashboard: localhost:8080 # 指定sentinel控制台地址(dash board)
port: 8719 #默认 8719,假如被占用了, 会自动从 8719 开始依次+1 扫描。直至找到未被占用的端口
# 暴露所有监控点
management:
endpoints:
web:
exposure:
include: "*"
#openfeign和sentinel整合,必须配置
feign:
sentinel:
enabled: true
测试
浏览器输入: http://localhost/member/openfeign/consumer/get/1,目前是Openfeign调用(负载均衡)

注意事项和细节
因为 member-service-nacos-consumer-80 已经被 sentinel 监控,所以我们可以加入相关的流控规则, 比如为 /member/openfeign/consumer/get/1 加入流控规则 qps = 1

测试: 如果/member/openfeign/consumer/get/1 请求 QPS 超过 1, 会输出

规则持久化
规则没有持久化的问题
如果 sentinel 流控规则没有持久化,当重启调用 API/接口 所在微服务后,规则就会丢失,需要重新加入
解决方案:通过 Nacos 进行持久化
规则持久化方案
1.阿里云 Ahas[最方便/付费]
官方文档:应用高可用服务(AHAS)-阿里云帮助中心 (aliyun.com)
2.在 Nacos Server 配置规则, 完成持久化 -官方推荐
3.将规则持久化到本地文件, 定时同步
4.其他
Nacos Server 配置中心-规则持久化实例

需求
在 member-service-nacos-consumer-80的 /member/openfeign/consumer/get/1 API接口添加流控规则 QPS=1/快速失败
要求将该流控规则加入到 nacos server 配置中心,实现持久化
配置实现步骤
打开nacos


[
{
"resources": "/member/openfeign/consumer/get/1",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
然后点击右下角发布

在 Nacos Server 配置中心增加 Sentinel 客户端/微服务模块 的流控规则参数说明
resource∶资源名称;
limlitApp∶ 来源应用;
grade∶阈值类型,0表示线程数,1表示QPS;
count∶单机阈值;
strategy∶流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior∶流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode∶是否集群
修改member-service-nacos-consumer-80的pom.xml
<!--引入sentinel 和 nacos持久化整合依赖,使用版本仲裁-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
修改member-service-nacos-consumer-80的application.yaml
server:
port: 80
spring:
application:
name: member-service-nacos-consumer-80
# 配置nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos server的地址
sentinel:
transport:
dashboard: localhost:8080 # 指定sentinel控制台地址(dash board)
port: 8719 #默认 8719,假如被占用了, 会自动从 8719 开始依次+1 扫描。直至找到未被占用的端口
datasource:
ds1:
# 流控规则配置是从nacos server 配置中获取
nacos:
server-addr: localhost:8848
dataId: member-service-nacos-consumer-80
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
# 暴露所有监控点
management:
endpoints:
web:
exposure:
include: "*"
#openfeign和sentinel整合,必须配置
feign:
sentinel:
enabled: true
测试
浏览器输入: http://localhost/member/openfeign/consumer/get/1,目前是Openfeign调用(负载均衡),而且流控规则已经生效了

查看 Sentinel 控制台, 发现已经同步了流控规则
注意事项和细节
在 nacos server 配置 sentinel 流控规则的 Data ID 也可以自己指定,比如写成 lzw-id,只要在 sentinel client/微服务 的 applicaion.yml 的datasource.ds1.nacos.dataId 的值保持一致即可