基于Sentinel的微服务限流及熔断原理与实战 - 哔哩哔哩 (bilibili.com)
微服务架构|5.2 基于 Sentinel 的服务限流及熔断 (baidu.com)
Spring Cloud + Alibaba Sentinel 源码原理深度剖析!_musicml的博客-CSDN博客
SpringCloud项目引入Sentinel做流控 - 云+社区 - 腾讯云 (tencent.com)
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
基本概念
- 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码 , 就是资源 ,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。 - 规则
围绕资源的实时状态设定的规则,可以包括 流量控制规则、 熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
对比
功能 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离(并发线程数隔离) | 线程池隔离/信号量隔离 线程切换费时 隔离好 |
熔断降级策略 | 基于响应时间、异常比率额、异常数 | 基于异常比率 |
限流 | 基于QPS,支持基于调用关系的限流 | 支持有限 |
控制台 | 可配置规则、查看秒级监控 | 简单的监控查看 |
启动
java -jar xxx --server.port=xxxx
熔断【自动】
设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据 触发系统主动规则(不用配置的,远程调用失败就自动按fallback来)
A调B,B很久没返回,直接不等了,调用 B 的直接返回降级数据
// 在服务消费者中,实现 feign 远程接口,接口的实现方法即为调用错误的容错方法 在调用方做
@FeignClient(value = "taomall-seckill",fallback = SecKillFeignServiceFallBack.class)
@Component
public class SecKillFeignServiceFallBack implements SecKillFeignService {
@Override
public R getSkuKillInfo(Long skuId) {
log.info("熔断方法调用了....");
return R.error(BizCodeEnume.TO_MANY_REQUEST.getCode(),BizCodeEnume.TO_MANY_REQUEST.getMsg());}}
降级【手动配置】
在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行。降级:某些服务不理,或者简单处理【抛异常、返回 NULL、调用 Mock 数据、调用 Fallback 处理逻辑】 基于全局考虑停止一些服务
调用方做降级:需要配置规则,比如,要求响应时间必须100ms,那么1s内连续5个请求的响应时间都超过100ms,就会自动熔断,再过了时间窗口后恢复。
提供方降级:配置规则,提供方是运行的,但是会返回提供方配置的返回规则(限流的数据)【流量超大】,在秒杀服务中,加了url请求调用受限的返回的配置类,降级和流控共使用指定的返回
限流【手动配置】
流量进行控制
接入限流埋点【没有埋点,在簇点链路就找不到】
Sentinel starter 默认为所有的 HTTP 服务提供了限流埋点,通过@SentinelResource和SphU.entry()实现
自定义配置类,添加如下配置
WebCallbackManager.setUrlBlockHandler(); //url请求调用受限后执行统一返回,fallback是针对特定自定义资源受限的返回
自定义保护资源
try (Entry entry = SphU.entry("secKillSkus")){}catch{} //在控制台簇点链路去新增流控或降级规则
@SentinelResource(value = "getSkuKillInfoResource",blockHandler = "blockHandler"/*,fallback = */) //需要指定blockHandler这个方法,返回和资源方法一样的,参数名一样,可以拿到异常,fallback会针对类型的异常
流控规则
-
快速失败
-
warm up(令牌桶算法)
预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
-
排队等待(漏斗算法)
可以直接在网关配置
配置API名称(路由),以及header的匹配规则等,设置分组控制,指定返回fallback
熔断降级设计理念
-
通过并发线程数进行限制
和Hystrix线程池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
-
通过响应时间对资源进行降级:直到过了指定的时间窗口之后才重新恢复。
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
实时监控
需要actuator的支持,引入对应的依赖,暴漏endpoints
配置保存
通过控制台设置规则后将规则推送到统一的规则中心, 客户端实现ReadableDataSource接口端监听规则中心实时获取变更
DataSource 扩展常见的实现方式有:
拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用Nacos、Zookeeper 等配置 中心。这种方式有更好的实时性和一致性保证。
拉模式配置步骤:
-
引入sentinel-datasource-nacos依赖
-
配置类,加载各种流控、降级、系统规则和权限策略(等价在application.properties中配置)
-
在 在nacos中创建dataId ,并使用 json格式,并添加流空规则
resource:资源名,即限流规则的作用对象
count: 限流阈值
grade: 限流阈值类型(QPS 或并发线程数)
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
strategy: 调用关系限流策略
controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
原理部分
Sentinel 底层采用高性能的滑动窗口数据结构 LeapArray
来统计实时的秒级指标数据,支持泛型
LeapArray有4个关键的属性。
- windowLengthInMs 每个窗口有多少毫秒
- sampleCount 窗口数量
- intervalInMs 要统计的间隔时长
- array 即统计的数据存放的地方
而窗口中的数据存储结构用的是MetricBucket,在ArrayMetric类中,有成员变量LeapArray
MetricBucket类中定义一些添加各种类型结果(在MetricEvent枚举类中),属性有:
- LongAdder[] counters 统计各种访问是正常还是阻塞等的数目
- volatile long minRt 超时丢弃
MetricBucket构造器用到了MetricEvent枚举类,包括正常通过,正常阻塞和quota
源码
public class ArrayMetric implements Metric {
private final LeapArray<MetricBucket> data;
}
public abstract class LeapArray<T> {
protected int windowLengthInMs; //样本窗口长度
protected int sampleCount; //时间窗长度/样本窗口长度
protected int intervalInMs; //时间窗长度protected final
protected final AtomicReferenceArray<WindowWrap<T>> array;
}
public class WindowWrap<T> { //样本窗口private final long windowLengthInMs;
private final long windowLengthInMs;
private long windowStart;
private T value; //统计数据
}
public class MetricBucket {
private final LongAdder[] counters; //统计多维度数据
}
public enum MetricEvent {
PASS,
BLOCK, //被限流
EXCEPTION,
SUCCESS,
RT, //响应时间
OCCUPIED_PASS //Passed in future quota (pre-occupied, since 1.5.0).
}
public WindowWrap<T> currentWindow(long timeMillis) { //计算当前时间窗口
int idx = calculateTimeIdx(timeMillis);
long windowStart = calculateWindowStart(timeMillis);
}
Slot
-
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径
-
ClusterBuilderSlot
存储资源的统计信息以及调用者信息,例如该资源的RT、QPS、Thread count、Block count及Exception count等,这些信息将用作多维度限流、降级的依据。简单来说,就是用与构建ClusterNode
-
StatisticSlot
用于记录、统计不同维度的runtime指标监控信息
-
ParamFlowSlot
对应热点流控
-
SystemSlot
系统保护
-
AuthoritySlot
来源访问控制
-
FlowSlot
根据预设的限流规则及前面的slot统计的状态,来进行流量控制,对应流控规则
-
DegradeSlot
熔断降级
Node
-
Node
用于完成数据统计的接口
-
StatisticNode
统计节点,是Node接口的实现类,用于完成数据统计
-
Entrance Node
入口节点,一个Context会有一个入口节点,用于统计当前Context的总体流量数据
-
DefaultNode
默认节点,用于统计一个资源在当前Context中的流量数据
-
ClusterNode
集群节点,用于统计一个资源在所有Context中的总体流量