本文目录导读:

在 PHP 中实现限流降级,通常需要结合限流(控制请求速率)和降级(在系统压力过大时返回降级响应),以下是常用的实现方案,从简单到复杂:
限流实现方案
基于 Redis 的固定窗口限流
<?php
class RateLimiter {
private $redis;
private $key;
private $maxRequests;
private $windowSize;
public function __construct($redis, $key, $maxRequests, $windowSize = 60) {
$this->redis = $redis;
$this->key = $key;
$this->maxRequests = $maxRequests;
$this->windowSize = $windowSize;
}
public function allow() {
$current = $this->redis->get($this->key);
if ($current === false) {
$this->redis->setex($this->key, $this->windowSize, 1);
return true;
}
if ($current < $this->maxRequests) {
$this->redis->incr($this->key);
return true;
}
return false;
}
}
基于 Redis 的滑动窗口限流(更精确)
<?php
class SlidingWindowRateLimiter {
private $redis;
private $key;
public function __construct($redis, $key) {
$this->redis = $redis;
$this->key = $key;
}
public function allow($maxRequests, $windowSize = 60) {
$currentTime = time();
$windowStart = $currentTime - $windowSize;
// 使用有序集合存储请求时间戳
$this->redis->zremrangebyscore($this->key, 0, $windowStart);
$count = $this->redis->zcard($this->key);
if ($count < $maxRequests) {
$this->redis->zadd($this->key, $currentTime, uniqid());
$this->redis->expire($this->key, $windowSize);
return true;
}
return false;
}
}
基于令牌桶算法(推荐)
<?php
class TokenBucket {
private $redis;
private $key;
private $capacity; // 令牌桶容量
private $rate; // 每秒生成令牌数
public function __construct($redis, $key, $capacity, $rate) {
$this->redis = $redis;
$this->key = $key;
$this->capacity = $capacity;
$this->rate = $rate;
}
public function allow() {
$script = "
local bucket = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local tokens = redis.call('get', bucket .. ':tokens')
if not tokens then
tokens = capacity
else
tokens = tonumber(tokens)
end
local lastTime = redis.call('get', bucket .. ':lastTime')
if not lastTime then
lastTime = now
else
lastTime = tonumber(lastTime)
end
local elapsed = now - lastTime
tokens = math.min(capacity, tokens + elapsed * rate)
redis.call('set', bucket .. ':tokens', tokens)
redis.call('set', bucket .. ':lastTime', now)
redis.call('expire', bucket .. ':tokens', 60)
redis.call('expire', bucket .. ':lastTime', 60)
if tokens >= 1 then
redis.call('set', bucket .. ':tokens', tokens - 1)
return 1
else
return 0
end
";
return $this->redis->eval($script, 1, $this->key, $this->capacity, $this->rate, time());
}
}
降级实现方案
简单的降级开关
<?php
class CircuitBreaker {
private $redis;
private $key;
private $failureThreshold; // 失败阈值
private $timeoutWindow; // 超时窗口
public function __construct($redis, $key, $failureThreshold = 5, $timeoutWindow = 30) {
$this->redis = $redis;
$this->key = $key;
$this->failureThreshold = $failureThreshold;
$this->timeoutWindow = $timeoutWindow;
}
public function isOpen() {
$state = $this->redis->get($this->key . ':state');
return $state === 'open';
}
public function recordSuccess() {
$this->redis->del($this->key . ':failures');
$this->redis->set($this->key . ':state', 'closed');
}
public function recordFailure() {
$failures = $this->redis->incr($this->key . ':failures');
$this->redis->expire($this->key . ':failures', $this->timeoutWindow);
if ($failures >= $this->failureThreshold) {
$this->redis->setex($this->key . ':state', $this->timeoutWindow, 'open');
}
}
}
完整降级示例
<?php
class ServiceDegrader {
private $circuitBreaker;
public function __construct() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$this->circuitBreaker = new CircuitBreaker($redis, 'api_name');
}
public function callApi($params) {
// 检查熔断状态
if ($this->circuitBreaker->isOpen()) {
return $this->fallbackResponse();
}
try {
// 调用真实服务
$result = $this->makeRealApiCall($params);
$this->circuitBreaker->recordSuccess();
return $result;
} catch (Exception $e) {
$this->circuitBreaker->recordFailure();
return $this->fallbackResponse();
}
}
private function fallbackResponse() {
// 降级响应:返回缓存数据或默认值
return [
'data' => $this->getCacheData(),
'is_degraded' => true
];
}
private function getCacheData() {
// 从缓存获取降级数据
return Redis::get('fallback_data') ?? [];
}
private function makeRealApiCall($params) {
// 真实 API 调用
}
}
综合限流降级框架
中间件方式(适用于 Laravel/Slim 等框架)
<?php
class RateLimitAndDegradeMiddleware {
public function handle($request, $next) {
$rateLimiter = new TokenBucket(
Redis::connection(),
'user:' . $request->user_id,
$capacity = 100,
$rate = 10
);
// 限流检查
if (!$rateLimiter->allow()) {
return response()->json([
'error' => 'Too Many Requests',
'retry_after' => 60
], 429);
}
// 熔断检查
$circuitBreaker = new CircuitBreaker(Redis::connection(), 'api');
if ($circuitBreaker->isOpen()) {
// 降级策略
return $this->getCachedResponse($request);
}
try {
$response = $next($request);
$circuitBreaker->recordSuccess();
return $response;
} catch (Exception $e) {
$circuitBreaker->recordFailure();
return $this->getCachedResponse($request);
}
}
private function getCachedResponse($request) {
// 从缓存获取降级响应
return Cache::remember('degraded:' . $request->path(), 60, function() {
return ['status' => 'degraded'];
});
}
}
使用第三方库
- Redis 的 Redis::throttle() (Laravel)
- Rate Limit 包 -
briceburg/php-rate-limiter - Symfony RateLimiter 组件
实践建议
降级策略选择
- 缓存降级:从 Redis/数据库缓存获取旧数据
- 默认值降级:返回预设的默认响应
- 延迟队列:将请求放入队列稍后处理
- 静态兜底:返回静态页面/响应
监控与告警
// 限流降级监控
public function trackMetrics($metric) {
$monitor->increment('rate_limit.blocks');
$monitor->increment('circuit_breaker.opens');
}
配置化
$config = [
'rate_limits' => [
'public_api' => ['capacity' => 100, 'rate' => 10],
'internal_api' => ['capacity' => 1000, 'rate' => 100],
],
'circuit_breaker' => [
'failure_threshold' => 5,
'timeout_window' => 30,
'half_open_timeout' => 10,
]
];
降级策略组合
switch ($degradeLevel) {
case 1: // 轻度降级
return $this->cacheOnly();
case 2: // 中度降级
return $this->defaultResponse();
case 3: // 重度降级
return $this->simplePage();
case 4: // 完全熔断
throw new ServiceUnavailableException();
}
注意事项
- 原子性:限流操作要保证原子性(建议使用 Lua 脚本或 Redis 事务)
- 性能:避免在每个请求中做过于复杂的限流计算
- 分布式一致性:多实例部署时需要考虑分布式限流
- 降级隔离:不同服务的降级策略要相互独立
- 监控:需要完善的日志和监控系统
- 自动恢复:降级后要有自动恢复机制
选择方案时,根据你的实际需求(QPS、并发量、业务复杂度)来选择合适的实现方式,对于小型应用,简单的 Redis 限流 + 熔断即可;对于大型应用,建议使用专业的限流组件或 API 网关。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播