速率限制的一些思考

无论是在我们日常的软件使用中还是软件开发中,我们总是会遇到速率限制的问题,例如短信验证码限制一小时最多只能发送5次,这是日常生活的情况;在工作中,我们可能会限制说 DB 的操作不能超过 100 qps,这也是一种限制操作,那么对于这些限制速率的行为,有没有什么好一点的实践或者理论,最近我就看了一些,但是理解可能并不是很深刻,但不妨写出来和大家交流一番。

常用的限流策略

在看了不少的实践文章之后,我发现有主要讲得都是两种方法,而且都是从网络限流中迁移过来的,分别是:Leaky bucketToken bucket,这两种方法可能乍看之下差不多,而且有不少文章并没有明确得指出他们的区别,所以很容易混淆误导;但这不是唯一的原因,还有个原因就是在某些条件下,他们其实达到的效果和实现都是一致的,所以不免让人混淆。

下面我就以我个人的理解分别介绍讲解一下这两种策略,同时,针对 Token bucket 我将会使用 Python 编程语言写一个简单的实现,方便大家有一个更清晰的认识。

Leaky bucket

Leaky bucket 最初也是用在网络方面,用于计算机网络和通信网络中包交换的速率限制,后面也就迁移到其他领域了。Leaky bucket 的理论有两种,分别称为基于 meter 的和 基于 queue 的,他们实现的具体思路不同,而且当你真正实现的时候,会发现有很大的区别,下面我也分别介绍这两种。

基于 meter 的 Leaky bucket

这种基于 meterLeaky bucket 相对来说比较简单,其实它就有一个计数器,然后有消息要发送的时候,就看计数器够不够,如果计数器没有满的话,那么这个消息就可以被处理,如果计数器不足以发送消息的话,那么这个消息将会被丢弃。

那么这个计数器是怎么来的呢,基于 meter 的形式的计数器就是发送的频率,例如你设置得频率是不超过 5条/s ,那么计数器就是 5,在一秒内你每发送一条消息就减少一个,当你发第 6 条的时候计时器就不够了,那么这条消息就被丢弃了。

从这里可以看出基于 meterLeaky bucket 的特点就是肯定不超过指定速率,而且可以一定程度保持原始消息的发送信息,但是不能很好得应对突发的短期流量。

基于 queue 的 Leaky bucket

另外一种基于 queueLeaky bucket 实现起来比较复杂,但是原理却比较简单,和 meter 差不多,也是存在一个 counter,这个 counter 却不表示速率限制,而是表示 bucket 的大小,这里就是当有消息要发送的时候看 bucket 中是否还有位置,如果有,那么就将消息放进 queue 中,注意,这里就是不一样的地方,这里只是将消息放进 Leaky bucket 维护的一个 queue 的,这个 queue 以 FIFO 的形式提供服务;如果 bucket 没有位置了,那么同样得,消息将被抛弃。

在消息被放进 queue 之后,Leaky bucket 还维护了一个定时器,这个定时器的周期就是我们设置的频率周期,例如我们设置得频率是 5条/s,那么定时器的周期就是 200ms,定时器每 200ms 去 queue 里获取一次消息,如果有消息,那么就发送出去,如果没有,那么轮空了。

从上面的描述中,可以看出,对于 基于 queueLeaky bucket 来说,它可以保证的是任务之间的执行间隔严格按照我们设置得频率,不会超频,但是也正是因为如此,完全失去了任务进来的相关信息,而且对于突发流量也完全无法应对,无论流量多或少,都是固定的频率。

Token bucket

Token bucket,我们中文习惯性称之为 令牌桶,它的特点就是有一个 bucket,然后在 bucket 中存放了一定数额的 token,每当你要发送消息的时候,需要从 bucket 中获取一个 token,只要获取成功,那么你就可以发送,否则,那么将被放弃。

