zexz / token-bucket
Token Bucket 算法的实现。
Requires
- php: >=5.6
- ext-bcmath: *
- malkusch/lock: ^1
Requires (Dev)
- ext-redis: ^2.2.4|^3
- php-mock/php-mock-phpunit: ^1
- phpunit/phpunit: ^5
- predis/predis: ^1
This package is not auto-updated.
Last update: 2024-09-23 11:58:10 UTC
README
这是一个 PHP 中线程安全的 Token Bucket 算法实现。您可以使用 Token Bucket 限制资源的使用率(例如,流带宽或 API 使用)。
Token Bucket 是一个抽象的隐喻,没有资源消耗的方向。即您可以限制消费或生产的速率。例如,您可以限制第三方 API 服务的消费速率,或者您也可以限制您自己的 API 服务的使用速率。
安装
使用 Composer
composer require bandwidth-throttle/token-bucket
用法
该包位于命名空间 bandwidthThrottle\tokenBucket
。
示例
此示例将限制所有请求的全球资源速率,每秒10个请求。
use bandwidthThrottle\tokenBucket\Rate; use bandwidthThrottle\tokenBucket\TokenBucket; use bandwidthThrottle\tokenBucket\storage\FileStorage; $storage = new FileStorage(__DIR__ . "/api.bucket"); $rate = new Rate(10, Rate::SECOND); $bucket = new TokenBucket(10, $rate, $storage); $bucket->bootstrap(10); if (!$bucket->consume(1, $seconds)) { http_response_code(429); header(sprintf("Retry-After: %d", floor($seconds))); exit(); } echo "API response";
注意:在此示例中,TokenBucket::bootstrap()
是代码的一部分。这不建议在生产环境中使用,因为这会产生不必要的存储通信。TokenBucket::bootstrap()
应该是应用程序的启动或部署过程的一部分。
存储范围
首先,您需要决定资源的范围。即您想要按请求、按用户还是跨所有请求限制它?您可以通过选择所需的范围的 Storage
实现来完成此操作
-
RequestScope
限制仅在单个请求内限制速率。例如,限制下载的带宽。每个请求都会有相同的带宽限制。 -
SessionScope
限制会话内资源的速率。速率控制在一个会话的所有请求上。例如,限制每个用户的 API 使用。 -
GlobalScope
限制所有进程(即请求)的资源速率。例如,限制所有进程的资源聚合下载带宽。此范围允许进程之间的竞争条件。因此,TokenBucket 在共享互斥锁上进行同步。
TokenBucket
当您有了存储后,您最终可以实例化一个 TokenBucket
。第一个参数是桶的容量。即永远不会有更多的令牌可用。这也意味着消耗超过容量的令牌是无效的。
第二个参数是令牌添加 Rate
。它决定了用令牌填充桶的速度。速率是单位内添加的令牌数量,例如 new Rate(100, Rate::SECOND)
每秒添加 100 个令牌。
第三个参数是存储,用于持久化桶中的令牌数量。存储确定桶的范围。
启动
需要一个初始化令牌桶。虽然方法 TokenBucket::bootstrap()
对已初始化的桶没有副作用,但不建议在每次请求时都调用它。最好将其包含在应用程序的初始化或部署流程中。
消耗
现在您已经初始化了令牌桶,可以开始消耗令牌。方法 TokenBucket::consume()
将返回 true
表示消耗了令牌,或者 false
表示未消耗。如果消耗了令牌,则应用程序可以继续提供服务。
如果未消耗令牌,则不应提供服务。在这种情况下,consume()
将将秒数写入其第二个参数(这是通过引用传递的)。这是直到有足够令牌可用的时间。
BlockingConsumer
在第一个示例中,我们要么提供服务,要么以HTTP状态码429失败。实际上,这是一种非常高效的资源限制API请求的方法,因为它不会在您的服务器上预留资源。
然而,有时我们希望不失败而是等待一会儿然后继续提供服务。您可以通过使用 BlockingConsumer
来消耗令牌桶来实现这一点。
use bandwidthThrottle\tokenBucket\Rate; use bandwidthThrottle\tokenBucket\TokenBucket; use bandwidthThrottle\tokenBucket\BlockingConsumer; use bandwidthThrottle\tokenBucket\storage\FileStorage; $storage = new FileStorage(__DIR__ . "/api.bucket"); $rate = new Rate(10, Rate::SECOND); $bucket = new TokenBucket(10, $rate, $storage); $consumer = new BlockingConsumer($bucket); $bucket->bootstrap(10); // This will block until one token is available. $consumer->consume(1); echo "API response";
这将有效地将速率限制为每秒10个请求。但是在这种情况下,客户端不必担心429错误。相反,连接只是延迟到期望的速率。
许可证和作者
该项目是免费的,遵循WTFPL许可证。此项目由Markus Malkusch 负责联系:markus@malkusch.de。
捐赠
如果您喜欢这个项目并且感觉慷慨,请在此处捐赠一些比特币:1335STSwu9hST4vcMRppEPgENMHD2r1REK