当前位置:首页 > PHP

php实现 令牌桶

2026-03-13 05:20:14PHP

令牌桶算法简介

令牌桶算法是一种常用的流量控制算法,通过固定速率生成令牌,请求需获取令牌才能执行,未获取令牌的请求会被限制或排队。适用于API限流、接口防刷等场景。

php实现 令牌桶

PHP实现令牌桶

基本实现思路

  1. 维护一个容器存储令牌(可用Redis或内存变量)
  2. 设定令牌生成速率和桶容量
  3. 每次请求时检查可用令牌数量
  4. 有足够令牌则扣除并允许请求,否则拒绝

基于Redis的实现

<?php
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("hmset", key, "time", lastTime, "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_limit', 10, 1); // 容量10,速率1个/秒

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

纯PHP实现(无Redis)

<?php
class MemoryTokenBucket {
    private $tokens;
    private $capacity;
    private $rate;
    private $lastTime;

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

    public function consume($tokens = 1) {
        $now = microtime(true);
        $elapsed = $now - $this->lastTime;
        $this->lastTime = $now;

        $this->tokens = min(
            $this->capacity,
            $this->tokens + $elapsed * $this->rate
        );

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

        return false;
    }
}

// 使用示例
$bucket = new MemoryTokenBucket(10, 1); // 容量10,速率1个/秒
if ($bucket->consume()) {
    echo "请求通过";
} else {
    echo "请求被限流";
}
?>

实现要点

  1. 使用Redis的Lua脚本保证原子性操作
  2. 精确计算时间差来补充令牌
  3. 支持突发流量(桶容量大于生成速率时)
  4. 分布式环境下必须使用Redis等共享存储

性能优化建议

  1. 对高频访问的令牌桶可增加本地缓存
  2. 批量获取令牌时建议使用Lua脚本
  3. 多键操作时考虑使用Redis管道
  4. 根据业务场景调整桶容量和生成速率

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

相关文章

php实现搜索

php实现搜索

PHP实现搜索功能的方法 在PHP中实现搜索功能通常涉及数据库查询和用户输入的匹配。以下是几种常见的实现方式: 使用MySQL LIKE语句 通过简单的LIKE语句实现基础搜索: $searchT…

php实现注册

php实现注册

注册功能实现步骤 数据库准备 创建用户表,包含字段如id、username、password、email等。示例SQL: CREATE TABLE users ( id INT AUTO_I…

php实现文件的下载

php实现文件的下载

PHP 实现文件下载的方法 在 PHP 中实现文件下载功能通常需要设置正确的 HTTP 头信息,并输出文件内容。以下是几种常见的实现方式: 使用 header() 函数强制下载 通过设置 Conte…

php实现聊天

php实现聊天

实现 PHP 聊天功能的方法 使用 WebSocket 和 PHP 实现实时聊天 WebSocket 是一种全双工通信协议,适合实现实时聊天功能。PHP 可以通过 Ratchet 库实现 WebSo…

php实现接口

php实现接口

PHP 实现接口的方法 在 PHP 中,接口(Interface)用于定义一组方法的规范,而不实现具体功能。类可以实现一个或多个接口,并必须实现接口中定义的所有方法。 定义接口 接口使用 inter…

php 实现单链表

php 实现单链表

单链表的基本概念 单链表是一种线性数据结构,由节点组成,每个节点包含数据域和指向下一个节点的指针域。链表的头节点是访问整个链表的入口。 单链表的节点类实现 在PHP中,可以通过类来定义链表节…