【sentinel】令牌桶算法在Sentinel中的应用

news2025/2/12 11:37:36

令牌桶算法

令牌桶算法介绍

令牌桶算法,又称token bucket。


从图中我们可以看到,令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。

令牌桶算法实现

令牌桶算法实现代码如下:

package com.morris.user.demo;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 令牌桶算法
 */
@Slf4j
public class TokenBucketDemo {

    public static void main(String[] args) {

        TokenBucket sideWindow = new TokenBucket(2);
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(new Random().nextInt(3000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info(finalI + "--> " + sideWindow.canPass());
            }).start();
        }
    }
    
    public static class TokenBucket {
        // 上一次请求通过的时间
        private long lastRequestTime = System.currentTimeMillis();
        // 往桶内加入令牌的速率
        private final long rate;
        // 桶的容量
        private static final long CAPACITY = 10;
        // 桶内当前令牌的数量,要使用小数
        private double tokens = 0;

        public TokenBucket(int maxCount) {
            this.rate = maxCount;
        }

        public synchronized boolean canPass() {
            long now = System.currentTimeMillis();

            // 计算从lastRequestTime~now时间段应该增加的令牌数
            this.tokens = Math.min(CAPACITY, this.tokens + (now - lastRequestTime) * 1.0D / 1000 * this.rate);

            lastRequestTime = now;

            if (this.tokens < 1) {
                // 令牌不足
                return false;
            }
            this.tokens--;
            return true;
        }
    }
}

令牌桶限流原理


工作过程:

  • 请求流量从令牌桶中获取令牌,持有令牌就放行,否则就会被拒绝
  • 假如设置阈值每秒允许100个请求通过,则请求发送的速率为100个请求每秒,r=100/s
  • 需要令牌生产的速率为1秒/r,即1/100,每10毫秒产生一个令牌
  • 令牌桶容量为b,已满多余的令牌将被丢弃
  • 令牌桶为空请求被拒
  • 允许突发流量最大突发流量为令牌桶容量b
  • 请求通过相应的令牌从令牌桶中移除

假如系统平时流量很低,突然陡增的流量需要缓慢增加。具体到令牌桶,可以通过控制令牌的生产速率来对流量进行控制。令牌生产速率如何控制?

我们在使用sentinel设置QPS的预热流控时,需要设置阈值count和预热时长warmUpPeriodInSec,下面梳理下与下图坐标图的关系。


坐标图说明:

参数说明
x轴表示令牌桶中的令牌数量
y轴生产一个令牌需要的时间(秒)
stableInterval稳定生产一个令牌需要的时间
coldInterval生产一个令牌需要的最大时长,与冷启动因子coldFactor有关,可以通过-Dcsp.sentinel.flow.cold.factor设置,默认为3。
warmUpPeriodInSec预热时长,默认为10秒。对应到坐标图中为(2)梯形面积
thresholdPermits(warningToken)令牌桶中的一个阈值,超过该值时开启预热
maxPermits(maxToken)令牌桶中最大令牌数

换算关系:

  • count,已知由用户设置,例如每秒允许通过100个请求
  • warmUpPeriodInSec,已知由用户设置,默认为10秒,时间区域上红色(2)梯形区域
  • coldFactor,已知默认为3
stableInterval = 1/count

coldInterval = stableInterval * coldFactor

由于coldFactor默认为3,y轴stableIntervalcoldInterval的距离是0stableInterval的距离两倍,所以从thresholdPermits到0的时间是从maxPermits到thresholdPermits时间的一半,也就是冷启动周期的一半。因为梯形的面积等于warmupPeriod,所以长方形面积是梯形面积的一半,长方形的面积是warmupPeriod/2。

根据长方形面积公式:长 * 宽  = 面积,可得:

坐标时间(1)长方形区域面积 = 长(thresholdPermits(warningToken)) * 宽(stableInterval)

坐标时间(1)长方形区域面积 = 0.5 * warmUpPeriodInSec

thresholdPermits(warningToken) = 0.5 * warmUpPeriodInSec / stableInterval

根据梯形的面积公式:(上低 + 下低)* 高 / 2,可得:

warmupPeriod = (stableInterval + coldInterval)* (maxPermits - thresholdPermits)/ 2

maxPermits = thresholdPermits + 2 * warmupPeriod / (stableInterval + coldInterval)

备注:由斜率公式k=(y1-y2)➗(x1-x2),得出斜率如下:

slope = (coldInterval-stableInterval)(maxPermits(maxToken)-thresholdPermits(warningToken))

Sentinel每秒生产一次令牌,将新生产的令牌放入令牌桶,并记录本次生产令牌的时间,当下次生产时,根据当前时间与上一次生产令牌的时间间隔计算、以及每个令牌的生产间隔时间计算出本次需要生产的令牌数。

