securepoint / token-bucket
Token Bucket算法的实现。
Requires
- php: ^8.1
- ext-bcmath: *
- malkusch/lock: ^2.2
Requires (Dev)
- ext-memcached: *
- ext-pdo: *
- ext-redis: *
- mikey179/vfsstream: ^1.6
- php-mock/php-mock-phpunit: ^2.7
- phpstan/phpstan: ^1.10
- phpstan/phpstan-deprecation-rules: ^1.1
- phpunit/phpunit: 10.2.7
- predis/predis: ^2.2
- rector/rector: ^0.18.4
- symplify/coding-standard: ^12.0
- symplify/easy-coding-standard: ^12.0
Suggests
- ext-memcached: Required to use the MemcachedStorage.
- ext-pdo: Requried to use the PDOStorage.
- ext-redis: Required to use the PHPRedisStorage.
- predis/predis: Required to use the PredisStorage (^2.2).
This package is auto-updated.
Last update: 2024-09-09 08:43:01 UTC
README
这是一个线程安全的PHP中Token Bucket算法实现。您可以使用Token Bucket来限制资源的使用率(例如流带宽或API使用)。
Token Bucket是一个抽象隐喻,没有资源消耗的方向。也就是说,您可以限制消耗或生产的速率。例如,您可以限制第三方API服务的消耗速率,或者限制您自己的API服务的使用速率。
这是从原始仓库bandwidth-throttle/token-bucket的分支。.
安装
使用Composer
composer require securepoint/token-bucket
使用方法
该包位于命名空间Securepoint\TokenBucket
。
示例
此示例将限制全局资源的速率,使其每秒最多进行10个请求。
use Securepoint\TokenBucket\Rate; use Securepoint\TokenBucket\TokenBucket; use Securepoint\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 Securepoint\TokenBucket\Rate; use Securepoint\TokenBucket\TokenBucket; use Securepoint\TokenBucket\BlockingConsumer; use Securepoint\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许可。