当前位置:首页 > PHP

php实现限流算法

2026-02-16 08:07:55PHP

固定窗口计数器算法

固定窗口计数器算法将时间划分为固定大小的窗口,每个窗口内请求次数达到阈值后拒绝后续请求。实现简单但临界时间点可能出现请求突增。

class FixedWindowRateLimiter {
    private $limit;
    private $window;
    private $counter = 0;
    private $lastReset;

    public function __construct($limit, $window) {
        $this->limit = $limit;
        $this->window = $window;
        $this->lastReset = time();
    }

    public function allowRequest() {
        $currentTime = time();
        if ($currentTime - $this->lastReset >= $this->window) {
            $this->counter = 0;
            $this->lastReset = $currentTime;
        }

        if ($this->counter < $this->limit) {
            $this->counter++;
            return true;
        }
        return false;
    }
}

// 使用示例:每分钟允许100次请求
$limiter = new FixedWindowRateLimiter(100, 60);
if ($limiter->allowRequest()) {
    // 处理请求
} else {
    // 拒绝请求
}

滑动窗口日志算法

滑动窗口日志算法记录每个请求的时间戳,统计当前窗口内的请求数量。精度高但内存消耗随请求量增加。

class SlidingWindowLogRateLimiter {
    private $limit;
    private $window;
    private $timestamps = [];

    public function __construct($limit, $window) {
        $this->limit = $limit;
        $this->window = $window;
    }

    public function allowRequest() {
        $currentTime = time();
        $this->timestamps = array_filter(
            $this->timestamps,
            function ($timestamp) use ($currentTime) {
                return $timestamp > $currentTime - $this->window;
            }
        );

        if (count($this->timestamps) < $this->limit) {
            $this->timestamps[] = $currentTime;
            return true;
        }
        return false;
    }
}

令牌桶算法

令牌桶算法以固定速率向桶中添加令牌,请求获取令牌成功则放行。允许突发流量,适合需要应对流量波动的场景。

class TokenBucketRateLimiter {
    private $capacity;
    private $tokens;
    private $fillRate; // 令牌/秒
    private $lastFill;

    public function __construct($capacity, $fillRate) {
        $this->capacity = $capacity;
        $this->fillRate = $fillRate;
        $this->tokens = $capacity;
        $this->lastFill = time();
    }

    public function allowRequest($tokens = 1) {
        $this->refill();
        if ($this->tokens >= $tokens) {
            $this->tokens -= $tokens;
            return true;
        }
        return false;
    }

    private function refill() {
        $now = time();
        $elapsed = $now - $this->lastFill;
        $this->tokens = min(
            $this->capacity,
            $this->tokens + $elapsed * $this->fillRate
        );
        $this->lastFill = $now;
    }
}

// 使用示例:桶容量100,每秒填充2个令牌
$limiter = new TokenBucketRateLimiter(100, 2);
if ($limiter->allowRequest()) {
    // 处理请求
}

漏桶算法

漏桶算法以固定速率处理请求,超出桶容量的请求被丢弃。确保流量输出速率恒定,适合保护下游系统。

class LeakyBucketRateLimiter {
    private $capacity;
    private $leakRate; // 请求/秒
    private $queue = [];
    private $lastLeak;

    public function __construct($capacity, $leakRate) {
        $this->capacity = $capacity;
        $this->leakRate = $leakRate;
        $this->lastLeak = time();
    }

    public function allowRequest() {
        $this->leak();
        if (count($this->queue) < $this->capacity) {
            $this->queue[] = time();
            return true;
        }
        return false;
    }

    private function leak() {
        $now = time();
        $elapsed = $now - $this->lastLeak;
        $leakCount = $elapsed * $this->leakRate;

        $this->queue = array_slice(
            $this->queue,
            $leakCount
        );
        $this->lastLeak = $now;
    }
}

Redis实现的分布式限流

使用Redis实现分布式环境下的限流,确保多服务器间共享计数状态。以下为令牌桶算法的Redis实现:

class RedisTokenBucketRateLimiter {
    private $redis;
    private $keyPrefix;
    private $capacity;
    private $fillRate;

    public function __construct($redis, $keyPrefix, $capacity, $fillRate) {
        $this->redis = $redis;
        $this->keyPrefix = $keyPrefix;
        $this->capacity = $capacity;
        $this->fillRate = $fillRate;
    }

    public function allowRequest($userId, $tokens = 1) {
        $key = $this->keyPrefix . $userId;
        $now = time();

        $lua = <<<LUA
local key = KEYS[1]
local now = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local fillRate = tonumber(ARGV[3])
local tokens = tonumber(ARGV[4])

local lastData = redis.call("HMGET", key, "tokens", "lastFill")
local lastTokens = tonumber(lastData[1]) or capacity
local lastFill = tonumber(lastData[2]) or now

local elapsed = now - lastFill
local newTokens = math.min(
    capacity,
    lastTokens + elapsed * fillRate
)

if newTokens >= tokens then
    redis.call("HMSET", key, "tokens", newTokens - tokens, "lastFill", now)
    return 1
else
    return 0
end
LUA;

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

实际应用建议

生产环境中建议结合以下策略:

php实现限流算法

  • 优先选择Redis实现的分布式限流方案
  • 对API接口可采用中间件形式集成限流
  • 配合HTTP 429状态码和Retry-After头部返回限流信息
  • 重要接口建议采用令牌桶算法,普通接口可采用滑动窗口

Nginx层面也可通过limit_req模块实现限流,与业务层限流形成多级防护。

标签: 算法php
分享给朋友:

相关文章

php实现socket

php实现socket

PHP 实现 Socket 通信 PHP 可以通过 socket 扩展实现网络通信,支持 TCP/UDP 协议。以下是实现 Socket 通信的核心步骤和代码示例。 创建 TCP 服务器 //…

php 队列的实现

php 队列的实现

PHP 队列的实现方法 使用数据库实现队列 创建一个数据表存储队列任务,包含任务ID、状态、创建时间等字段。通过SQL语句实现任务的入队和出队操作。 // 入队操作 INSERT INTO queu…

php登陆功能实现

php登陆功能实现

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

php变量的实现

php变量的实现

PHP变量的实现机制 PHP变量的实现基于Zend引擎的底层数据结构。核心结构体zval用于存储变量类型和值,其设计允许动态类型转换和高效内存管理。 zval结构体 PHP变量通过zval结构体实现…

php 实现 java md5加密

php 实现 java md5加密

PHP 实现 Java MD5 加密 Java 和 PHP 的 MD5 加密结果可能存在差异,主要由于字符编码和输出格式的不同。以下是确保 PHP 与 Java MD5 加密结果一致的方法。 确保字…

php实现des加密

php实现des加密

DES 加密实现方法 在 PHP 中实现 DES 加密可以通过内置的 mcrypt 或 openssl 扩展完成。由于 mcrypt 已在 PHP 7.2 中废弃,推荐使用 openssl 扩展。…