本文目录导读:

我来为您提供PHP漏桶算法的实现代码:
基础漏桶算法实现
<?php
/**
* 漏桶算法实现
* 用于流量控制/限流
*/
class LeakyBucket {
private $capacity; // 桶的容量
private $leakRate; // 漏出速率(单位:请求/秒)
private $water; // 当前桶中的水量
private $lastLeakTime; // 上次漏水时间
/**
* @param int $capacity 桶容量(最大并发数)
* @param float $leakRate 漏水速率(每秒处理的请求数)
*/
public function __construct($capacity, $leakRate) {
$this->capacity = $capacity;
$this->leakRate = $leakRate;
$this->water = 0;
$this->lastLeakTime = microtime(true);
}
/**
* 尝试添加请求到桶中
* @return bool 是否允许通过
*/
public function allowRequest() {
$this->leak();
// 检查桶是否已满
if ($this->water < $this->capacity) {
$this->water++;
return true;
}
return false;
}
/**
* 漏水操作
*/
private function leak() {
$currentTime = microtime(true);
$elapsedTime = $currentTime - $this->lastLeakTime;
// 计算漏水量
$leakedAmount = $elapsedTime * $this->leakRate;
// 更新水量
$this->water = max(0, $this->water - $leakedAmount);
// 更新上次漏水时间
$this->lastLeakTime = $currentTime;
}
/**
* 获取当前桶中的水量
*/
public function getWater() {
$this->leak();
return $this->water;
}
/**
* 重置桶
*/
public function reset() {
$this->water = 0;
$this->lastLeakTime = microtime(true);
}
}
使用示例
<?php
// 示例:创建一个容量为10,每秒漏水5个的漏桶
$bucket = new LeakyBucket(10, 5);
// 模拟请求
$requests = 20; // 模拟20个请求
for ($i = 1; $i <= $requests; $i++) {
if ($bucket->allowRequest()) {
echo "请求 {$i}: 通过 ✓\n";
// 处理请求...
} else {
echo "请求 {$i}: 拒绝 ✗(限流)\n";
}
// 模拟请求间隔0.1秒
usleep(100000);
}
// 查看当前水量
echo "\n当前水量: " . $bucket->getWater() . "\n";
高级版本(支持Redis分布式限流)
<?php
/**
* Redis实现分布式漏桶算法
* 适用于多实例部署场景
*/
class RedisLeakyBucket {
private $redis;
private $key; // Redis key
private $capacity; // 桶容量
private $leakRate; // 漏水速率
/**
* @param Redis $redis Redis连接实例
* @param string $key 唯一标识
* @param int $capacity 桶容量
* @param float $leakRate 漏水速率
*/
public function __construct($redis, $key, $capacity, $leakRate) {
$this->redis = $redis;
$this->key = $key;
$this->capacity = $capacity;
$this->leakRate = $leakRate;
}
/**
* 尝试请求
* @return bool
*/
public function allowRequest() {
$luaScript = <<<LUA
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local leakRate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- 获取当前水量和时间
local water = tonumber(redis.call('get', key .. ':water') or '0')
local lastLeakTime = tonumber(redis.call('get', key .. ':time') or '0')
-- 如果还没有记录,初始化
if lastLeakTime == 0 then
lastLeakTime = now
end
-- 计算漏水量
local elapsedTime = now - lastLeakTime
local leakedAmount = elapsedTime * leakRate
water = math.max(0, water - leakedAmount)
-- 更新时间和水量
redis.call('set', key .. ':time', now)
-- 判断是否允许请求
if water < capacity then
water = water + 1
redis.call('set', key .. ':water', water)
return 1
else
redis.call('set', key .. ':water', water)
return 0
end
LUA;
$result = $this->redis->eval(
$luaScript,
[$this->key, $this->capacity, $this->leakRate, microtime(true)],
1
);
return $result == 1;
}
/**
* 重置限流器
*/
public function reset() {
$this->redis->del($this->key . ':water');
$this->redis->del($this->key . ':time');
}
}
使用示例(Redis版)
<?php
// Redis连接
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 创建分布式漏桶
$bucket = new RedisLeakyBucket($redis, 'api_limit', 10, 5);
// 测试请求
for ($i = 1; $i <= 20; $i++) {
if ($bucket->allowRequest()) {
echo "请求 {$i}: 通过 ✓\n";
} else {
echo "请求 {$i}: 拒绝 ✗\n";
}
usleep(100000);
}
使用场景建议
- API限流:控制接口调用频率
- 流量整形:平滑突发流量
- 资源保护:保护后端服务不被打垮
- 任务队列控制:控制任务处理速率
注意事项
- 参数调优:需要根据实际业务场景调整容量和漏水速率
- 性能考虑:Redis版本比内存版本稍慢,但支持分布式
- 精度问题:使用
microtime(true)保证时间精度 - 并发安全:Redis版通过Lua脚本保证原子性
这个漏桶算法实现适合大多数PHP项目的限流需求,您可以根据具体场景选择使用内存版本或Redis分布式版本。