服务第一次启动时,或者接口很久没有被访问,都会导致当前时间与上次生产令牌的时间相差甚远,所以第一次生产令牌将会生产maxPermits个令牌,直接将令牌桶装满。由于令牌桶已满,接下来10s就是冷启动阶段。

由于冷启动阶段生产令牌的间隔时间比较正常消费速度慢,因此随着时间的推移,桶中的剩余令牌数就会趋近于thresholdPermits,生产令牌的时间间隔也会从coldInterval降低到stableInterval。当桶中剩余令牌数小于thresholdPermits时,冷启动结束,系统进入稳定状态,生产令牌的时间间隔为stableInterval,每秒生产的令牌数就等于QPS。

Sentinel并不会在请求通过时减少令牌桶中的令牌数量,而是在下一秒生产新的令牌时,再减去桶中与上一秒通过的请求数相等数量的令牌,这就是Sentinel官方介绍的令牌自动掉落。

Sentinel没有在每个请求通过时从令牌桶取走令牌,那么Sentinel是如何控制QPS的呢,我们再来看一张图:

  • x1:当前令牌桶中超过thresholdPermits的令牌数量;

  • y1:y1加上stableInterval等于当前令牌生产的时间间隔;

根据斜率和x1可算出y1:

y1 = slope * x1

y1加上stableInterval即为当前的令牌生产速率。

当前秒生产令牌的时间间隔为:

slope * (storedTokens - thresholdPermits) + stableInterval 

由于:stableInterval  = 1.0(1秒) / 限流阈值(count)

所以上述等式 =  slope * (storedTokens - thresholdPermits) + 1.0 / count

最后算得当前时间戳的QPS阈值为:

1.0 / slope * (storedTokens - thresholdPermits) + 1.0 / count

当令牌桶中的令牌数小于thresholdPermits(warningToken)时,令牌按照固定速率生产,请求流量稳定。当令牌数大于thresholdPermits(warningToken)时,开启预热。此段时期,生产的令牌的速率小于令牌滑落的速度,一段时间后,令牌小于等于thresholdPermits(warningToken),请求回归到稳定状态,预热结束。

令牌桶算法在sentinel中的应用

漏桶算法主要用于流控规则中流控效果为Warm UP。

Sentinel中使用的令牌桶算法,是参考着Guava中的令牌桶算法来的。

WarmUpController的构造方法中会对告警阈值。斜率等参数进行计算并初始化。

public WarmUpController(double count, int warmUpPeriodInSec) {
    construct(count, warmUpPeriodInSec, 3);
}

private void construct(double count, int warmUpPeriodInSec, int coldFactor) {

    if (coldFactor <= 1) {
        throw new IllegalArgumentException("Cold factor should be larger than 1");
    }

    // 最大阈值
    this.count = count;

    // 冷启动因子3
    this.coldFactor = coldFactor;

    // 告警阈值,超过该值,开启冷启动
    // thresholdPermits = 0.5 * warmupPeriod / stableInterval.
    warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
    // / maxPermits = thresholdPermits + 2 * warmupPeriod / (stableInterval + coldInterval)
    // 令牌桶中允许的最大令牌数
    maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));

    // slope
    // slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
    // 斜率
    slope = (coldFactor - 1.0) / count / (maxToken - warningToken);

}

再来看下限流时是怎么做的?

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController#canPass(com.alibaba.csp.sentinel.node.Node, int, boolean)

public boolean canPass(Node node, int acquireCount, boolean prioritized) {
    // 当前的QPS
    long passQps = (long) node.passQps();

    // 上一个时间窗口的QPS
    long previousQps = (long) node.previousPassQps();

    // 计算令牌
    syncToken(previousQps);

    // 开始计算它的斜率
    // 如果进入了警戒线,开始调整他的qps
    long restToken = storedTokens.get();
    if (restToken >= warningToken) {
        // 开启预热
        long aboveToken = restToken - warningToken;
        // 消耗的速度要比warning快,但是要比慢
        // current interval = restToken*slope+1/count
        // 根据斜率计算出预热时的QPS
        double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));
        if (passQps + acquireCount <= warningQps) {
            return true;
        }
    } else {
        // 不开启预热
        if (passQps + acquireCount <= count) {
            return true;
        }
    }

    return false;
}

