当前位置:首页 > PHP

php实现 令牌桶

2026-01-30 08:32:25PHP

令牌桶算法简介

令牌桶算法是一种常用的流量控制算法,通过固定速率生成令牌,请求需要获取令牌才能被处理。当令牌耗尽时,请求会被限制或拒绝。

PHP实现令牌桶

使用Redis存储令牌状态

Redis适合存储和操作令牌桶的状态,利用其原子性操作保证并发安全。

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 tryAcquire($tokens = 1) {
        $now = microtime(true);
        $script = <<<LUA
local key = KEYS[1]
local now = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local capacity = tonumber(ARGV[3])
local tokensRequested = tonumber(ARGV[4])

local lastTime = tonumber(redis.call("hget", key, "lastTime")) or now
local tokensAvailable = tonumber(redis.call("hget", key, "tokens")) or capacity

local elapsed = now - lastTime
local newTokens = elapsed * rate
if newTokens > 0 then
    tokensAvailable = math.min(capacity, tokensAvailable + newTokens)
    lastTime = now
end

if tokensAvailable >= tokensRequested then
    tokensAvailable = tokensAvailable - tokensRequested
    redis.call("hmset", key, "lastTime", lastTime, "tokens", tokensAvailable)
    return 1
else
    return 0
end
LUA;

        return $this->redis->eval($script, [$this->key, $now, $this->rate, $this->capacity, $tokens], 1);
    }
}

使用示例

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$bucket = new TokenBucket($redis, 'api_rate_limit', 100, 10); // 容量100,每秒生成10个令牌

if ($bucket->tryAcquire()) {
    echo "请求通过";
} else {
    echo "请求被限流";
}

纯PHP实现(无Redis)

适合单机环境,使用文件或共享内存存储状态。

php实现 令牌桶

class FileTokenBucket {
    private $file;
    private $capacity;
    private $rate;

    public function __construct($file, $capacity, $rate) {
        $this->file = $file;
        $this->capacity = $capacity;
        $this->rate = $rate;
    }

    public function tryAcquire($tokens = 1) {
        $fp = fopen($this->file, 'c+');
        if (!flock($fp, LOCK_EX)) {
            fclose($fp);
            return false;
        }

        $data = @json_decode(fread($fp, filesize($this->file) ?: 1024), true) ?: [
            'tokens' => $this->capacity,
            'lastTime' => microtime(true)
        ];

        $now = microtime(true);
        $elapsed = $now - $data['lastTime'];
        $newTokens = $elapsed * $this->rate;
        $data['tokens'] = min($this->capacity, $data['tokens'] + $newTokens);
        $data['lastTime'] = $now;

        if ($data['tokens'] >= $tokens) {
            $data['tokens'] -= $tokens;
            ftruncate($fp, 0);
            fseek($fp, 0);
            fwrite($fp, json_encode($data));
            flock($fp, LOCK_UN);
            fclose($fp);
            return true;
        }

        flock($fp, LOCK_UN);
        fclose($fp);
        return false;
    }
}

关键参数说明

  • 容量(capacity):桶中最多存放的令牌数量
  • 速率(rate):每秒生成的令牌数量
  • 获取数量(tokens):每次请求消耗的令牌数(默认为1)

应用场景

  • API接口限流
  • 防止暴力破解
  • 分布式系统流量控制

注意事项

  • Redis实现适合分布式环境
  • 文件锁实现存在性能瓶颈
  • 令牌生成采用惰性计算(请求时计算)
  • 实际应用中可结合IP或用户ID区分不同桶

标签: 令牌php
分享给朋友:

相关文章

php 实现购物车

php 实现购物车

PHP 实现购物车功能 购物车功能是电子商务网站的核心部分,可以通过 PHP 和 Session 或数据库来实现。以下是两种常见的实现方式: 使用 Session 存储购物车数据 Sessi…

php 函数实现

php 函数实现

PHP 函数实现基础 PHP 函数的定义使用 function 关键字,后跟函数名和参数列表。函数名不区分大小写,但建议保持一致。 function functionName($param1, $p…

php 实现评论

php 实现评论

实现 PHP 评论功能 数据库设计 创建数据库表存储评论数据,通常包含以下字段: id:评论唯一标识 user_id:用户ID content:评论内容 created_at:评论时间 parent…

php实现短信验证

php实现短信验证

PHP实现短信验证的步骤 选择合适的短信服务提供商 国内常见的短信服务提供商包括阿里云短信、腾讯云短信、云片等。注册账号并获取API密钥和签名模板是第一步。这些平台通常提供详细的开发文档和SDK。…

php实现apk下载

php实现apk下载

实现APK下载的PHP代码 创建一个简单的PHP脚本来处理APK文件的下载请求,确保文件传输正确且安全。 <?php $file = 'path/to/your/app.apk'; if (f…

php实现重定向

php实现重定向

使用header函数进行重定向 在PHP中,header()函数是实现重定向的常用方法。通过发送HTTP头信息Location,浏览器会自动跳转到指定URL。需确保在调用header()前没有输出任何…