高可用之限流-07-token bucket 令牌桶算法

限流系列

开源组件 rate-limit: 限流

高可用之限流-01-入门介绍

高可用之限流-02-如何设计限流框架

高可用之限流-03-Semaphore 信号量做限流

高可用之限流-04-fixed window 固定窗口

高可用之限流-05-slide window 滑动窗口

高可用之限流-06-slide window 滑动窗口 sentinel 源码

高可用之限流-07-token bucket 令牌桶算法

高可用之限流 08-leaky bucket漏桶算法

高可用之限流 09-guava RateLimiter 入门使用简介 & 源码分析

令牌桶算法

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。

典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

从原理上看,令牌桶算法和漏桶算法是相反的,一个“进水”,一个是“漏水”。

Google的Guava包中的RateLimiter类就是令牌桶算法的解决方案。

漏桶算法和令牌桶算法的选择

漏桶算法与令牌桶算法在表面看起来类似,很容易将两者混淆。但事实上,这两者具有截然不同的特性,且为不同的目的而使用。

漏桶算法与令牌桶算法的区别在于,漏桶算法能够强行限制数据的传输速率,令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。

需要注意的是,在某些情况下,漏桶算法不能够有效地使用网络资源,因为漏桶的漏出速率是固定的,所以即使网络中没有发生拥塞,漏桶算法也不能使某一个单独的数据流达到端口速率。

因此,漏桶算法对于存在突发特性的流量来说缺乏效率。而令牌桶算法则能够满足这些具有突发特性的流量。

通常,漏桶算法与令牌桶算法结合起来为网络流量提供更高效的控制。

算法描述与实现

描述

假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中(每秒会有r个令牌放入桶中);

假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;

当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌(不同大小的数据包,消耗的令牌数量不一样),并且数据包被发送到网络;

如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外(n个字节,需要n个令牌。该数据包将被缓存或丢弃);

算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。

对于在流量限制外的数据包可以以不同的方式处理:

1)它们可以被丢弃;

2)它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;

3)它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。

实现

添加令牌的时机

我们当然不用起一个定时任务不停的向桶中添加令牌,只要在访问时记下访问时间,下次访问时计算出两次访问时间的间隔,然后向桶中补充令牌,补充的令牌数为

(long) (durationMs * rate * 1.0 / 1000)

其中rate是每秒补充令牌数。

这里可以前傻傻的一个任务不停的执行对比起来,还是比较巧妙的。

初步实现

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.rate.limit.core.core.ILimitContext;
import com.github.houbb.rate.limit.core.util.ExecutorServiceUtil;
import org.apiguardian.api.API;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** 令牌桶算法** @author houbinbin* Created by bbhou on 2017/9/20.* @since 0.0.6*/
public class LimitTokenBucket extends LimitAdaptor {private static final Log LOG = LogFactory.getLog(LimitTokenBucket.class);/*** 令牌的发放速率* <p>* 每一秒发放多少。** @since 0.0.6*/private final long rate;/*** 容量* <p>* 后期暴露为可以配置** @since 0.0.6*/private final long capacity;/*** 令牌数量** @since 0.0.6*/private volatile long tokenNum;/*** 上一次的更新时间** @since 0.0.6*/private volatile long lastUpdateTime;/*** 构造器** @param context 上下文* @since 0.0.4*/public LimitTokenBucket(final ILimitContext context) {// 暂不考虑特殊输入,比如 1s 令牌少于1 的场景long intervalSeconds = context.timeUnit().toSeconds(context.interval());this.rate = context.count() / intervalSeconds;// 8 的数据this.capacity = this.rate * 8;// 这里可以慢慢的加,初始化设置为0// 这样就有一个 warmUp 的过程this.tokenNum = 0;this.lastUpdateTime = System.currentTimeMillis();}/*** 获取锁** @since 0.0.5*/@Overridepublic synchronized boolean acquire() {if (tokenNum < 1) {// 加入令牌long now = System.currentTimeMillis();long durationMs = now - lastUpdateTime;long newTokenNum = (long) (durationMs * 1.0 * rate / 1000);LOG.debug("[Limit] new token is " + newTokenNum);if (newTokenNum > 0) {// 超过的部分将舍弃this.tokenNum = Math.min(newTokenNum + this.tokenNum, capacity);lastUpdateTime = now;} else {// 时间不够return false;}}this.tokenNum--;return true;}}

测试

