当前位置:首页 > 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
        );
    }
}

实际应用建议

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

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

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

php实现限流算法

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

相关文章

php实现分页

php实现分页

PHP实现分页的基本方法 使用原生PHP实现分页功能需要结合数据库查询和HTML展示。以下是核心实现步骤: 数据库查询与分页逻辑 // 连接数据库 $conn = new mysqli('loca…

php实现搜索功能

php实现搜索功能

实现基本的搜索功能 在PHP中实现搜索功能通常涉及数据库查询。以下是一个简单的实现方式,假设使用MySQL数据库: <?php // 连接数据库 $conn = new mysqli('lo…

php实现递归

php实现递归

递归的基本概念 递归是一种函数调用自身的技术,适用于解决可以分解为相同子问题的问题。在PHP中,递归需要明确的终止条件(基线条件),否则会导致无限循环。 递归实现阶乘 阶乘是经典的递归案例,n! =…

php实现队列

php实现队列

PHP实现队列的方法 队列是一种先进先出(FIFO)的数据结构,PHP可以通过多种方式实现队列功能。 使用数组模拟队列 PHP数组可以模拟队列的基本操作,通过array_push和array_shi…

php如何实现直播

php如何实现直播

实现直播功能的方法 PHP可以通过结合其他技术和工具来实现直播功能。以下是几种常见的方法: 使用流媒体服务器 配置流媒体服务器如Nginx-RTMP、Red5或Wowza。这些服务器支持RTMP协…

php购物车实现

php购物车实现

PHP购物车实现方法 使用PHP实现购物车功能可以通过多种方式完成,以下是几种常见的实现方法: Session存储购物车数据 利用PHP的$_SESSION超全局变量存储购物车数据是最简单的方法。…