protected void syncToken(long passQps) {
    // 当前时间
    long currentTime = TimeUtil.currentTimeMillis();
    // 当前秒数
    currentTime = currentTime - currentTime % 1000;
    long oldLastFillTime = lastFilledTime.get();
    if (currentTime <= oldLastFillTime) {
        return;
    }

    // 当前令牌中的桶数
    long oldValue = storedTokens.get();
    // 计算新产生的令牌数
    long newValue = coolDownTokens(currentTime, passQps);

    // 将新产生的令牌放入令牌桶
    if (storedTokens.compareAndSet(oldValue, newValue)) {
        // 从令牌桶中减去已使用的令牌
        long currentValue = storedTokens.addAndGet(0 - passQps);
        if (currentValue < 0) {
            storedTokens.set(0L);
        }
        lastFilledTime.set(currentTime);
    }

}

private long coolDownTokens(long currentTime, long passQps) {
    long oldValue = storedTokens.get();
    long newValue = oldValue;

    // 添加令牌的判断前提条件:
    // 当令牌的消耗程度远远低于警戒线的时候
    if (oldValue < warningToken) {
        newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);
    } else if (oldValue > warningToken) {
        if (passQps < (int)count / coldFactor) {
            newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);
        }
    }
    return Math.min(newValue, maxToken);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】0-2 深度学习相关数学概念的简单总结-概率与信息论

样本空间 样本空间是一个实验或随机试验所有可能结果的集合&#xff0c;随机试验中的每个可能结果称为样本点。例如投掷一个骰子&#xff0c;那么样本空间就是{1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6}。 随机变量 随机变量&#xff0c;顾名思义…

IDEA全局设置JDK、Maven、编码格式

本机已安装JDK版本&#xff1a; 本机已安装Maven版本&#xff1a; 一、IDEA设置全局JDK设置 File---->New Projects Settings---->Structure for New Projects... 先将本地安装的JDK添加到SDK 将项目SDK设置为刚刚添加的本地JDK版本 File---->New Projects Settings-…

Webstorm 加载vue项目时,特别卡顿,完美解决。觉得有用加好友打赏

觉得有用加好友打赏&#xff1a;QQ&#xff1a;854138497 上图cpu直接干满。 根据上图提示&#xff0c;直接 disable hints&#xff0c;或者到下图的settings里面设置。 Code vision取消后&#xff0c;webstorm 明显就不卡了。记得重启webstorm。 还有一种方式&#xff0c;根…

完美解决MacOS关于ld: library not found for -lnetcdff错误

1. 问题描述 在使用Intel版本的Mac编译某个程序时出现了错误&#xff0c;显示如下图。 说明&#xff1a;libnetcdff是netcdf的Fortran的接口&#xff0c;如下ChatGPT解释。 2. 出现的原因 原因是Makefile中定义的静态库链接并没有在系统默认的库路径下找到&#xff0c;默认…

Jmeter HTTP Cookie管理器的使用

目录 前言&#xff1a; 1、在HTTP信息头管理器组件中添加Cookie信息 &#xff08;1&#xff09;测试计划内包含的元件 &#xff08;2&#xff09;请求取样器内容 &#xff08;3&#xff09;HTTP信息头管理器内容 &#xff08;4&#xff09;查看结果 2、使用HTTP Cookie管…

你想知道的 MySQL 性能调优方式,都在这里

前言&#xff1a;对于性能测试来说&#xff0c;数据库的监控是尤为的重要&#xff0c;以及对数据库进行调优&#xff0c;用以提升性能&#xff0c;是能在短期内有显著的效果的&#xff0c;本文针对MySQL数据库进行分析如何定位MySQL数据库的性能问题。 关键 MySQL 统计指标 如…

一张软考系统架构设计师证书到底能证明了什么?

软考证书证明你考过了软考高级架构&#xff0c;拥有了评高级职称的资格&#xff01; 证书的作用还有&#xff1a; 1、以考代评&#xff1a;软考证书可以用来评职称 2、积分落户&#xff1a;可用于积分落户&#xff0c;加相应的分&#xff0c;软考高级职业资格都几乎可以直接…

Tinker 组件修复,踩坑

1、You need to use a Theme.AppCompat theme (or descendant) with this activity. 复现步骤 补丁加载成功之后重启应用&#xff0c;再退出应用重进闪退 日志 TinkerUncaughtHandler catch exception:java.lang.IllegalStateException: You need to use a Theme.AppCompat th…

Cesium教程(十七):淹没分析

Cesium教程(十七):淹没分析 1、什么是淹没分析 淹没分析是根据某片区域的地形及洪水流量速度,动态模拟该地形区域水位逐渐上涨的淹没过程。该功能可适用于山区、丘陵等地形起伏较大区域,模拟洪水涨到安全限定高度的淹没过程,为防洪水救灾提供一定的参考。此外,还可以为河…

红帽认证常见答疑(二):电脑配置、实验环境和考试环境、可以自学吗

