当前位置:首页 > PHP

php实现 令牌桶

2026-01-30 08:32:25PHP

令牌桶算法简介

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

php实现 令牌桶

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)

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

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 实现打印功能的方法 PHP 可以通过多种方式实现打印功能,具体取决于打印的目标(如打印机、PDF、网页等)和需求(如直接打印、生成打印预览等)。以下是几种常见的实现方式: 使用 JavaSc…

php实现分页

php实现分页

PHP实现分页的基本方法 使用LIMIT和OFFSET进行数据库查询分页。通过计算当前页码和每页显示数量,动态生成SQL语句。 $page = isset($_GET['page']) ? (in…

php实现文件上传

php实现文件上传

PHP 文件上传实现方法 在 PHP 中实现文件上传需要处理 HTML 表单和服务器端脚本。以下是详细步骤: 创建 HTML 表单 使用 enctype="multipart/form-data"…

php实现websocket

php实现websocket

PHP 实现 WebSocket WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。PHP 可以通过多种方式实现 WebSocket 功能,以下是几种常见的方法: 使用 Ratc…

php实现

php实现

PHP 实现的基本方法 PHP 是一种服务器端脚本语言,广泛用于 Web 开发。以下是 PHP 实现的一些常见方法。 变量与数据类型 PHP 变量以 $ 开头,支持多种数据类型: $n…

php实现登录

php实现登录

PHP 登录功能实现 数据库连接与用户表创建 创建一个数据库连接文件 db.php,用于管理数据库连接。MySQL 示例代码如下: <?php $host = 'localhost'; $db…