74 lines
1.9 KiB
PHP
74 lines
1.9 KiB
PHP
<?php
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Redis;
|
|
|
|
class RateLimiter
|
|
{
|
|
protected $platform;
|
|
protected $apiName;
|
|
protected $config;
|
|
|
|
public function __construct($platform, $apiName)
|
|
{
|
|
$this->platform = $platform;
|
|
$this->apiName = $apiName;
|
|
$this->config = config("rate_limits.{$platform}.{$apiName}", []);
|
|
}
|
|
|
|
public function allowRequest()
|
|
{
|
|
return true; // 临时绕过限流
|
|
|
|
$key = "rate_limit:{$this->platform}:{$this->apiName}";
|
|
|
|
// 使用 Redis 计数器,每秒重置
|
|
$current = Redis::incr($key);
|
|
if ($current == 1) {
|
|
Redis::expire($key, 1);
|
|
}
|
|
|
|
$qps = $this->config['qps'] ?? 2;
|
|
if ($current > $qps) {
|
|
return false;
|
|
}
|
|
|
|
// 分钟限制
|
|
$minKey = "rate_limit_min:{$this->platform}:{$this->apiName}:" . date('YmdHi');
|
|
$minCurrent = Redis::incr($minKey);
|
|
if ($minCurrent == 1) {
|
|
Redis::expire($minKey, 60);
|
|
}
|
|
$minLimit = $this->config['minute'] ?? 30;
|
|
if ($minCurrent > $minLimit) {
|
|
return false;
|
|
}
|
|
|
|
// 日限制
|
|
$dayKey = "rate_limit_day:{$this->platform}:{$this->apiName}:" . date('Ymd');
|
|
$dayCurrent = Redis::incr($dayKey);
|
|
if ($dayCurrent == 1) {
|
|
Redis::expire($dayKey, 86400);
|
|
}
|
|
$dayLimit = $this->config['daily'] ?? 1000;
|
|
if ($dayCurrent > $dayLimit) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function waitForAvailability($timeout = 60)
|
|
{
|
|
$start = time();
|
|
while (time() - $start < $timeout) {
|
|
if ($this->allowRequest()) {
|
|
return true;
|
|
}
|
|
// 指数退避
|
|
$wait = min(5, pow(2, time() - $start));
|
|
sleep($wait);
|
|
}
|
|
return false;
|
|
}
|
|
} |