Alibaba Sentinel 源码阅读(Part 2 LeapArray)

原创
2018/10/21 12:00
阅读数 1.6K

前言

这一篇是上一篇的继续,如果不了解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() {
        WindowWrap<MetricBucket> wrap = data.currentWindow();
        wrap.value().addSuccess();
    }

获取时间窗口内统计数据

@Override
    public long success() {
        data.currentWindow();
        long success = 0;

        List<MetricBucket> list = data.values();
        for (MetricBucket window : list) {
            success += window.success();
        }
        return success;
    }

所以重点的方法就是 data.currentWindow()方法了

protected final AtomicReferenceArray<WindowWrap<T>> array;

public LeapArray(int windowLengthInMs, int intervalInSec) {
        this.windowLengthInMs = windowLengthInMs;
        this.intervalInMs = intervalInSec * 1000;
        this.sampleCount = intervalInMs / windowLengthInMs;
        // 初始化容量大小
        this.array = new AtomicReferenceArray<WindowWrap<T>>(sampleCount);
    }


  /**
     * Get window at provided timestamp.
     *
     * @param time a valid timestamp
     * @return the window at provided timestamp
     */
    public WindowWrap<T> currentWindow(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<T> old = array.get(idx);
            if (old == null) {
                WindowWrap<T> window = new WindowWrap<T>(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<T>(windowLengthInMs, time, newEmptyBucket());
            }
        }
    }


这部分的内容会维持一个有效的环形数组以统计数据,具体要自己debug 看了。

总结

这里也只是把大致流程梳理了一下方便大家看源码而已,很多地方没有具体分析,这部分还是需要自己亲力亲为。

参考

Window Sliding Technique

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部