sentinel有多种规则,包括:降级、限流、热点等等规则,这些规则均会涉及到时间因素,既在单位时间内的请求量满足各种条件之后的各种动作。
这里我们一起来探针一下sentinel中滑动窗口的实现
如上是一个滑动窗口的示意图。
这里先不说sentinel中滑动窗口的实现代码,咱们想清楚一个问题,为后续阅读滑动窗口代码梳理清楚思路
使用滑动窗口来解决什么问题?
我们使用滑动窗口是为了:统计在一个时间单位里面的数据
要统计一各时间单位,就涉及到时间单位的定义(是1秒还是100ms),统计数据,就需要一个容器来装载数据,同时为了避免在某个时间节点的数据激增,减少统计的误差,可能还需要把一个"单位时间"做更小的切分
涉及的类
com.alibaba.csp.sentinel.slots.statistic.base.LeapArray
LeapArray是sentinel中滑动时间窗口的基类,首先看下该类的类定义:
涉及到3个基本属性和一个引用类型
其中:windowLengthInMs、sampleCount、intervalInMs和我们上面说的单位时间和单位时间的切割有关系,array是用来存放数据的,既:单位时间内的数据存放容器
其中WindowWrap顾名思意:就是时间窗口数据的包装类
LeapArray的构造器:
看完如上两段之后,我们做如下假设:需要统计在1000ms内的数据,假设我们把1000ms这个单位时间分成两段,每段500ms,每段的数据存储在一个windowWrapper中
那当某个时间点,我们有个数据需要存储时:
1 需要计算出该时间节点属于哪个时间窗口(2个500ms的段)
2 需要计算出该时间节点的对应的是哪个windowWrap
以下两个方法就可以解决如上两个问题:
试想以下:当时间节点来到1001时:此时计算出来的start位置(calculateWindowStart方法计算结果)就是:1000,1002计算出的起始位置也是:1000。同时1001和1002计算出来数据落在数组的位置(calculateTimeIdx的结果)也是一样的,均是:0
由此不难看出,如上2个方法把我们需要的关键数据已经计算出来了:时间窗口的开始位置(因为窗口长度一样,结束位置肯定也是一样的),时间窗口对应的容器
所以,如果我们要获取当前时间节点的数据和在当前时间节点添加数据的计算方法:
首先,计算出两个关键数据:容器下标:idx,时间窗口起始位置:windowStart
当从数据中去那容器数据时为空,创建新容器,存入数组。当然这里涉及到乐观锁的运用
当不为空,且当前时间和老容器的其实位置相同时:该事件节点和之前某次操作的时间差在500ms以内,使用同一个容器存储数据
如果当前时间节点比上次操作的时间节点超过了500ms(甚至更久),修改容器中的时间起始位置,重置容器的数据(涉及悲观锁的运用)
以上就是sentinel的滑动时间窗口的核心逻辑
当我们需要统计当前时间节点所在的1个单位时间内的数据时:
也就是:当我们需要获取当前时间节点的数据时:直接读取数组中的数据,如果数组中的数据windowWrap的时间起点和当前时间的时间起点不超过intervalInMs(在一个时间单位中),就取出
当需要往容器中添加数据时:我们在LeayArray的子类(OccupiableBucketLeapArray)找到了实现:
以上就是:sentinel的滑动时间窗口的实现核心逻辑