
PHP后端限流算法比较:从简单计数到漏桶与令牌桶实战
上周在优化API网关时,我又遇到了那个熟悉的问题:某个热门接口突然被刷,导致整个服务响应变慢。经过一番折腾,我重新梳理了PHP中几种常见的限流方案,今天就把这些实战经验分享给大家,特别是三种核心算法的实现对比。
1. 为什么我们需要限流?
记得第一次做电商促销活动时,由于没有做限流,瞬间涌入的请求直接把数据库打挂了。从那以后我明白了:限流不只是防止恶意请求,更是保障系统稳定性的重要手段。它能:
- 避免单个接口耗尽服务器资源
- 防止爬虫过度抓取
- 保证核心业务的稳定性
- 实现服务的优雅降级
2. 固定窗口计数器:最简单的开始
这是我最早接触的限流方式,思路很简单:在固定时间窗口内计数,超过阈值就拒绝。
class FixedWindowLimiter
{
private $redis;
private $key;
private $maxRequests;
private $windowSize;
public function __construct($redis, $key, $maxRequests = 100, $windowSize = 60)
{
$this->redis = $redis;
$this->key = $key;
$this->maxRequests = $maxRequests;
$this->windowSize = $windowSize;
}
public function isAllowed()
{
$current = $this->redis->get($this->key);
if ($current === false || $current < $this->maxRequests) {
$this->redis->multi();
$this->redis->incr($this->key);
$this->redis->expire($this->key, $this->windowSize);
$this->redis->exec();
return true;
}
return false;
}
}
踩坑提醒:这种算法在窗口切换时会有临界问题。比如限制每分钟100次,可能在59秒和61秒分别请求100次,实际上2秒内请求了200次。
3. 滑动窗口:解决临界问题
为了解决固定窗口的缺陷,我采用了滑动窗口算法,记录更细粒度的时间片:
class SlidingWindowLimiter
{
private $redis;
private $key;
private $maxRequests;
private $windowSize;
private $precision;
public function __construct($redis, $key, $maxRequests = 100, $windowSize = 60, $precision = 10)
{
$this->redis = $redis;
$this->key = $key;
$this->maxRequests = $maxRequests;
$this->windowSize = $windowSize;
$this->precision = $precision; // 时间片精度(秒)
}
public function isAllowed()
{
$now = time();
$windowStart = $now - $this->windowSize;
// 移除过期时间片
$this->redis->zRemRangeByScore($this->key, 0, $windowStart);
// 获取当前窗口内请求数
$current = $this->redis->zCard($this->key);
if ($current < $this->maxRequests) {
$this->redis->zAdd($this->key, $now, uniqid());
$this->redis->expire($this->key, $this->windowSize * 2);
return true;
}
return false;
}
}
4. 漏桶算法:平滑流量输出
在处理消息队列消费时,我发现漏桶算法特别适合控制处理速率:
class LeakyBucketLimiter
{
private $redis;
private $key;
private $capacity; // 桶容量
private $leakRate; // 漏水速率(个/秒)
public function __construct($redis, $key, $capacity = 100, $leakRate = 10)
{
$this->redis = $redis;
$this->key = $key;
$this->capacity = $capacity;
$this->leakRate = $leakRate;
}
public function isAllowed()
{
$now = microtime(true);
$data = $this->redis->hGetAll($this->key);
if (empty($data)) {
$this->redis->hMSet($this->key, [
'water' => 1,
'last_time' => $now
]);
$this->redis->expire($this->key, 3600);
return true;
}
$water = (float)$data['water'];
$lastTime = (float)$data['last_time'];
// 计算漏水量
$leakAmount = ($now - $lastTime) * $this->leakRate;
$water = max(0, $water - $leakAmount);
if ($water + 1 <= $this->capacity) {
$this->redis->hMSet($this->key, [
'water' => $water + 1,
'last_time' => $now
]);
return true;
}
return false;
}
}
5. 令牌桶算法:应对突发流量
在需要应对突发流量的场景下,令牌桶是我的首选:
class TokenBucketLimiter
{
private $redis;
private $key;
private $capacity; // 桶容量
private $fillRate; // 令牌添加速率(个/秒)
public function __construct($redis, $key, $capacity = 100, $fillRate = 10)
{
$this->redis = $redis;
$this->key = $key;
$this->capacity = $capacity;
$this->fillRate = $fillRate;
}
public function isAllowed($tokens = 1)
{
$now = microtime(true);
$data = $this->redis->hGetAll($this->key);
if (empty($data)) {
$this->redis->hMSet($this->key, [
'tokens' => $this->capacity - $tokens,
'last_time' => $now
]);
$this->redis->expire($this->key, 3600);
return true;
}
$tokens = (float)$data['tokens'];
$lastTime = (float)$data['last_time'];
// 计算新增令牌
$newTokens = ($now - $lastTime) * $this->fillRate;
$tokens = min($this->capacity, $tokens + $newTokens);
if ($tokens >= 1) {
$this->redis->hMSet($this->key, [
'tokens' => $tokens - 1,
'last_time' => $now
]);
return true;
}
return false;
}
}
6. 实战场景选择建议
经过多个项目的实践,我总结出这样的选择指南:
- API限流:滑动窗口或令牌桶,平衡精度和性能
- 消息队列消费:漏桶算法,保证匀速处理
- 突发流量应对:令牌桶算法,充分利用系统资源
- 简单场景:固定窗口计数器,快速实现
记得在分布式环境下,一定要用Redis等外部存储来保证计数的一致性。本地内存计数只适合单机部署。
7. 我的踩坑总结
最后分享几个实际踩过的坑:
- Redis连接超时要做好重试机制,否则限流本身会成为单点故障
- 时间同步问题:确保所有服务器时间一致
- 内存泄漏:记得设置合适的key过期时间
- 性能测试:在实际流量下验证算法效果
限流不是银弹,需要根据具体业务场景灵活选择。希望这些经验能帮你少走弯路!
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
源码库 » PHP后端限流算法比较
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
源码库 » PHP后端限流算法比较
