hyperf/token-bucket

令牌桶算法的实现。

v2.0.0 2023-01-07 12:45 UTC

This package is auto-updated.

Last update: 2024-09-07 16:34:07 UTC


README

PHPUnit

这是一个线程安全的PHP实现,用于令牌桶算法。您可以使用令牌桶来限制资源的使用率(例如,流带宽或API使用)。

令牌桶是一个抽象隐喻,它没有资源消耗的方向。也就是说,您可以限制消耗或生产的速率。例如,您可以限制第三方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。第一个参数是桶的容量。也就是说,将不会有更多的令牌可用。这也意味着消耗比容量更多的令牌是无效的。

第二个参数是令牌添加速率Rate。它决定了填充桶的令牌速度。速率是单位内添加的令牌数量,例如,new Rate(100, Rate::SECOND)将每秒添加100个令牌。

第三个参数是存储,它用于持久化桶的令牌数量。存储确实决定了桶的作用范围。

初始化

令牌桶需要初始化。虽然方法 TokenBucket::bootstrap() 对已初始化的桶没有副作用,但并不建议对每个请求都调用它。更好的做法是在应用程序的初始化或部署过程中包含它。

消费

现在你已经初始化了桶,可以开始消费令牌。方法 TokenBucket::consume() 将返回 true(如果已消费令牌)或 false(否则)。如果令牌已被消费,则应用程序可以继续提供服务。

否则,如果没有消费令牌,不应提供服务。在这种情况下,consume() 将将秒数写入第二个参数(该参数通过引用传递)。这是直到有足够令牌可用的时间。

阻塞消费者

在第一个例子中,我们要么提供服务,要么使用 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

Build Status