既然 token 会被消耗,那么肯定有补充的方式,是的,Token bucket 的 token 补充方式就是以设定的频率往bucket 里放置 token,而 bucket 是有大小的,如果要放置 token 的时候 bucket 满了,那么 token 将被抛弃,否则 bucket 中的 token 数量增加。

这里就是问题的有趣之处,和基于 queue 的 Leaky bucket 的一点区别就在于 bucket 是有容量的,也就是说假设我们设置的频率是 5条/s,但是我将 bucket 的 size 设置为 10,那么也就是说当 bucket 被放满的时候,同一时间我可以提供的 token 数量是 10 个,这意味着我可以临时支持 10条/s 的速率,这就是 token bucket 比较有意思的一点。

Token bucket 可以也能在一定程度上保持流量的来源特征,同时也支持一定的突发流量,但是,从另外一方面也可能导致超频,当然这依赖于你的选择,可以不要。

一个 Python 实现

根据上面的 Token bucket 的描述,我就以 Python 编程语言为例,给出一个

?

这个例子比较简单, Token bucket 有两个因子,分别是:频率bucket大小,这里我们就将他们定义为 fill_ratetokens,然后当我们需要发送消息的时候,就调用 consume 申请指定数量的 token,如果我们发现 token 是够的,那么 ok 没问题,不够的话就没办法了。

这里的判断 token 数量的逻辑还是不错的,值得同学们稍作思考一番。下面是一个使用的例子:

?

总结

最后,总结一下两种不同的方式的不同点和优缺点:

Leaky bucket 的优点就是可以保证速率肯定不超过我们设定的速率,最多也就相等;同时,实现可以很简单(meter),也可以很复杂(queue),不过更多情况下我们是认为它不能保持原始流量的特征的。在流量方面, Leaky bucket 不能应对突发流量,但是,反过来想,这个可以用来防御恶意流量,不过这个 bucket 的大小选择是个难题。

Token bucket 的一个比较大的优点就是可以应对突发流量,同时如果我们希望也可以转化为 meter leaky bucket,但是缺点也是比较明显,就是可能会产生突发性的流量,例如一个小时的流量都在第一秒耗完了,当然,这有个比较麻烦得解决方案:滑动窗口,这里没有提到。

Reference

  1. Leaky bucket
  2. Token bucket
  3. 接口限流
时间: 2024-10-05 04:58:45

速率限制的一些思考的相关文章

TPS及计算方法