学习红帽需要配置什么样的电脑&#xff1f; RHCE推荐学员自己的电脑内存在16G左右&#xff0c;RHCA推荐学员电脑内存在32-64G&#xff0c;且最好配置128G以上的固态硬盘&#xff0c;如果自己没有该配置的电脑&#xff0c;誉天可以提供远程学习环境&#xff0c;可以随时随地连接…

Java中常用的工具类——字符串类

提示&#xff1a;字符串类是编程中最常用的一种数据类型&#xff0c;比较重要❗ 文章目录 前言一、字符串类创建对象方式静态创建动态创建 二、String字符串内容不可改变三、字符串常用方法length方法charAt方法substring方法indexOf与lastIndexOfindexOf方法lastIndexOf方法 替…

企业即时通信软件有哪些?要如何选择?

随着企业数字化转型的加速&#xff0c;即时通信软件已成为企业日常沟通的重要工具。但是市面上的企业即时通信软件种类繁多&#xff0c;如何选择适合自己企业的即时通信软件呢&#xff1f;本文将为大家介绍几款常用的企业即时通信软件以及如何选择。 企业微信 企业微信是腾讯公…

牛逼!Windows竟然也能运行QEMU虚拟机!

这些天研究 Miracast&#xff0c;又倒腾了开发用的虚拟机&#xff0c;但是发现了新的东西就是 QEMU ——全宇宙最强的硬件模拟器&#xff0c;原来这玩意可以在Windows上跑虚拟机的。 环境部署 msys2mingw-w64-qemu系统isoHyper-V 代替 KVM/Linux 安装 在 Msys2 Repo下载exe…

【服务器数据恢复】RAID5重建导致数据丢失的数据恢复案例

服务器数据恢复环境&#xff1a; HP某型号服务器&#xff0c;5块硬盘组建了一组raid5磁盘阵列。 服务器故障&分析&#xff1a; 服务器在工作过程中&#xff0c;raid5磁盘阵列中的一块磁盘掉线&#xff0c;由于raid5的容错特点&#xff0c;raid阵列未受影响&#xff0c;工作…

弥合风险缺口 筑牢共享安全|2023开放原子全球开源峰会开源安全技术与实践分论坛成功举办

6 月 12 日&#xff0c;2023 开放原子全球开源峰会开源安全技术与实践分论坛成功举办。本场论坛围绕开源软件供应链、开源漏洞信息共享机制、开源安全测试工具、人工智能新技术对开源安全领域的影响等方向分享了技术和最佳实践&#xff0c;讨论了开源安全领域最新产业动态。 开…

认知升维: 道、法、术、器、势

身处在严重内卷的时代&#xff0c;我们各方面面临转型调整&#xff0c;提升认知&#xff0c;更好适应说变就变|比翻书变脸还快的节奏&#xff0c;需要自身不断精进&#xff0c;不断挑战自我&#xff0c;这个世界上&#xff1a;一切都在变&#xff0c;唯独不变的是变化本身&…

enscape3.5新版本更新,神仙功能还不少

Enscape于2015年首次发布&#xff0c;已成为建筑可视化市场的关键工具之一&#xff0c;在上一次CGarchitect渲染调查中名列前十。 图片来源于CGarchitect渲染调查报告 相对于同行的其他渲染器&#xff0c;Enscape最大的优势在于它并未因为其易于使用特性而降低了品质&#xff…

一篇文章搞定《CoordinatorLayout完成电商首页》

一篇文章搞定《CoordinatorLayout完成电商首页》 前言NestedScrollNestedScrollingParentNestedScrollingChildNestedScrollingChildHelper 和 NestedScrollingParentHelper CoordinatorLayoutCoordinatorLayout知识点讲解实现后续功能 通过自定义Behavior实现Fling效果认识Beh…

操作系统课程设计 模拟FAT文件系统的设计与实现

一、 目的与要求 1.研究FAT文件系统的物理布局。 2.掌握FAT文件系统中目录的结构与目录项定义。 3.掌握文件操作如建立目录&#xff0c;建立文件&#xff0c;删除文件&#xff0c;复制文件时&#xff0c;对FAT和目录的操作步骤。 4.合理设计文件系统布局与数据结构&#xff08…

Day_49归并排序

目录 一. 归并排序的思想 1.归并排序的过程 2. 两种实现方式&#xff1a; 2.1 非递归实现方式 2.2递归实现方式 二. 归并排序的代码实现 1. 数组的辅助空间及初始化 2. 核心代码 2.1每个小组的基本设置 2.2小组内部的排序 三. 代码展示 四. 运行结果 五. 总结 一. 归并排序…