流控效果
当 QPS、线程数超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。
直接拒绝
直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT
)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
。 这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
2018-04-1914:03:46
passqps:480.0
2.Atterthewarm-upperiod.thesystembeginstoprocessthe
samenumberofrequestsasdefinedbythees
I.Thesysemhasbeenidleforawhileandalargenumberofrequestshave
arrived
blockgps
passgps
默认 coldFactor
为 3,即请求 QPS 从 threshold / 3
开始,经预热时长逐渐升至设定的 QPS 阈值。
规则设置如下图所示:
新增流控规则
资源名
lgetStockDetail
针对来源
default
阅值类型
单机关值
100
线程数
QPS
是否集群
流控模式
关联
链路
直接
流控效果
快速失败
排队等待
WarmUp
120
预热时长
关闭高级选项
新增
新增并继续添加
取消
通过 Jmeter 请求过后,可以看到如下效果,完成流控
GEThttp://stock-serce/getStockDeail
50
响应时间(ms)
通过QPS
时间
拒绝QPS
40
16.0
1.0
2021-05-1617:26:2
17:27:22
0.0
5
通过QPS
30
0.0
8.0
17:27:12
41.0
拒绝QPS
20
0.0
17:27:11
7.0
33.0
10
0.0
8.0
28.0
17:27:10
17:27:09
24.0
11.0
0.0
17:26
17:26
17:23
17:25
17:24
17:24
17:27:08
7.0
0.0
23.0
拒绝QPS
通过QPS
匀速排队
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
规则设置如下图所示:
编辑流控规则
资源名
TgetStockDetail
针对来源
default
闹值类型
单机闽值
100
线程数
QPS
是否集群
流控模式
O关联
链路
直接
流控效果
快速失败
排队等待
WarmUp
10
超时时间
关闭高级选项
保存
取消
然后我们通过 jmeter 请求过后可以看到如下效果:
俱实时监控
关键字
开序
TgetStockDetail
1600
通过QPS
拒绝QPS
响应时间(ms)
时间
1400
1200
0.0
17:23:51
12.0
15.0
1000
17:23:50
2021-05-1617:22:01
20.0
0.0
15.0
800
通过QPS
100
600
0.0
17:23:49
15.0
19.0
55
拒绝QPS
400
0.0
15.0
21.0
17:23:48
200
0
0.0
17:23:47
14.0
20.0
17:20
17:19
17:20
17:21
17:23
17:22
15.0
0.0
20.0
17:23:46
通过QPS
拒绝QPS
4. 降级规则
流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
熔断降级策略
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
我们可以在控制台配置:
编辑降级规则
资源名
igetStockDetail
O异常比例
熔断策略
慢调用比例
0异常数
比例闹值
0.1
最大RT
10
5
最小请求数
10
熔断时长
s
取消
保存
jmeter 模拟请求
127.0.0.1
("code":102,"message":"服务降级了""data"
- 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。
我们可以在控制台配置:
新增降级规则
资源名
lgetStockDetail
熔断策略
异常比例
异常数
慢调用比例
比例间值
0.1
5
5
熔断时长
最小请求数
s
取消
新增
新增并继续添加
- 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
我们可以在控制台配置:
新增降级规则
资源名
lgetStockDetail
熔断策略
慢调用比例
异常数
异常比例
异常数
0.1
熔断时长
5
最小请求数
5
新增
取消
新增并继续添加
熔断降级说明
熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field |
说明 |
默认值 |
resource |
资源名,即规则的作用对象 |
|
grade |
熔断策略,支持慢调用比例/异常比例/异常数策略 |
慢调用比例 |
count |
慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 |
|
timeWindow |
熔断时长,单位为 s |
|
minRequestAmount |
熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) |
5 |
statIntervalMs |
统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) |
1000 ms |
slowRatioThreshold |
慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
5. 热点规则
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
QPS:5,blocked
热点,
-
param:axb
请求
param:abo
热点,QPS:3,pass
Sentinel
resA
热点限流
param.xs
非热点,QPS:10,
pass
QPS:5
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
热点规则配置需要注意:
1. 首先资源必须是通过 @SentinelResource
申明
2. 参数类型必须是基础数据类型, 否则配置无效
热点规则配置如下图所示:
编辑热点规则
资源名
lgetStockDetail
限流模式
QPS模式
0
参数索引
秒
统计窗口时长
单机闽值
10
10
是否集群
高级选项
保存
取消
注意:资源名称要和 @SentinelResource 中的资源名称对应才能生效
控制器类的代码如下所示:
@SentinelResource(value = "ResOrderGet", fallback = "fallback", fallbackClass = SentinelExceptionHandler.class, blockHandler = "blockHandler", blockHandlerClass = SentinelExceptionHandler.class ) @GetMapping("/order/get/{id}") public CommonResult<StockModel> getStockDetails(@PathVariable Integer id) { StockModel stockModel = new StockModel(); stockModel.setCode("STOCK==>1000"); stockModel.setId(id); return CommonResult.success(stockModel); } // 异常处理类 public class SentinelResourceExceptionHandler { //限流熔断业务逻辑 public static CommonResult<StockModel> blockHandler(@PathVariable Integer id) { return CommonResult.error(null, -100, "系统错误 (限流熔断业务逻辑)"); } //异常降级业务逻辑 public static CommonResult<StockModel> fallback(@PathVariable Integer id) { return CommonResult.error(null, -100, "系统错误 (异常降级业务逻辑)"); } }
返回异常信息:
127.0.0.1:8066/order/get/3
1/20210516184336
m/h
http://127.0.0.1:8066/order/get/3
4
5
-100
"code"
0100
"系统错误(异常降级业务逻辑)",
"message"
"data":null
6 授权规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过
ContextUtil.enter(resourceName, origin)
方法中的origin
参数传入。
Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,就会调用 RequestOriginParser解析访问来源。
// 注意导包 import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser; import javax.servlet.http.HttpServletRequest; public class SentinelRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { return request.getParameter("origin"); } }
修改 Config 配置信息
@Configuration public class FilterContextConfig { @Bean public FilterRegistrationBean sentinelFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*"); // 入口资源关闭聚合 registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false"); registration.setName("sentinelFilter"); registration.setOrder(1); // CommonFilter 的 BlockException 自定义处理逻辑 WebCallbackManager.setUrlBlockHandler(new SentinelFlowHandler()); //解决授权规则不生效的问题 //com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser WebCallbackManager.setRequestOriginParser(new SentinelRequestOriginParser()); return registration; } }
规则配置
编辑授权规则
资源名
igetOrderDetail
流控应用
order
授权类型
白名单
黑名单
保存
取消
执行请求
正常通过
6>C
127.0.0.1:8066/getorderDetail?originorder
120210516205223
1
/http://127.0.0.1:8066/getordeetai?oriqin-od
F
寸LC0100
"id":1,
"code":"100-1000-x.yz",
"stockModei":null
异常不通过
127.0.0.1:8066/getorderDetail?originode
HNm寸5no
120210516205153
/http://127.0.0.1:8066/getorderDeail?origin-oder
4
"code"
105
"message":"授权规则不通过",
100
nul'
"data":
子
7 系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 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 达到阈值即触发系统保护。
原理
如下图所示
我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。
- 推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。
我们用 T 来表示(水管内部的水量),用RT来表示请求的处理时间,用P来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在 P * RT
个请求。换一句话来说,当 T ≈ QPS * Avg(RT)
的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。
接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。
- 推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力。
然而,和 TCP BBR 的不一样的地方在于,还需要用一个系统负载的值(load1)来激发这套机制启动。
注:这种系统自适应算法对于低 load 的请求,它的效果是一个“兜底”的角色。对于不是应用本身造成的 load 高的情况(如其它进程导致的不稳定的情况),效果不明显。
配置页面
新增系统保护规则
阅值类型
OLOADORTO线程数O入口QPSO.CPU使用率
1000
闹值
新增
取消
触发流控规则
127.0.0.1:8066/getorderDetail?originorder
HNm
120210516211223
//http://127.0.0.1:8066/getorderDetai?originorde
f
4
LO0100
"code":104,
"message":"系统规则(负载/...不满足要求)",
"data":nul'
8 集群流控
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
- Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
- Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
规则推送
Sentinel 控制台同时提供简单的规则管理以及推送的功能。规则推送分为 3 种模式,包括 "原始模式"、"Pull 模式" 和"Push 模式"。
这里先简单的介绍"原始模式"。
规则管理
您可以在控制台通过接入端暴露的 HTTP API 来查询规则。
Sentinel控制台1.8.0
注销
应用名
搜索
新增流控规则
order-service
首页
(1/1)
order-service
流换规则
刷新
关键字
172.20.10.3:8720
L实时监控
贺值
流控效果
资源名
流控模式
门值类型
阁值横式
操作
来源应用
旗点链路
直接
单机
OPS
快速失败
test1
default
开除
编辑
流控规则
降级规则
共1条记录每页
条记录
10
小热点规则
系统规则
授权规则
集群流控
机器列表
(1/2)
stock-service
规则推送
新增流控规则
资源名
/hello
针对来源
default
单机闻值
阁值类型
单机闽值
线程数
QPS
是否集群
高级选项
新增并继续添加
取消
新增
目前控制台的规则推送也是通过 规则查询更改 HTTP API 来更改规则。这也意味着这些规则仅在内存态生效,应用重启之后,该规则会丢失。
注:若通过控制台推送规则时出现 invalid type 或 empty type 的错误,请确保 transport 模块版本与 core 模块版本保持一致;若控制台版本 >= 1.7.1,请将接入端的相关依赖也升级至 1.7.1 及以上版本。
以上是原始模式。当了解了原始模式之后,我们非常鼓励您通过 动态规则 并结合各种外部存储来定制自己的规则源。我们推荐通过动态配置源的控制台来进行规则写入和推送,而不是通过 Sentinel 客户端直接写入到动态配置源中。在生产环境中,我们推荐 push 模式,具体可以参考:在生产环境使用 Sentinel。
注:若要使用集群流控功能,则必须对接动态规则源,否则无法正常使用。您也可以接入 AHAS Sentinel 快速接入全自动托管、高可用的集群流控能力。
Sentinel 同时还提供应用维度规则推送的示例页面(流控规则页面,前端路由为 /v2/flow
),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。Sentinel 抽取了通用接口用于向远程配置中心推送规则以及拉取规则:
DynamicRuleProvider<T>
: 拉取规则(应用维度)DynamicRulePublisher<T>
: 推送规则(应用维度)
用户只需实现 DynamicRuleProvider
和 DynamicRulePublisher
接口,并在 v2 的 controller 中通过 @Qualifier
注解替换相应的 bean 即可实现应用维度推送。我们提供了 Nacos 和 Apollo 的示例,改造详情可参考 应用维度规则推送示例。
鉴权
从 Sentinel 1.5.0 开始,控制台提供通用的鉴权接口 AuthService,用户可根据需求自行实现。
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel
。该鉴权能力非常基础,生产环境使用建议根据安全需要自行改造。
Sentinel
用户
sentinel
密码
登录
清空
用户可以通过如下参数进行配置:
-Dsentinel.dashboard.auth.username=sentinel
用于指定控制台的登录用户名为sentinel
;-Dsentinel.dashboard.auth.password=123456
用于指定控制台的登录密码为123456
;如果省略这两个参数,默认用户和密码均为sentinel
;-Dserver.servlet.session.timeout=7200
用于指定 Spring Boot 服务端 session 的过期时间,如7200
表示 7200 秒;60m
表示 60 分钟,默认为 30 分钟;
同样也可以直接在 Spring properties 文件中进行配置。
注意:部署多台控制台时,session 默认不会在各实例之间共享,这一块需要自行改造。