Sentinel
使用方如何确定限流的具体QPS?
- 需要找到临界点。
临界点之前:压力测试线性增大QPS,TPS也在逐步提高,时延相对稳定。
临界点附近:压力测试线性增大QPS,TPS负载接近满额(达到峰值),服务中请求处理出现排队情况,时延非线性上涨。
临界点之后:压力测试线性增大QPS,计算资源超额负载,进一步导致GC,进程调度、网络等资源压力导致TPS迅速下降(反噬),请求排队过长,导致时延进一步指数级上升,最终导致服务资源耗尽导致宕机无响应等情况。
将TPS定点附近值作为对QPS的限制阈值相对理想。
Sentinel数据统计底层实现?即每个桶固定时间段内的统计。
sentinel核心流程:
- NodeSelectorSlot:调用链路构建。
- ClusterBuilderSlot:统计簇点构建。
- StatisticSlot:监控统计。(WindowLeapArray,滑动窗口思想)
- ParamFlowSlot:热点参数限流。
- SystemSlot:系统保护。
- AuthoritySlot:来源访问控制。
- FlowSlot:流量控制。
- DegradeSlot:熔断降级。
- Action:…
优化点:
1. Sentinel通过复用Bucket降低对内存的消耗。(循环数组,循环覆盖复用思想)
2. 使用LongAddr降低并发统计数据对性能的消耗。(base+弹扩cell,分段锁思想)
3. Sentinel通过责任链模式实现统计、限流、熔断降级等功能,实现局部无锁化。(责任链模式,局部无锁化)
Sentinel使用了滑动窗口算法
来做数据统计。(具体实现类:LeapArray,StatisticSlot中的LeapArray采用了一个环形数组的数据结构,和一致性hash算法的图类似)
-
如何根据下脚标在环形数组中获取滑动窗口(桶),即将流量随着时间推移映射到桶里?(通过刚刚算出来的开始时间,与桶的开始时间做比较,无桶新建、旧桶更新、新桶直返)
(1) 如果桶不存在则创建新的桶,并通过CAS将新桶赋值到数组下标位。(新建时候CAS)
(2) 如果获取到的桶不为空,并且桶的开始时间等于刚刚算出来的时间,那么返回当前获取到的桶。
(3) 如果获取到的桶不为空,并且桶的开始时间小于刚刚算出来的开始时间,那么说明这个桶是上一圈用过的桶,重置当前桶。(重置一个桶,很难保证其原子操作,直接使用一个ReentrantLock锁来做并发控制。)
(4) 如果获取到的桶不为空,并且桶的开始时间大于刚刚算出来的开始时间,理论上不应该出现这种情况。 -
如何实现滑动?(不同时间到来的流量映射到窗口,随着时间滑动)
// 根据当前时间计算出当前时间属于那个滑动窗口的数组下标
int idx = calculateTimeIdx(timeMillis);
// 根据当前时间计算出当前滑动窗口的开始时间
long windowStart = calculateWindowStart(timeMillis);// 根据当前时间计算出当前时间属于那个滑动窗口的数组下标
private int calculateTimeIdx(/@Valid/ long timeMillis) {
// 利用除法取整原则,保证了一秒内的所有时间搓得到的timeId是相等的
long timeId = timeMillis / windowLengthInMs;
// 利用求余运算原则,保证一秒内获取到的桶的下标位是一致的
return (int) (timeId % array.lengt