Sentinel 是什么
什么是限流
限流指的是限制系统或服务的请求流量,以保证系统的稳定性和可用性。在高并发场景下,系统容易出现请求量过大而导致资源瓶颈、响应变慢甚至崩溃的情况。
什么是Sentinel
Sentinel是阿里巴巴开源的一款流量控制组件,主要用于分布式系统中的流量控制、熔断降级等功能。它提供了多种限流算法和限流维度,可以根据业务需求进行配置和使用,帮助开发者在高并发场景下有效地保护系统的稳定性。
为什么使用 Sentinel 限流
在高并发场景下,系统容易出现请求量过大而导致资源瓶颈、响应变慢甚至崩溃的情况。为了保证系统的可用性和稳定性,需要对请求进行限流。
- 保护系统稳定性:限制每个用户或服务的请求速率,可以有效地降低系统的负载,避免系统崩溃。
- 提高服务可用性:限流可以避免系统因过度压力而出现响应延迟、错误甚至宕机等问题,提高服务的可用性和稳定性。
- 防止资源被耗尽:在高并发的情况下,系统资源很容易被耗尽,例如数据库连接池、网络带宽等,通过限流可以避免资源被耗尽,提高系统的性能。
- 防止恶意攻击:限流可以控制请求的频率,防止恶意攻击、爬虫等非法访问。
Sentinel 特点和优势
Sentinel具有以下几个主要特点:
- 多种限流算法:包括令牌桶、漏桶等,可以根据业务场景选择合适的算法。
- 多种限流维度:包括QPS、并发线程数、异常比例等,可以根据不同的维度来进行限流。
- 熔断降级:可以在服务不可用或异常时快速切换到备用服务或默认返回值,保证系统的可用性。
- 实时监控:提供了Dashboard可视化监控界面,可以实时查看系统的运行状况、设置规则、查看报表等。
- 报警机制:可以在系统出现异常时自动发送邮件、短信等通知。
Sentinel 限流的原理和实现
Sentinel的使用可以分为两个部分:
- 核心库(Java 客户端)
- 控制台(Dashboard)
Sentinel 的核心骨架,将不同的 Slot 按照顺序串在一起(责任链模式)),从而将不同的功能(限流、降级、系统保护)组合在一起。下面主要就是介绍其中的数据统计与流量统计的实现。
配置实时刷新
Sentinel Datasource 配置的规则可以动态实时调整。
在应用启动阶段,程序会主动从 Sentinel Datasource 获取限流规则配置。而在运行期,我们也可以在 Sentinel 控制台动态修改限流规则,应用程序会实时监听配置中心的数据变化,进而获取变更后的数据。
Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel 客户端
大家有兴趣的可以就着这张图对照着源码来看
数据统计
要做流量控制,首先,就要先进行统计,它要知道当前接口的 QPS 和并发是多少,进而判断是否限流。
数据统计的代码在 StatisticNode 中,对于 QPS 数据,它使用了滑动窗口的设计。滑动窗口算法的核心类 LeapArray。
滑动窗口算法的原理是在固定窗口中分割出多个小时间窗口,分别在每个小时间窗口中记录访问次数,然后根据时间将窗口往前滑动并删除过期的小时间窗口。最终只需要统计滑动窗口范围内的所有小时间窗口总的计数即可。
将单位时间切成了多个窗口,每次计算 QPS 时,计算 当前窗口 + 过去几个窗口 的流量总和,这样就避免了固定窗口的问题 (具体使用几个窗口,取决于窗口大小和单位时间大小。例如上图,每个窗口大小为 500 ms,以 1 s 为单位时间做限流,每次使用 current + last 即可)
对于 线程数 数据,它使用了线程计数器进行累加。
publicclassStatisticNodeimplementsNode {
/**
* 统计近一秒的数据, 按秒统计,分成两个窗口,每个窗口500ms,用来统计QPS
* Holds statistics of the recent {@code INTERVAL} seconds. The {@code INTERVAL} is divided into time spans
* by given {@code sampleCount}.
*/privatetransientvolatileMetricrollingCounterInSecond=newArrayMetric(SampleCountProperty.SAMPLE_COUNT,
IntervalProperty.INTERVAL);
/**
* Holds statistics of the recent 60 seconds. The windowLengthInMs is deliberately set to 1000 milliseconds,
* meaning each bucket per second, in this way we can get accurate statistics of each second.
* 统计近一分钟的数据, 按分钟统计,分成60个窗口,每个窗口 1000ms
*/privatetransientMetricrollingCounterInMinute=newArrayMetric(60, 60 * 1000, false);
/**
* The counter for thread count. 当前并发线程数
*/privateLongAddercurThreadNum=newLongAdder();
/**
* The last timestamp when metrics were fetched.
*/privatelonglastFetchTime= -1;
}
复制代码
流量控制
Sentinel的流量控制有两个维度的控制:并发线程数和QPS
它们都是针对某个具体的接口来设置的
Sentinel 的限流原理主要是通过统计系统的QPS(即每秒请求数量)和并发量来控制系统的流量,从而达到限流的目的。当并发请求数量超过预设的阈值时,Sentinel 会拒绝该请求,并返回一个 FlowException 的错误响应。
限流过滤的实现主要在 FlowSlot 的 entry 方法进行。FlowSlot 会根据预设的规则,结合StatisticSlot 统计出来的实时信息进行流量控制。
publicclassFlowSlotextendsAbstractLinkedProcessorSlot<DefaultNode> {
@Overridepublicvoidentry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args)throws Throwable {
//流控检查
checkFlow(resourceWrapper, context, node, count, prioritized);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
voidcheckFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
List<FlowRule> rules = flowRules.get(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
if (!canPassCheck(rule, context, node, count, prioritized)) {
thrownewFlowException(rule.getLimitApp(), rule);
}
}
}
}
publicvoidcheckFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {
if (ruleProvider == null || resource == null) {
return;
}
// 获取对应配置的流控规则
Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
//规则逐一校验,检查是否通过if (!canPassCheck(rule, context, node, count, prioritized)) {
thrownewFlowException(rule.getLimitApp(), rule);
}
}
}
}
}
复制代码
Sentinel 流量控制实践
引入依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId><version>2.1.2.RELEASE</version></dependency>复制代码
添加配置
spring:cloud:sentinel:transport:port:8719# 假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口,默认8719dashboard:http://localhost:8719# 指定控制台服务的地址filter:enabled:true# 心跳监测eager:true复制代码
启动控制台,添加配置
控制台的规则默认存储在内存中,重启就会失效,如果需要持久化配置信息,可以参考官方文章配置(在生产环境中使用 Sentinel · alibaba/Sentinel Wiki · GitHub)
测试
返回对应的错误信息,当然具体的错误信息,大家可以自己根据项目需求去包装一下。