当前位置:首页 > PHP

php实现 令牌桶

2026-02-15 06:20:14PHP

令牌桶算法简介

令牌桶算法是一种常用于流量控制和速率限制的算法。它维护一个固定容量的桶,按照固定速率向桶中添加令牌。请求需要获取令牌才能执行,若桶中没有足够令牌,请求会被限制。

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

            local lastTime = redis.call("hget", key, "time")
            local currentTokens = redis.call("hget", key, "tokens")

            if not lastTime then
                lastTime = now
                currentTokens = capacity
            else
                local elapsed = now - lastTime
                local newTokens = elapsed * rate
                currentTokens = math.min(capacity, currentTokens + newTokens)
                lastTime = now
            end

            if currentTokens >= tokens then
                currentTokens = currentTokens - tokens
                redis.call("hset", key, "time", lastTime)
                redis.call("hset", key, "tokens", currentTokens)
                return 1
            else
                return 0
            end
        ';

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

// 使用示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$bucket = new TokenBucket($redis, 'api_rate_limit', 10, 1); // 容量10,每秒1个令牌

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

纯PHP实现

如果不依赖Redis,可以使用纯PHP实现单机版令牌桶。

class LocalTokenBucket {
    private $capacity;
    private $tokens;
    private $rate;
    private $lastTime;

    public function __construct($capacity, $rate) {
        $this->capacity = $capacity;
        $this->tokens = $capacity;
        $this->rate = $rate;
        $this->lastTime = microtime(true);
    }

    public function consume($tokens = 1) {
        $now = microtime(true);
        $elapsed = $now - $this->lastTime;
        $this->tokens = min($this->capacity, $this->tokens + $elapsed * $this->rate);
        $this->lastTime = $now;

        if ($this->tokens >= $tokens) {
            $this->tokens -= $tokens;
            return true;
        }
        return false;
    }
}

// 使用示例
$bucket = new LocalTokenBucket(10, 1); // 容量10,每秒1个令牌
if ($bucket->consume()) {
    echo "请求通过";
} else {
    echo "请求被限制";
}

实现要点

令牌补充逻辑 计算自上次请求以来的时间差,补充相应数量的令牌: $newTokens = elapsedTime * rate$

令牌消耗检查 每次请求时检查是否有足够令牌: $if (currentTokens >= requestedTokens) { allow } else { deny }$

原子性操作 Redis实现使用Lua脚本确保操作的原子性,防止并发问题。

容量限制 补充令牌时不超过桶的容量: $currentTokens = min(capacity, currentTokens + newTokens)$

php实现 令牌桶

这两种实现方式分别适用于不同场景,Redis版本适合分布式系统,纯PHP版本适合单机应用。根据实际需求选择合适的实现方式。

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

相关文章

php实现文件上传

php实现文件上传

文件上传的基本实现 在PHP中实现文件上传需要使用$_FILES超全局数组处理上传的文件数据。表单必须设置enctype="multipart/form-data"属性,并采用POST方法提交。 创…

php实现websocket

php实现websocket

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

php 实现排序

php 实现排序

PHP 实现数组排序的方法 PHP 提供了多种内置函数和方法来实现数组排序,适用于不同的排序需求。以下是一些常见的排序实现方式: 使用 sort() 函数对数组进行升序排序 sort() 函数对数组…

php 实现mvc

php 实现mvc

PHP 实现 MVC 架构的方法 MVC(Model-View-Controller)是一种常见的软件设计模式,用于分离业务逻辑、用户界面和用户输入。以下是 PHP 实现 MVC 架构的关键步骤:…

php登陆功能实现

php登陆功能实现

实现PHP登录功能 数据库准备 创建一个用户表存储登录信息,基本字段包括用户名、密码(需加密)等: CREATE TABLE users ( id INT AUTO_INCREMENT PR…

php 实现面包屑导航

php 实现面包屑导航

实现面包屑导航的方法 面包屑导航(Breadcrumb Navigation)是一种常见的网站导航方式,用于显示用户当前页面的路径。以下是几种在 PHP 中实现面包屑导航的方法。 基于 URL 路径…