reactphp-x/limiter

为Web和reactphp提供通用的速率限制器。适用于API客户端、网络爬虫或其他需要限流的任务

v1.0.0 2024-09-16 05:55 UTC

This package is auto-updated.

Last update: 2024-09-16 05:56:50 UTC


README

为Web和node.js提供通用的速率限制器。适用于API客户端、网络爬虫或其他需要限流的任务。公开了两个类,RateLimiter和TokenBucket。TokenBucket提供了对速率限制的底层接口,具有可配置的突发速率和滴答速率。RateLimiter位于TokenBucket之上,并添加了对每个间隔内可以移除的最大令牌数的限制,以符合常见的API限制,例如“每小时最多150个请求”。

安装

composer require reactphp-x/limiter -vvv

用法

一个简单的示例,每小时允许150个请求

use ReactphpX\Limiter\RateLimiter;
use function React\Async\async;
use function React\Async\await;

// Allow 150 requests per hour (the Twitter search limit). Also understands
// 'second', 'minute', 'day', or a number of milliseconds
$limiter = new RateLimiter(150, "hour");

async (function sendRequest() {
  // This call will throw if we request more than the maximum number of requests
  // that were set in the constructor
  // remainingRequests tells us how many additional requests could be sent
  // right this moment
    $remainingRequests = await($limiter->removeTokens(1));
    callMyRequestSendingFunction(...);
})();

另一个示例,每250ms允许发送一条消息

use ReactphpX\Limiter\RateLimiter;
use function React\Async\async;
use function React\Async\await;

$limiter = new RateLimiter(1, 250);

async(function sendMessage() {
    $remainingMessages = await($limiter->removeTokens(1));
    callMyMessageSendingFunction(...);
})();

默认行为是在解决承诺之前等待当前生效的速率限制持续时间,但如果你传递"fireImmediately": true,承诺将立即解决,并将remainingRequests设置为-1

use ReactphpX\Limiter\RateLimiter;
use function React\Async\async;
use function React\Async\await;

$limiter = new RateLimiter(
    150,
    "hour",
    true
);

async(function requestHandler(request, response) {
  // Immediately send 429 header to client when rate limiting is in effect
  $remainingRequests = await($limiter->removeTokens(1));
  if ($remainingRequests < 0) {
    $response.writeHead(429, {'Content-Type': 'text/plain;charset=UTF-8'});
    $response.end('429 Too Many Requests - your IP is being rate limited');
  } else {
    callMyMessageSendingFunction(...);
  }
})();

RateLimiter和TokenBucket中都提供了同步方法tryRemoveTokens()。这将立即返回一个布尔值,指示令牌移除是否成功。

use ReactphpX\Limiter\RateLimiter;
use function React\Async\async;
use function React\Async\await;

$limiter = new RateLimiter(10,"second");

if ($limiter->tryRemoveTokens(5))
  echo('Tokens removed');
else
  echo('No tokens removed');

要获取removeTokens承诺之外的剩余令牌数,只需使用getTokensRemaining方法。

use ReactphpX\Limiter\RateLimiter;
use function React\Async\async;
use function React\Async\await;

$limiter = new RateLimiter(1, 250);

// Prints 1 since we did not remove a token and our number of tokens per
// interval is 1
echo($limiter->getTokensRemaining());

直接使用令牌桶在字节级别进行节流

use ReactphpX\Limiter\TokenBucket;
use function React\Async\async;
use function React\Async\await;

define("BURST_RATE", 1024 * 1024 * 150); // 150KB/sec burst rate
define("FILL_RATE", 1024 * 1024 * 50); // 50KB/sec sustained rate

// We could also pass a parent token bucket in to create a hierarchical token
// bucket
// bucketSize, tokensPerInterval, interval
const bucket = new TokenBucket(
  BURST_RATE,
  FILL_RATE,
  "second"
);

async(function handleData($myData) use ($bucket) {
  await($bucket->removeTokens(strlen($myData));
  sendMyData($myData);
})();

附加说明

应使用消息队列或某种防止同时调用removeTokens()的方式使用令牌桶和速率限制器。否则,如果较新的消息持续耗尽令牌桶,则较早的消息可能会被长时间阻塞。这可能导致消息顺序混乱或在高负载下出现“丢失”消息的现象。

测试

./vendor/bin/phpunit tests

许可证

MIT许可证