erp-backend/app/Services/RateLimiter.php
2026-04-01 17:07:04 +08:00

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;
}
}