PHP 漏桶算法代码

wen PHP项目 3

本文目录导读:

PHP 漏桶算法代码

  1. 基础漏桶算法实现
  2. 使用示例
  3. 高级版本(支持Redis分布式限流)
  4. 使用示例(Redis版)
  5. 使用场景建议
  6. 注意事项

我来为您提供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);
}

使用场景建议

  1. API限流:控制接口调用频率
  2. 流量整形:平滑突发流量
  3. 资源保护:保护后端服务不被打垮
  4. 任务队列控制:控制任务处理速率

注意事项

  1. 参数调优:需要根据实际业务场景调整容量和漏水速率
  2. 性能考虑:Redis版本比内存版本稍慢,但支持分布式
  3. 精度问题:使用microtime(true)保证时间精度
  4. 并发安全:Redis版通过Lua脚本保证原子性

这个漏桶算法实现适合大多数PHP项目的限流需求,您可以根据具体场景选择使用内存版本或Redis分布式版本。

抱歉,评论功能暂时关闭!