Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
Sentinel与Hystrix的区别
Hystrix常用的线程池隔离会造成线程上下切换的overhead比较大;Hystrix使用的信号量隔离对某个资源调用的并发数进行控制,效果不错,但是无法对慢调用进行自动降级;Sentinel通过并发线程数的流量控制提供信号量隔离的功能;
此外,Sentinel支持的熔断降级维度更多,可对多种指标进行流控、熔断,且提供了实时监控和控制面板,功能更为强大。
spring.cloud.sentinel.eager=true
spring:
cloud: #配置SpringCloudGateway的路由
sentinel:
transport:
dashboard: ip:port #sentinel控制台的请求地址
datasource:
ds1:
nacos:
server-addr: ip:port
data-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-flow
group-id: DEFAULT_GROUP
data-type: json
rule-type: gw-flow //流控规则
ds2:
nacos:
server-addr: ip:port
data-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-api-group
group-id: DEFAULT_GROUP
data-type: json
rule-type: gw-api-group //api类型
eager: true #立即加载
log:
dir: /data/sentinel/gateway //日志存放目录
[
{
"apiName": "itsmApiFlow",
"predicateItems": [
{
"pattern": "/service-request/detail/getInitInfo"
},
{
"pattern": "/service-request/pool/**",
"matchStrategy": 1
}
]
}
]
[
{
"resource": "service-asset",
"resourceMode": 0,
"count": 100,
"intervalSec": 1
},
{
"resource": "itsmApiFlow",
"resourceMode": 1,
"count": 100,
"intervalSec": 1
}
]
流控即流量控制,其原理是监控应用流量的QPS(每秒查询率)或并发线程数等指标,当达到指定的阙值
资源名:唯一名称,默认是请求的路径,可以自定义
针对来源:流控针对的调用来源,可填写服务名,若为default,则不区分调用来源
阙值类型:
- QPS(每秒请求数量):当调用该接口的QPS达到阙值的时候,进行限流
- 线程数:当调用该接口的线程数达到阈值的时候,进行限流
单机阈值:QPS或线程数的阈值数,达到阈值则限流
是否集群:是否开启集群流控,不选择则默认为单机模式
流控模式:
- 直接(默认):接口达到限流条件时,开启限流
- 关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
- 链路:当从某个接口过来的资源达到限流条件时,开启限流
流控效果:
- 直接拒绝:该方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。
- Warm Up(冷启动):该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。
- 排队等待:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
需求:对于/product接口每秒请求量大于1的时候开始限流。
Jmeter测试:
限流生效
【另一种实现方案】
我们可以将限流规则写在代码里,再添加到规则管理器中,但这种方式不为推崇,扩展性以及灵活性极差。
@GetMapping("/product")
public String product(Long id){
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource("/product/");
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
return "test";
}
线程数限流用于保护业务线程数不被耗尽
例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对高线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离),或者使用信号量来控制同时请求的个数(信号量隔离)。这种隔离方案虽然能够控制线程数量,但无法控制请求排队时间。当请求过多时排队也是无益的,直接拒绝能够迅速降低系统压力。Sentinel线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程个数,如果超出阈值,新的请求会被立即拒绝
测试:
当指定接口关联的接口达到限流条件时,将对指定接口开启限流。
此方式适合做应用让步,比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的
测试案例:当接口test1每秒访问量达到1时,/testconfig接口将被限流无法访问。
链路流控模式:
针对资源链路上的接口进行限流,例如:A、B两个接口都调用某一资源C,A->C、B->C可以看成两个简单的链路,此时可以针对C配置链路限流,比如限制A调用C,而B调用C则不受影响,它的功能有点类似于针对 来源配置项,而链路流控是针对上级接口,它的粒度 更细。
测试案例:两个测试接口,调用同一service资源,对其中一个调用链路进行流控
默认配置项, 直接失败,抛出异常,不做任何额外的处理,是最简单的效果。
预热/冷启动:
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。
根据codeFactor(冷加载因子,默认为3)的值,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值 。
测试案例:
对/product资源进行链路线路,设置阈值为10。则刚访问此接口时,实际限流阈值为10/3,即为3,也就是刚开始的时候阈值只有3,当经过10s后,阈值才慢慢提高到10。
让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
测试案例:
对service资源进行链路线路,设置阈值为10。设置流控效果为排队等候,每秒10次请求时,再有请求就排队等候,等待超时时间为10000ms, 超时过后,请求将被踢出排队队列,返回限流异常。
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 最大 RT:慢调用临界 RT,超出该值计为慢调用。单位毫秒
- 比例阈值:RT模式下慢速请求比率的阈值。默认1.0d
- 熔断时长:断路器打开时的恢复超时(以秒为单位)。超时后,断路器将转换为半开状态以尝试一些请求。。单位秒,图中表示触发熔断后,接下来的10秒内请求会自动被熔断,经过10S后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 最小请求数:可以触发熔断中断的最小请求数(在有效的统计时间范围内)。默认值为5
- StatIntervalMs: 统计时长(单位为 ms),如 601000 代表分钟级(1.8.0 引入),默认1000,在控制台没有选项,需代码实现。
- 使用Jmeter压测工具访问测试接口,查看限流效果图,具体限流过程大概为:
(1)请求进入后台,sentinel会根据设置的统计时长(默认1S)统计时间段内请求的总数
(2)首先判断统计的请求总数是否小于用户的设置的最小请求数(默认5),小于则不熔断,反之则进入下一步
(3)然后根据用户设置的最大 RT,判断统计中的请求是否为慢调用,大于设置值为是慢调用请求
(4)再次计算慢调用请求/总统计请求比例,是否超过设置的比例阈值。
(5)当统计时间内的请求数及慢调用比例阈值都超过设置的阈值后,接下来的熔断时长内请求会自动被熔断
(6)熔断时长结束后,熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
实现requsetOriginParser获取来源,测试接口
@Component
public class TestRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
// 实际应该从请求参数或其他来源获取
return "app";
}
}
系统自适应限流,Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 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 达到阈值即触发系统保护。
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
我们可以点击右上角的“添加 Token Server”按钮来添加新的 token server 并分配 client:
初始化定时任务线程池ScheduledThreadPoolExecutor。
获取心跳时间(默认10秒)
发送心跳定时任务
健康状态:健康/失联