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-starter nacos的场景启动器starter -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-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: 自己的密码
#配置nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos Server的地址
sentinel:
transport:
dashboard: localhost:8080 #指定sentinel控制台的地址
port: 8719 #指定端口
解读transport.port
-
transport.port 端口配置会在被监控的微服务对应主机上启动 Http Server
-
该Http Server 会与Sentinel控制台进行交互
-
比如sentinel 控制台添加了一个限流规则, 会把规则数据push 给这个Http Server 接收 Http Server 再将这个规则注册到Sentinel 中
-
简单的讲: transport.port 指定被监控的微服务应用于sentinel 控制台交互的端口.
-
默认端口是 8719, 假如被占用了, 就会自动的从8719开始依次+1扫描,直到找到一个没有被占用的端口
成功启动后, netstat -anb 可以查看到该端口
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/member/get/1
5 Sentinel 控制台监控页面
浏览器输入: http://localhost:10004/member/get/1
进入到Sentinel 查看实时监控效果, http://localhost:8080/#/dashboard
注意事项和细节
1 QPS: Queries Per Second(每秒查询率),是服务器每秒响应的查询次数
2 Sentinel 采用的是懒加载, 只有调用了某个接口/服务,才能看到监控数据
Sentinel 流量控制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N4eXDTEY-1685611806673)(<转存失败,建议直接上传图片文件 …/%E5%9B%BE%E7%89%87/image-20230526105400291.png>)]
对上图的解读
资源名∶唯一名称,默认请求路径
针对来源∶Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
阈值类型/单机阈值∶
QPS(每秒钟的请求数量)∶当调用该api 的QPS 达到阈值的时候,进行限流
线程数∶当调用该api 的线程数达到阈值的时候,进行限流
解读QPS 和线程数的区别,
比如QPS 和线程我们都设置阈值为1
(1) 对QPS 而言, 如果在1 秒内, 客户端发出了2 次请求, 就达到阈值, 从而限流
(2) 对线程数而言, 如果在1 秒内, 客户端发出了2 次请求, 不一定达到线程限制的阈值,为什么呢? 假设我们1次请求后台会创建一个线程, 但是这个请求完成时间是0.1 秒(可以视为该请求对应的线程存活0.1 秒), 所以当客户端第2 次请求时(比如客户端是在0.3 秒发出的), 这时第1 个请求的线程就已经结束了, 因此就没有达到线程的阈值, 也不会限流就相当于处理的速度很快第二个线程创建的时候第一个线程已经结束了
(3) 可以这样理解, 如果1个请求对应的线程 平均执行时间为0.1 那么在1s内可以发送10次请求, 就相当于 线程阈值为1 和 QPS 阈值为10
相当于 1秒内发出11次请求 每个线程的执行时间为0.1 就不满足 1一个线程了 因为第10个线程还没有结束11个就来了所以11给会被失败
是否集群∶不需要集群
流控模式∶
直接∶ api 达到限流条件时,直接限流
关联∶ 当关联的资源达到阈值时,就限流自己
链路∶ 当从某个接口过来的资源达到限流条件时,开启限流
流控效果∶
快速失败∶直接失败,抛异常
Warm Up∶根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS 阈值
排队等待∶匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
流量控制实例-QPS
需求分析/图解
-
需求: 通过Sentinel 实现流量控制
-
当调用member-service-nacos-provider-10004 的/member/get/ 接口/API 时,限制1秒内最多访问1 次,否则直接失败,抛异常.
配置实现步骤
- 为/member/get/1 增加流控规则
- 在流控规则菜单,可以看到新增的流控规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/member/get/1
5 Sentinel 控制台监控页面
浏览器输入: 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了
方案2: URL资源清洗
可以通过UrlCleaner 接口来实现资源清洗,也就是对于/member/get/{id}这个URL,
我们可以统一归集到/member/get/*资源下,具体配置代码如下,实现UrlCleaner 接口,并重写clean 方法即可
@Component
public class CustomUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) { //资源清理
/**
* public static boolean isBlank(String str) {
* int strLen;
* if (str != null && (strLen = str.length()) != 0) {
* for(int i = 0; i < strLen; ++i) {//遍历
* if (!Character.isWhitespace(str.charAt(i))) {
* return false;
* }
* }
*
* return true;
* } else {
* return true;
* }
* }
*/
//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/* 增加流控规则
- 在流控规则菜单,可以看到新增的流控规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/member/get/1
5 结果页面
浏览器输入: http://localhost:10004/member/get/1 , 快速刷新, 页面显示正常(原因是服务执行时间很短,刷新下一次的时候,启动的工作线程,已经完成)
为了看到效果,我们修改下controller/MemberController.java
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
//模拟超时, 让线程休眠1s
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出线程的情况
log.info("enter 10004 getMemberById 当前线程id={} 时间={}",
Thread.currentThread().getId(), new Date());
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 接口时,后台会启动一个线程[演示下]
-
阈值类型QPS 和线程数的区别讨论
线程阈值为1的时候
如果一个线程平均执行时间为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
@GetMapping("/t1")
public Result t1() {
return Result.success("t1()执行..");
}
@GetMapping("/t2")
public Result t2() {
//输出线程信息
log.info("执行t2() 线程id={}", Thread.currentThread().getId());
return Result.success("t2()执行..");
}
配置实现步骤
- 为/t1 增加流控规则
- 在流控规则菜单,可以看到新增的流控规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 Postman 模拟高并发访问/t2
- 创建新的http request
- 保存request 到一个新的collection 中
3. 设置run collection 参数, 并运行
4. 浏览器访问: 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/限流---冷启动
默认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 增加流控规则
- 在流控规则菜单,可以看到新增的流控规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/t2
浏览器访问http://localhost:10004/t2 快速刷新页面,在前3 秒,会出现流控异常,后3 秒就正常了(如果你刷新非常快QPS>9 , 仍然会出现流控异常)
注意事项和细节
测试Warm up 效果不是很好测,如果出不来可以尝试,调整流控规则: 比如QPS 为11, Warmup 预热时间6 秒
如果请求停止(即: 一段时间没有达到阈值), Warm up 过程将重复, 可以理解是一个弹性过程
流量控制实例-排队
排队介绍
-
排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法
-
一张图
- 这种方式主要用于处理间隔性突发的流量,例如消息队列。比如这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。- 类似前面说的削峰填谷
- 匀速排队,阈值必须设置为QPS
需求分析/图解
- 需求: 通过Sentinel 实现流量控制-排队
- 调用member-service-nacos-provider-10004 的/t2 API 接口,将QPS 设置为1
- 当调用/t2 的QPS 超过1 时,不拒绝请求,而是排队等待, 依次执行
- 当等待时间超过10 秒,则为等待超时.
修改业务类
- 为了测试看到效果,修改controller/MemberController.java
@GetMapping("/t1")
public Result t1() {
return Result.success("t1()执行..");
}
@GetMapping("/t2")
public Result t2() {
//让线程休眠1s, 模拟执行时间为1s => 当多少个请求就会造成超时
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出线程信息
log.info("执行t2() 线程id={}", Thread.currentThread().getId());
return Result.success("t2()执行..");
}
配置实现步骤
- 为/t2 增加流控规则
- 在流控规则菜单,可以看到新增的流控规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/t2
浏览器访问http://localhost:10004/t2 快速刷新页面9 次,观察前台/后台输出的情况
输出结果分析
没有报错误
后台请求排队执行,每隔1s 匀速执行
- 浏览器访问http://localhost:10004/t2 快速刷新页面20 次,当请求等待时间超过10S,仍然出现流控异常
Sentinel 熔断降级
线程堆积引出熔断降级
-
一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方API等[架构图]。
-
例如,支付的时候,可能需要远程调用银联提供的API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
-
这时,我们对不稳定的服务进行熔断降级,让其快速返回结果,不要造成线程堆积
文档地址: https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
基本介绍
解读上图:
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。
链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。
因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩
熔断,降级,限流三者的关系
-熔断强调的是服务之间的调用能实现自我恢复的状态
-限流是从系统的流量入口考虑, 从进入的流量上进行限制, 达到保护系统的作用
-降级, 是从系统业务的维度考虑,流量大了或者频繁异常, 可以牺牲一些非核心业务,保护核心流程正常使用
梳理:
-熔断是降级方式的一种
-降级又是限流的一种方式
-三者都是为了通过一定的方式在流量过大或者出现异常时, 保护系统的手段
熔断策略
慢调用比例
- 慢调用比例(SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用
- 当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
- 熔断时长后, 熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用RT 则结束熔断,若大于设置的慢调用RT 则会再次被熔断
- 配置参考
异常比列
1、异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
2、经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)
3、若接下来的一个请求成功完成(没有错误)则结束熔断, 否则会再次被熔断
4、异常比率的阈值范围是[0.0, 1.0],代表0% - 100%
5、配置参数
工作示意图
异常数
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 个请求,降低访问量了,断路器关闭,微服务恢复
修改业务类
- 修改controller/MemberController.java 增加方法t3()
@GetMapping("/t3")
public Result t3() {
//让线程休眠300ms毫秒, 模拟执行时间
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.success("t3()执行...");
}
配置实现步骤
- 为/t3 增加降级规则
- 在流控规则菜单,可以看到新增的降级规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 Postman 测试
4.1先创建collection , 也可以在已经存在的collection 进行修改
4.2点击Run sentinel
4.3 浏览器访问: http://localhost:10004/t3
4.4. 停止Postman
4.5. 浏览器访问: http://localhost:10004/t3 , 结果正常了(需要在停止Postman 10s 后)
注意事项和细节
1 平均响应时间超出阈值且在1s 内通过的请求>=5, 两个条件同时满足后触发降级
2 熔断时间过后,关闭断路器,访问恢复正常
熔断降级实例-异常比例
需求分析/图解
- 需求: 通过Sentinel 实现熔断降级控制
2. 当调用member-service-nacos-provider-10004 的/t4 API 接口时,当资源的每秒请求量>=5, 并且每秒异常总数占通过量的比值超过20%(即异常比例到20%), 断路器打开(即:进入降级状态), 让/t4 API 接口微服务不可用 - 当对/t4 API 接口访问降到1S 内1 个请求,降低访问量了,断路器关闭,5 秒后微服务恢复
修改业务类
- 修改controller/MemberController.java 增加方法t4()
@GetMapping("/t4")
public Result t4() {
//设计异常比例达到50% > 20%
if (++num % 2 == 0) {
//制造一个异常
System.out.println(3 / 0);
}
log.info("熔断降级测试[异常比例] 执行t4() 线程id={}",
Thread.currentThread().getId());
return Result.success("t4()执行...");
}
配置实现步骤
- 为/t4 增加降级规则
- 在流控规则菜单,可以看到新增的降级规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 Postman 测试
- 先创建给collection , 也可以在已经存在的collection 进行修改, 一定确保更新成功.
- 点击Run sentinel
- 浏览器访问: http://localhost:10004/t4
- 停止Postman
- 浏览器访问: http://localhost:10004/t4 , 结果正常了(一次返回异常,一次返回正确结果)
注意事项和细节
1 当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值,资源进入降级状态, 需要两个条件都满足
2 测试时,如果熔断降级和恢复服务两个状态切换不明显,将熔断时间窗口值调整大一点比如60, 就OK 了
熔断降级实例-异常数
需求分析/图解
-
需求: 通过Sentinel 实现熔断降级控制
-
当调用member-service-nacos-provider-10004 的/t5 API 接口时,当资源的每分钟请求量>=5,并且每分钟异常总数>=5 , 断路器打开(即: 进入降级状态), 让/t5 API 接口微服务不可用
-
当熔断时间(比如20S)结束后,断路器关闭, 微服务恢复
修改业务类
1.修改controller/MemberController.java 增加方法t5()
解释为什么要怎么设计因为 第一次其实不能算 因为第一次的消耗要被sentinel监测到还没有加入限流的规则
所以从第二次加入规则之后开始算 2-3-4-5-6总共五次并且一分钟内 达到限流要求开始限流 然后就进入了20秒的熔断
然后第7次不会限流但是会抛异常 但是第8次 这个就要注意了
因为是一分钟内算所以说如果前面的4-5-6加20熔断时间在加7-8次在一分钟内 我们第8次一样的会被熔断 举一反三 如果第7次加上3-4-5-6 加20秒熔断在一分钟内同样的
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()执行了...");
}
配置实现步骤
1、为/t5 增加降级规则
2、在流控规则菜单,可以看到新增的降级规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: http://localhost:10004/t5
http://localhost:10004/t5 , 访问5 次,出现5 次异常(1 分钟内完成)
5 次异常后,出现熔断降级
- 20S 后,再次访问http://localhost:10004/t5, 返回正常结果了
直到num大于10从就可以访问了
注意事项和细节
1 资源在1 分钟的异常数目超过阈值之后会进行熔断降级
2 异常数统计是分钟级别的,若设置的熔断时间窗口小于60s,则结束熔断状态后仍可能再进入熔断状态, 测试时,最好将熔断时间窗口设置超过60S
Sentinel 热点规则
一个问题引出热点key 限流
- 热点: 热点即经常访问的数据。很多时候我们希望统计热点数据中, 访问频次最高的Top K 数据,并对其访问进行限制。
- 比如某条新闻上热搜,在某段时间内高频访问, 为了防止系统雪崩, 可以对该条新闻进行热点限流
文档地址:https://github.com/alibaba/Sentinel/wiki/热点参数限流
基本介绍
解读上图:
- 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流
- 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
- Sentinel 利用LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控https://blog.csdn.net/qq_34416331/article/details/106668747
- 热点参数限流支持集群模式
热点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 达到规定范围, 服务恢复
修改业务类
- 修改controller/MemberController.java 增加方法queryNews()
/**
* 解读
* 1.@SentinelResource : 指定sentinel限流资源
* 2.value = "news" 表示sentinel限流资源 名称,由程序员指定
* 3. blockHandler = "newsBlockHandler": 当出现限流时,由newsBlockHandler方法进行处理
*/
@GetMapping("/news")
@SentinelResource(value = "news", blockHandler = "newsBlockHandler")
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 + " 新闻 from DB");
}
//热点key限制/限流异常处理方法
public Result newsBlockHandler(String id, String type,
BlockException blockException) {
return Result.success("查询id=" + id + " 新闻 触发热点key限流保护 sorry...");
}
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
配置实现步骤
- 为资源news 增加热点规则, 注意不是/news
- 在热点参数限流规则菜单,可以看到新增规则
- 浏览器输入: http://localhost:10004/news?id=1&type=教育 , 如果QPS 没有超过2,则返回正确结果
- 如果QPS 超过2,则返回热点key 处理信息
独立设置热点
- 独立设置热点id=10 的QPS 阈值(即添加例外)
2. 浏览器输入: http://localhost:10004/news?id=10&type=教育 , 如果QPS 没有超过100,则返回正确结果
- 浏览器访问的id 不是10 的,仍然遵守QPS 不能超过2 的热点限制
注意事项和细节
1 热点参数类型是(byte/int/long/float/double/char/String)
2 热点参数值,可以配置多个
3 热点规则只对指定的参数生效(比如本实例对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/系统自适应限流
一句话: 系统规则作用, 在系统稳定的前提下,保持系统的吞吐量
基本介绍
示意图
解读上图:
-
系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;
-
反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间+ 最短处理时间
-
相当于 你在排队是第3名 处理人员是1分钟处理好一个人 所以排队时间为3分钟 处理你的业务需要一分钟这个就是最短处理时间那么你的处理总时间就是4分钟
系统规则
-
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 系统规则
测试
1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: http://localhost:10004/t1
如果QPS 超过2, 打开断路器,返回流控信息(说明: 目的/t2 资源对应方法有休眠代码,所以使用/news?id=x&type=x 测试)