Sentinel -- FLOW SLOT核心原理篇

这个节点是sentinel流控接口,主要承担的作用是限流和预热。还是老套路,在介绍源码之前先介绍一下源码中用到的几个核心原理,这样大家看源码相对轻松一些。

1、核心算法

1.1 漏洞算法和令牌通算法

见《常见限流算法介绍(漏桶算法、令牌桶算法)及实现

1.2预热桶算法

系统在初始化或者长时间处于低利用率下,系统由于所依赖资源的限制,并不能立马达到它正常的服务水平;例如系统依赖的缓存过期导致新的请求会直接请求 db,再比如很多系统使用了连接池,长时间的 idle 情况下连接池只会保持少量的连接,新的请求会重新创建连接,这些都是耗时操作,完成这些操作之后,系统才能到达正常的服务水平。这时候变需要预热桶算法。

预热桶算法本质是按照一个 y=mx+b的一个线性函数,将投入令牌桶的时间间隔又大到小的过程。下面是guava源码中的一个函数图。

throttling
             |        
       cold  +              |   /
    interval |              |  /.
             |              | / .  
             |              |/  .   
             |              +   .   ← "f(storedPermits)"  
             |             /|   .
             |            / |   .
             |           /  |   .
      stable +----------/   |   .   ← "warmup period"  
    interval |          .   |   .      is the area of the trapezoid between thresholdPermits and maxPermits
             |          .   |   .
             |          .   |   .
           0 +----------+---|---+--------------→ storedPermits
             0 thresholdPermits maxPermits

我们可以定义一个冷却因子(coldFactor) ,令系统处于最冷的状态下获取一个令牌的时长 coldInterval = stableInterval * coldFactor ;「预热桶」从最冷状态到完成预热进入稳定期有个转折点,到达这个转折点时的令牌数量我们用 thresholdPermits 表示;这样,我们就获得了一个获取(一个)令牌的时长随着令牌数量变化的连续函数 f(storedPermits) : 

      1)  0 <= storedPermits <= thresholdPermits 时;f(storedPermits) = stableInterval; // 常数函数,函数值始终为 stableInterval ;       

       2) thresholdPermits <= storedPermits <= maxPermits 时,f(storedPermits) = (coldInterval - stableInterval) * storedPermits / (maxPermits -thresholdPermits); // 正比例函数,比例常数为 (coldInterval - stableInterval) / (maxPermits - thresholdPermits) ;

在上面这张图中,我们画一条与 x 轴垂直的线 n,这条线与函数曲线的交点的纵坐标当前 storedPermits 数量下获取单个令牌所需的时间;

当我们从右向左移动 n 时,表示系统接收到请求,令牌正在被消耗,假设系统连续接收到 k 个请求,获取对应令牌所需要的时间为:t = f(maxPermits) + f(maxPermits - 1) + f(maxPermits - 2) + ... + f(maxPermits - k),通过微积分的知识可以看出来这是在求函数 f 在 maxPermits - k 到 maxPermits 区间的定积分,可以用这个区间的函数图形的面积表示。也就是说图中梯形的面积为我们的预热时间。图中矩形的面积为预热时间的一半。网上及guava源码出的解释:英文冷却因子是3,然后code inteval 到 stable inteval的值是stable inteval到0值的2倍所以预热面积也是2倍的关系。

 

2、规则加载FlowRuleManager

1 FlowRuleManager初始化属性

存放限流规则的集合

private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();

规则加载/更新

// 规则加载/更新器
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
// 私有内部类实现(策略模式)限流属性更新加载器
private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {

    @Override
    public void configUpdate(List<FlowRule> value) {
        Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
        if (rules != null) {
            flowRules.clear();
            flowRules.putAll(rules);
        }
        RecordLog.info("[FlowRuleManager] Flow rules received: " + flowRules);
    }

    @Override
    public void configLoad(List<FlowRule> conf) {
        Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
        if (rules != null) {
            flowRules.clear();
            flowRules.putAll(rules);
        }
        RecordLog.info("[FlowRuleManager] Flow rules loaded: " + flowRules);
    }
}

变化规则集合

// 规则更新执行的调用者
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();

创建一个延迟线程池(数据统计监听器)

@SuppressWarnings("PMD.ThreadPoolCreationRule")
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
    new NamedThreadFactory("sentinel-metrics-record-task", true));

规则初始化和开启数据统计监听任务

static {
    // 设置监听
    currentProperty.addListener(LISTENER);
    // 每一秒钟调用MetricTimerListener的方法
    SCHEDULER.scheduleAtFixedRate(new MetricTimerListener(), 0, 1, TimeUnit.SECONDS);
}

 

 

 

 

 

3、FLOW SLOT源码解读

 

posted on 2016-03-09 09:00  duanxz  阅读(680)  评论(0编辑  收藏  举报