TPS (transaction per second)代表每秒执行的事务数量,可基于测试周期内完成的事务数量计算得出.例如,用户每分钟执行6个事务,TPS为6 / 60s = 0.10 TPS.同时我们会知道事务的响应时间(或节拍),以此例,60秒完成6个事务也同时代表每个事务的响应时间或节拍为10秒. 利特尔法则  (Little's law): 该法则由麻省理工大学斯隆商学院(MIT Sloan School of Management)的教授John Little﹐于1961年所提出与证

游戏数值策划属性篇(一):关于属性设计的几点思考

摘要: 本文转载自: http://www.cocoachina.com/game/20150906/13333.html(只作转载, 不代表本站和博主同意文中观点或证实文中信息) "属性"从何而来,终归何处?在整个游戏设计中笔者围绕着这个问题展开思考..... 一.关于属性设计思想的几点思考 1)定位 定位(Attributes)包括"角色"."装备"."坐骑"属性等,衍生属性多以"角色属性"作为参照.

多元函数的偏导数、方向导数、梯度以及微分之间的关系思考

本篇文章,探讨下多元函数微分学下的一些知识点之间的关系.包括全微分.偏导数.方向导数.梯度.全导数等内容. 初学这些知识的时候,学生会明显觉得这些概念不难掌握,而且定义及计算公式也很容易记住,但总觉得差那么点东西,说又不知道从何说起.反正笔者是这种感觉.其实最根本的原因是没有理清这些知识间的关系,对这些知识并没有本质的理解.不妨现在就跟笔者一起再重新认识下它们,看看是否解开了你内心得些许疑惑. 一.导数和微分到底是什么,以及为什么会有这些概念 关于导数和微分到底是个什么玩意,笔者在探讨一元函数微

像鸟一样思考更好的并行编程

介绍 编写一个应用程序并行运行很困难,对吧?我的意思是,它一定很难,否则我们会看到各处的并行程序.我们所看到的都是平滑的并行应用程序,可以毫不费力地使用每个可用的核心.相反,多线程应用程序是例外而不是规则. 编写并行程序似乎有两个主要障碍: 学习您选择的语言提供的并行编程结构和/或约定 可视化您的并行程序的功能 第一项似乎很明显:休息一下,学习所选编程语言的并行功能,然后离开你 - 并行程序将从你的编译器中跳出.除非那天下午通常会变成几天,这通常会变成一段更长的时间,而你选择的语言的平行特征的含

【转】腾讯高级工程师:一道面试题引发的高并发性能调试思考

https://dbaplus.cn/news-21-625-1.html 这样打破沙锅问到底的精神十分可贵!注意其中用到的工具 4月份的时候看到一道面试题,据说是腾讯校招面试官提的:在多线程和高并发环境下,如果有一个平均运行一百万次才出现一次的bug,你如何调试这个bug?(知乎原贴地址如下:https://www.zhihu.com/question/43416744) 遗憾的是知乎很多答案在抨击这道题本身的正确性,虽然我不是这次的面试官,但我认为这是一道非常好的面试题.当然,只是道加分题,

从随机过程的熵率和马尔科夫稳态过程引出的一些思考 - 人生逃不过一场马尔科夫稳态

1. 引言 0x1:人生就是一个马尔科夫稳态 每一秒我们都在做各种各样的选择,要吃青菜还是红烧肉.看电影还是看书.跑步还是睡觉,咋一看起来,每一个选择都是随机的,而人生又是由无数个这样的随机选择组成的结果.从这个前提往下推导,似乎可以得出一个结论,即人生是无常的,未来是不可预测的.但事实真的是如此吗? 以前的老人流行说一句话,三岁看小,七岁看老.这似乎是一句充满迷信主义色彩的俗语,但其实其中暗含了非常质朴而经典的理论依据,即随机过程不管其转移概率分布如何,随着时序的增大,最终会收敛在某个稳态上.

关于迭代測试的一些思考

作者:朱金灿 来源:http://blog.csdn.net/clever101 一个软件的功能的越来越多,怎样建立一个规范的測试流程来保证对开发的功能进行充分的測试,是摆在我们面前的难题.在改动bug中经常会出现一种"按下葫芦浮起瓢"情形--改动了A模块的bug,却造成了原来測试没有问题的B模块出现了新的问题.这就促使我们思考:怎样保证測试的百分百的覆盖率.为此我设想一种迭代測试和迭代公布的流程.这个流程详细是这种:全部功能測试分为常规功能測试和新功能測试.所谓常规功能測试是指之前測

Yii2框架RESTful API教程(二) - 格式化响应,授权认证和速率限制

之前写过一篇Yii2框架RESTful API教程(一) - 快速入门,今天接着来探究一下Yii2 RESTful的格式化响应,授权认证和速率限制三个部分 一.目录结构 先列出需要改动的文件.目录如下: web ├─ common │ └─ models │ └ User.php └─ frontend ├─ config │ └ main.php └─ controllers └ BookController.php 二.格式化响应 Yii2 RESTful支持JSON和XML格式,如果想指定

关于重构工作的一点思考

最近两周一直忙着和重构相关的事情,本文将简要概述从开始制定重构方案,到具体执行的过程中遇到的问题,以及对重构的一点理性思考. 起因: 本系统是2015年11月开始建设,当时为了快速投入使用,大量的烂代码,后期一直保持快速前进,没有进行过实质性的重构. 具体表现: ● 分层不清,sql哪都有,dao有.service也有,就差controller没写了.同样dao也包含业务逻辑. ● sql用的是spring jdbc,并没有使用mybatis,导致sql写起来有些复杂,封装不够基本都是原始sql