【LongAdder】Sentinel 使用的黑科技

1 sentinel 统计 原理滑动窗口

timeId代表一个WindowWrap,其中内部有个window字段,包含一个LongArray数组,分别记录【PASS, BLOCK, EXCEPTION, SUCCESS, RT, OCCUPIED_PASS;】类型的统计值

qps的计算就是获取当前时间窗口的计数除以时间计算得到的。

在当前时间点中进入的请求,会被统计到当前时间对应的时间窗口中。计算qps时,会用当前采样的时间窗口中对应的指标统计值除以时间间隔,就是具体的qps.

    public double passQps() {
        return (double)this.rollingCounterInSecond.pass() / this.rollingCounterInSecond.getWindowIntervalInSec();
    }


    public long pass() {
        this.data.currentWindow();
        long pass = 0L;
        List<MetricBucket> list = this.data.values();

        MetricBucket window;
        for(Iterator var4 = list.iterator(); var4.hasNext(); pass += window.pass()) {
            window = (MetricBucket)var4.next();
        }

        return pass;
    }

2 LongAdder 源码 (空间换时间,分散并发压力的又一个杰作,分段锁思想)

LongAdder 到底用了什么黑科技能做到高性比 AtomicLong 还要好呢呢?对于同样的一个 add() 操作,上文说到 AtomicLong 只对一个 Long 值进行 CAS 操作。而 LongAdder 是针对 Cell 数组的某个 Cell 进行 CAS 操作 ,把线程的名字的 hash 值,作为 Cell 数组的下标,然后对 Cell[i] 的 long 进行 CAS 操作。简单粗暴的分散了高并发下的竞争压力。

add() 操作步骤:

  1. 如果 cells 数组不为空,对参数进行 casBase 操作,如果 casBase 操作失败。可能是竞争激烈,进入第二步。
  2. 如果 cells 为空,直接进入 longAccumulate();
  3. m = cells 数组长度减一,如果数组长度小于 1,则进入 longAccumulate()
  4. 如果都没有满足以上条件,则对当前线程进行某种 hash 生成一个数组下标,对下标保存的值进行 cas 操作。如果操作失败,则说明竞争依然激烈,则进入 longAccumulate().

可见,操作的核心思想还是基于 cas。但是 cas 失败后,并不是傻乎乎的自旋,而是逐渐升级。升级的 cas 都不管用了则进入 longAccumulate() 这个方法。

看上去 LongAdder 性能全面超越了 AtomicLong。为什么 jdk 1.8 中还是保留了 AtomicLong 的实现呢?其实我们可以发现,LongAdder 使用了一个 cell 列表去承接并发的 cas,以提升性能,但是 LongAdder 在统计的时候如果有并发更新,可能导致统计的数据有误差。

如果用于自增 id 的生成,就不适合使用 LongAdder 了。这个时候使用 AtomicLong 就是一个明智的选择。而在 Sentinel 中 LongAdder 承担的只是统计任务,且允许误差。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页