sentinel

Sentinel原理:滑动窗口

StatisticSlot其他所有的Slot的基础。包括各种限流,熔断的规则,都是基于StatisticSlot统计出来的结果进行规则校验的。本篇文章我将深入研究下 Sentinel 是如何进行qps等指标的统计的,首先要确定的一点是,sentinel是基于滑动时间窗口来实现的。

Slot是从第一个往后一直传递到最后一个的,且当信息传递到StatisticSlot时,这里就开始进行统计了,统计的结果又会被后续的Slot所采用,作为规则校验的依据。

StatisticSlot中就是做了三件事:

  • 1.通过node中的当前的实时统计指标信息进行规则校验
  • 2.如果通过了校验,则重新更新node中的实时指标数据
  • 3.如果被block或出现了异常了,则重新更新node中block的指标或异常指标

  

从上面的代码中可以很清晰的看到,所有的实时指标的统计都是在node中进行的。

首先我们知道这里的node是一个 DefaultNode 实例,这里特别补充一个 DefaultNode 和  ClusterNode 的区别:

DefaultNode:保存着某个resource在某个context中的实时指标,每个DefaultNode都指向一个ClusterNode

ClusterNode:保存着某个resource在所有的context中实时指标的总和,同样的resource会共享同一个ClusterNode,不管他在哪个context中

完整流程:

timeId 是用来表示一个 WindowWrap 对象的时间id。

每一个 WindowWrap 对象由三个部分组成:

  • windowStart: 时间窗口的开始时间,单位是毫秒
  • windowLength: 时间窗口的长度,单位是毫秒
  • value: 时间窗口的内容,在 WindowWrap 中是用泛型表示这个值的,但实际上就是 Window 类

有了这个概念后,我们就可以通过时间窗口来计算统计一段时间内的诸如:qps,rt,threadNum等指标。 

public abstract class LeapArray<T> {

    // 时间窗口的长度
    protected int windowLength;
    // 采样窗口的个数
    protected int sampleCount;
    // 以毫秒为单位的时间间隔
    protected int intervalInMs;

    // 采样的时间窗口数组
    protected AtomicReferenceArray<WindowWrap<T>> array;

    /**
     * LeapArray对象
     * @param windowLength 时间窗口的长度,单位:毫秒
     * @param intervalInSec 统计的间隔,单位:秒
     */
    public LeapArray(int windowLength, int intervalInSec) {
        this.windowLength = windowLength;
        // 时间窗口的采样个数,默认为2个采样窗口
        this.sampleCount = intervalInSec * 1000 / windowLength;
        this.intervalInMs = intervalInSec * 1000;

        this.array = new AtomicReferenceArray<WindowWrap<T>>(sampleCount);
    }
}

 在 LeapArray 中创建了一个 AtomicReferenceArray 数组,用来对时间窗口中的统计值进行采样。通过采样的统计值再计算出平均值,就是我们需要的最终的实时指标的值了。

拿到当前时间窗口:data.currentWindow()

根据当前时间,算出该时间的timeId,并根据timeId算出当前窗口在采样窗口数组中的索引idx:long timeId = time / windowLength; int idx = (int)(timeId % array.length());

sentinel中就使用到了滑动窗口算法来进行统计,实际上sentinel中的滑动窗口用一个圆形来描述更合理一点。

StatisticSlot通过滑动窗口来统计数据,FlowSlot是真正限流的逻辑。

  sentinel主要根据FlowSlot中的流控进行流量控制,其中RateLimiterController就是漏桶算法的实现 。

 1、首先计算出当前请求(1次请求)平摊到1s内的时间花费,如果当前时间-上次请求时间>花费时间

设置本次请求时间,直接返回

2、否则,排队等待。并计算等待时间,上次请求时间+花费时间-当前时间>最大等待时间,就会直接丢弃。

3、没有超过,就更新上次请求时间+=花费时间(cas操作),并且之后又做了一次判断是否超过最大等待时间(个人理解是cas比较耗时,所以再次判断可能不超过了)

4、然后睡眠等待Thread.sleep(waittime)

 sentinel的令牌桶实现基于Guava,代码在WarmUpController中。 

1、下次再看。。。

关于限流的算法大体上可以分为四类:固定窗口计数器、滑动窗口计数器、漏桶(也有称漏斗,英文Leaky bucket)、令牌桶(英文Token bucket)。

 熔断降级

 熔断状态机

  • 熔断有三种状态,分别为OPEN、HALF_OPEN、CLOSED。
  • OPEN:表示熔断开启,拒绝所有请求
  • HALF_OPEN:探测恢复状态,如果接下来一个请求通过则结束熔断,否则继续熔断
  • CLOSED:表示熔断关闭,请求顺利通过

 RT:response time 响应时间。如果超过阈值,那么在接下来的时间窗口中,对这个方法的调用,会自动熔断(抛出DegradeException)

控制台配置熔断规则:

 熔断降级涉及到的几个属性如下:

 达到阈值后,系统的默认提示是一段英文,很不友好,我们可以自定义兜底方法。在 @SentinelResource 注解中进一步配置 blockHandler、fallback 属性字段。

  • blockHandler:主观层面,如果被限流或熔断,则调用该方法,进行兜底处理.

  • fallback:对业务的异常兜底,比如,执行过程中抛了各种Exception,则调用该方法,进行兜底处理。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值