  • Test.java
import com.github.houbb.heaven.util.util.TimeUtil;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.rate.limit.core.bs.LimitBs;
import com.github.houbb.rate.limit.core.core.ILimit;
import com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket;/*** <p> project: rate-limit-LimitTokenBucketTest </p>* <p> create on 2020/6/22 22:38 </p>** @author binbin.hou* @since 0.0.6*/
public class LimitTokenBucketTest {private static final Log LOG = LogFactory.getLog(LimitTokenBucket.class);/*** 2S 内最多运行 5 次* @since 0.0.5*/private static final ILimit LIMIT = LimitBs.newInstance().interval(1).count(10).limit(LimitTokenBucket.class).build();static class LimitRunnable implements Runnable {@Overridepublic void run() {for(int i = 0; i < 6; i++) {while (!LIMIT.acquire()) {// 等待令牌TimeUtil.sleep(100);}LOG.info("{}-{}", Thread.currentThread().getName(), i);}}}public static void main(String[] args) {new Thread(new LimitRunnable()).start();}}
  • 日志
22:47:19.084 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-0
22:47:19.186 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-1
22:47:19.286 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-2
22:47:19.386 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-3
22:47:19.486 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-4
22:47:19.586 [Thread-1] INFO  com.github.houbb.rate.limit.core.core.impl.LimitTokenBucket - Thread-1-5

相对来说令牌桶还是比较平滑的。

小结

令牌桶算法是网络流量整形和速率限制中最常使用的一种算法。

典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

传送到令牌桶的数据包需要消耗令牌。不同大小的数据包,消耗的令牌数量不一样。令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量。

因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。

原理

ps: 原理相对枯燥,理解即可。

令牌桶是网络设备的内部存储池,而令牌则是以给定速率填充令牌桶的虚拟信息包。每个到达的令牌都会从数据队列领出相应的数据包进行发送,发送完数据后令牌被删除。

请求注解(RFC)中定义了两种令牌桶算法——单速率三色标记算法和双速率三色标记算法,其评估结果都是为报文打上红、黄、绿三色标记。

QoS会根据报文的颜色,设置报文的丢弃优先级,其中单速率三色标记比较关心报文尺寸的突发,而双速率三色标记则关注速率上的突发,两种算法都可工作于色盲模式和非色盲模式。

以下结合这两种工作模式介绍一下RFC中所描述的这两种算法。

1)单速率三色标记算法

网络工程师任务小组(IETF)的RFC文件定义了单速率三色标记算法,评估依据以下3个参数:承诺访问速率(CIR),即向令牌桶中填充令牌的速率;承诺突发尺寸(CBS),即令牌桶的容量,每次突发所允许的最大流量尺寸(注:设置的突发尺寸必须大于最大报文长度);超额突发尺寸(EBS)。

一般采用双桶结构:C桶和E桶。Tc表示C桶中的令牌数,Te表示E桶中令牌数,两桶的总容量分别为CBS和EBS。初始状态时两桶是满的,即Tc和Te初始值分别等于CBS和EBS。令牌的产生速率是CIR,通常是先往C桶中添加令牌,等C桶满了,再往E桶中添加令牌,当两桶都被填满时,新产生的令牌将会被丢弃。

色盲模式下,假设到达的报文长度为B。若报文长度B小于C桶中的令牌数Tc,则报文被标记为绿色,且C桶中的令牌数减少B;若 Tc < B < Te,则标记为黄色,E和C桶中的令牌数均减少B;若 B > Te,标记为红色,两桶总令牌数都不减少。

在非色盲模式下,若报文已被标记为绿色或 B < Tc,则报文被标记为绿色,Tc减少B;若报文已被标记为黄色或 Tc < B < Te,则标记为黄色,且Te减少B;若报文已被标记为红色或 B > Te,则标记为红色,Tc和Te都不减少。

2)双速率三色标记算法

IETF的RFC文件定义了双速率三色算法,主要是根据4种流量参数来评估:CIR、CBS、峰值信息速率(PIR),峰值突发尺寸(PBS)。前两种参数与单速率三色算法中的含义相同,PIR这个参数只在交换机上才有,路由器没有这个参数。该值必须不小于CIR的设置值,如果大于CIR,则速率限制在CIR于PRI之间的一个值。

与单速率三色标记算法不同,双速率三色标记算法的两个令牌桶C桶和P桶填充令牌的速率不同,C桶填充速率为CIR,P桶为PIR;两桶的容量分别为CBS和PBS。用Tc和Tp表示两桶中的令牌数目,初始状态时两桶是满的,即Tc和Tp初始值分别等于CBS和PBS。

色盲模式下,如果到达的报文速率大于PIR,超过Tp+Tc部分无法得到令牌,报文被标记为红色,未超过Tp+Tc而从P桶中获取令牌的报文标记为黄色,从C桶中获取令牌的报文被标记为绿色;当报文速率小于PIR,大于CIR时,报文不会得不到令牌,但超过Tp部分报文将从P桶中获取令牌,被标记为黄色报文,从C桶中获取令牌的报文被标记为绿色;当报文速率小于CIR时,报文所需令牌数不会超过Tc,只从C桶中获取令牌,所以只会被标记为绿色报文。

在非色盲模式下,如果报文已被标记为红色或者超过Tp+Tc部分无法得到令牌的报文,被标记为红色;如果标记为黄色或者超过Tc未超过Tp部分报文记为黄色;如果报文被标记为绿或未超过Tc部分报文,被标记为绿色。

拓展阅读

guava 源码解析

漏桶算法实现

参考资料

漏桶算法&令牌桶算法理解及常用的算法

流量控制算法——漏桶算法和令牌桶算法

Token Bucket 令牌桶算法

华为-令牌桶算法

简单分析Guava中RateLimiter中的令牌桶算法的实现

网络拥塞及其对策

令牌桶算法限流

程序员必会 | 限流限速RateLimiter

令牌桶(TokenBucket)限流 - Java实现

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

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

相关文章

重构案例:将纯HTML/JS项目迁移到Webpack

我们已经了解了许多关于 Webpack 的知识&#xff0c;但要完全熟练掌握它并非易事。一个很好的学习方法是通过实际项目练习。当我们对 Webpack 的配置有了足够的理解后&#xff0c;就可以尝试重构一些项目。本次我选择了一个纯HTML/JS的PC项目进行重构&#xff0c;项目位于 GitH…

web3学习-区块链基础知识

1.1 区块链技术简史 block chain 点对点的分布式交易系统 比特币协议并不是图灵完备的。 以太坊协议加入了智能合约&#xff0c;智能合约是以太坊协议与比特币协议的最大区别&#xff08;图灵完备&#xff09; 1.2、区块链设计哲学 去中心化 由于没有中心化的数据库作为…

记录一个容易混淆的 Spring Boot 项目配置文件问题

记录一个容易混淆的 Spring Boot 项目配置文件问题 去年&#xff0c;我遇到了这样一个问题&#xff1a; 在这个例子中&#xff0c;由于密码 password 以 0 开头&#xff0c;当它被 Spring Boot 的 bean 读取时&#xff0c;前导的 0 被自动去掉了。这导致程序无法正确读取密码。…

网盘直链下载神器NDM

工具介绍 ​Neat Download Manager分享一款网盘不限速神器,安装步骤稍微有一点繁琐,但实际体验下载速度飞快,个人实际体验还是非常不错的 NDM是一款免费且强大的下载工具。可以帮助你下载各种文件&#xff0c;还能够在多任务下载中保持出色的速度及其稳定性 通过网盘分享的文…

【MySQL核心面试题】MySQL 核心 - Explain 执行计划详解!

欢迎关注公众号 【11来了】&#xff08;文章末尾即可扫码关注&#xff09; &#xff0c;持续 中间件源码、系统设计、面试进阶相关内容 在我后台回复 「资料」 可领取 编程高频电子书&#xff01; 在我后台回复「面试」可领取 30w 字的硬核面试笔记&#xff01; 感谢你的关注&…

MySQL【知识改变命运】10

联合查询 0.前言1.联合查询在MySQL里面的原理2.练习一个完整的联合查询2.1.构造练习案例数据2.2 案例&#xff1a;⼀个完整的联合查询的过程2.2.1. 确定参与查询的表&#xff0c;学⽣表和班级表2.2.2. 确定连接条件&#xff0c;student表中的class_id与class表中id列的值相等2.…

wordpress 子比主题美化 四宫格 多宫格 布局插件

wordpress 主题美化 四宫格 多宫格 布局插件&#xff08;只在子比主题上测试过&#xff0c;其它主题没测试&#xff09; A5资源网四宫格布局插件是一个功能丰富的WordPress插件,专为创建自适应的四宫格布局而设计。这个插件具有以下主要特点: 灵活的布局: 支持1到8个宫格的自定…

Springboot整合knife4j生成文档

前言 在开发过程中&#xff0c;接口文档是很重要的内容&#xff0c;用于前端对接口的联调&#xff0c;也用于给其他方使用。但是手写相对比较麻烦。 当然也有swagger之类的&#xff0c;但是界面没有那么友好。 官网&#xff1a; 整合步骤 整合依赖 需要根据版本进行&…

如何使用 pnpm 进行打补丁patch操作?推荐两个方法

前言 作为一个前端开发者&#xff0c;我们每天都在和各种各样的库和依赖打交道。node_modules 目录中存放着我们项目的各种依赖。我们有时需要对其中的一些依赖进行修改&#xff0c;比如修复某个 bug 或者增加某些自定义功能。这时候&#xff0c;给 node_modules 打补丁就显得…

为您的 WordPress 网站打造完美广告布局 A5广告单元格插件

一个为 WordPress 网站量身定制的强大工具,它将彻底改变您展示广告的方式 灵活多变的布局设计 A5 广告单元格插件的核心优势在于其无与伦比的灵活性。无论您是想要创建整齐的网格布局,还是希望打造独特的不规则设计,这款插件都能满足您的需求。 自定义网格数量&#xff1a;从 2…

androidStudio编译导致的同名.so文件冲突问题解决

files found with path lib/arm64-v8a/libserial_port.so from inputs: ...\build\intermediates\library_jni\debug\jni\arm64-v8a\libserial_port.so C:\Users\...\.gradle\caches\transforms-3\...\jni\arm64-v8a\XXX.so 解决方式如下&#xff1a; 1.将gradle缓存文件删…

TwinCAT3安装 Advanced Motion Pack库

文章目录 一.简介二.安装方式1. 下载地址2. 双击下载好的安装包3. 选择语言&#xff08;只有英文和德语&#xff09;4. 点击Next5. 选择Accept6. 填写公司和组织名称&#xff08;随意&#xff09;7. 点击Install8. 等待安装完成9. 点击Finish 一.简介 TF5420 TC3 Motion Pick-…

深度学习-1:逻辑回归和梯度下降

逻辑回归 逻辑回归是一个二分分类问题 比如判断一张图片中是否是猫就是一个二类分类问题 图像由像素值组成&#xff0c;要将图像输入模型&#xff0c;就将其变为一个向量&#xff0c;该向量存储三个通道上的所有像素值&#xff0c;若图像尺寸为64x64x3&#xff0c;则向量维度…

RequestBody接收参数报错com.fasterxml.jackson.databind.exc.MismatchedInputException

目录&#xff1a; 1、错误现象2、解决办法3、最终验证 1、错误现象 报错的现象和代码如下&#xff1a; 2、解决办法 查了很多都说参数类型对不上&#xff0c;但是明明是对上的&#xff0c;没有问题&#xff0c;最后只有换接收方式后验证是可以的&#xff1b;最终想了一下&…

Unity3D 观察者模式

Unity3D 泛型事件系统 观察者模式 观察者模式是一种行为设计模式&#xff0c;通过订阅机制&#xff0c;可以让对象触发事件时&#xff0c;通知多个其他对象。 在游戏逻辑中&#xff0c;UI 界面通常会监听一些事件&#xff0c;当数据层发生变化时&#xff0c;通过触发事件&am…

网络参考模型总结

物理层&#xff1a; 电信号&#xff0c;有中继器、集线器等设备。 数据链路层&#xff1a;帧&#xff0c;有网桥、交换机等设备&#xff0c;通过mac地址。 网络层&#xff1a;包&#xff0c;有路由器等设备&#xff0c;通过IP地址。 传输层&#xff1a;段&#xff0c;有网…

rootless模式下istio ambient的流量管理测试

接上一篇rootless模式下istio ambient鉴权策略&#xff0c;本次测试管理流量的功能。 服务流量分割 Bookinfo应用程序有三个版本的reviews服务&#xff0c;接下来对这些版本进行分配流量控制测试。 longtdsubuntu:~$ kubectl get pod |grep reviews reviews-v1-746f96c9d4-2…

出类拔萃的四款录屏工具你pick哪一个?

在这个屏幕时代&#xff0c;录屏已经成为我们日常工作和娱乐中不可或缺的一部分。无论是为了记录游戏精彩瞬间&#xff0c;还是为了分享教程和经验&#xff0c;一个好的录屏工具都是必不可少的。今天&#xff0c;就让我为大家推荐几款电脑免费录屏工具&#xff0c;并分享一下使…

DDD通用语言、多尿和尿频-《分析模式》漫谈41

“Analysis Patterns”的第3章有这么一句&#xff1a; A patient with observations of the presence of thirst, weight loss, and polyuria indicates diabetes. 2004&#xff08;机械工业出版社&#xff09;中译本的译文为&#xff1a; 虽然没有完全使用类图&#xff08;类…

【C++_string类练习】仅仅反转字母

题目链接&#xff1a;仅仅反转字母 解题思路&#xff1a; 这种反转字符的题目我第一个想到的方法就是&#xff1a;双指针 一个指针在前start&#xff0c;一个指针在后back&#xff0c; 如果指针所指向的位置的值是字母&#xff0c;那么两个指针位置的值就进行交换&#xff0…