前言
这一篇是上一篇的继续,如果不了解Sentinel ,请先阅读[Alibaba Sentinel 源码阅读(Part1 执行流程)](Alibaba Sentinel 源码阅读(Part1 执行流程))
入口
在上一篇我们看到 我们获取的所有信息,都是从StatisticNode
的这两个数据结构中获取的
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL); /** * Holds statistics of the recent 120 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. */ private transient Metric rollingCounterInMinute = new ArrayMetric(1000, 2 * 60);
rollingCounterInMinute
这个两分钟之内的每一秒中数据的一个list,而每一秒中的数据是存储在 MetricBucket
,
ArrayMetric
// ArrayMetric 实现了Metric 接口,同时包含了 MetricsLeapArray数据结构,接口的实现就是通过这个MetricsLeapArray来实现的// MetricsLeapArray 是从 LeapArray 继承的,所以这一篇的重点就是LeapArray了public class ArrayMetric implements Metric { private final MetricsLeapArray data; /** * Constructor * * @param windowLengthInMs a single window bucket's time length in milliseconds. * @param intervalInSec the total time span of this {@link ArrayMetric} in seconds. */ public ArrayMetric(int windowLengthInMs, int intervalInSec) { this.data = new MetricsLeapArray(windowLengthInMs, intervalInSec); }}
LeapArray
实际上就是一个环形数组,来给张官方的图就明白了
看文档其实很清晰,整个是基于时间窗口滑动算法来实现的
新增当前统计数据
@Override public void addSuccess() { WindowWrapwrap = data.currentWindow(); wrap.value().addSuccess(); }
获取时间窗口内统计数据
@Override public long success() { data.currentWindow(); long success = 0; Listlist = data.values(); for (MetricBucket window : list) { success += window.success(); } return success; }
所以重点的方法就是 data.currentWindow()方法了
protected final AtomicReferenceArray> array;public LeapArray(int windowLengthInMs, int intervalInSec) { this.windowLengthInMs = windowLengthInMs; this.intervalInMs = intervalInSec * 1000; this.sampleCount = intervalInMs / windowLengthInMs; // 初始化容量大小 this.array = new AtomicReferenceArray >(sampleCount); }
/** * Get window at provided timestamp. * * @param time a valid timestamp * @return the window at provided timestamp */ public WindowWrapcurrentWindow(long time) { long timeId = time / windowLengthInMs; // Calculate current index. int idx = (int)(timeId % array.length()); // Cut the time to current window start. time = time - time % windowLengthInMs; while (true) { WindowWrap old = array.get(idx); if (old == null) { WindowWrap window = new WindowWrap (windowLengthInMs, time, newEmptyBucket()); if (array.compareAndSet(idx, null, window)) { return window; } else { Thread.yield(); } } else if (time == old.windowStart()) { return old; } else if (time > old.windowStart()) { if (updateLock.tryLock()) { try { // if (old is deprecated) then [LOCK] resetTo currentTime. return resetWindowTo(old, time); } finally { updateLock.unlock(); } } else { Thread.yield(); } } else if (time < old.windowStart()) { // Cannot go through here. return new WindowWrap (windowLengthInMs, time, newEmptyBucket()); } } }
这部分的内容会维持一个有效的环形数组以统计数据,具体要自己debug 看了。
总结
这里也只是把大致流程梳理了一下方便大家看源码而已,很多地方没有具体分析,这部分还是需要自己亲力亲为。