对API进行限流的,有几种方案?用PHP如何做 ?

428 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 6 月更文挑战」的第 6 天,点击查看活动详情

什么是接口限流

那么什么是限流呢?顾名思义,限流就是限制流量,包括并发的流量和一定时间内的总流量,就像你宽带包了1个G的流量,用完了就没了,所以控制你的使用频率和单次使用的总消耗。

通过限流,我们可以很好地控制系统的qps,从而达到保护系统或者接口服务器稳定的目的。 接口限流的常用算法

1.固定窗口计数器(Fixed Window Counter):该方案将请求计数器与固定时间窗口结合。例如,你可以在每分钟内限制API调用次数为100次。如果超过该限制,则拒绝进一步的请求。这种方法简单直观,但可能会导致请求在时间窗口的开始瞬间发生突发。
2.滑动窗口计数器(Sliding Window Counter):该方案与固定窗口计数器类似,但请求计数器会在整个时间窗口内移动。例如,你可以设置每分钟最多100次的请求,但在每秒钟内的最大请求数不超过窗口大小除以60。这种方法可以更平滑地限制流量,但实现稍微复杂一些。
3.令牌桶算法(Token Bucket Algorithm):该算法基于一个令牌桶,其中包含固定数量的令牌。每当有请求到达时,就会从桶中获取一个令牌。如果桶中没有足够的令牌,则请求被拒绝。令牌按照一定的速率定期被添加到桶中,以控制请求的速率。这种方法可以更灵活地应对突发流量。

具体实例

<?php

class RateLimiter {
    private $redis; // Redis连接

    public function __construct() {
        // 假设已经建立了与Redis的连接
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    public function limitRequests($key, $limit, $window) {
        $currentTime = time();
        $keyPrefix = 'ratelimiter:' . $key;
        $windowKey = $keyPrefix . ':' . floor($currentTime / $window);

        $requests = $this->redis->get($windowKey);
        if ($requests === false) {
            // 创建新的窗口
            $this->redis->multi();
            $this->redis->incr($windowKey);
            $this->redis->expire($windowKey, $window + 1); // 设置过期时间略大于窗口大小
            $this->redis->exec();
            return true;
        }

        if ($requests < $limit) {
            // 在当前窗口内的请求数量未达到限制
            $this->redis->incr($windowKey);
            return true;
        }

        // 达到限制,拒绝请求
        return false;
    }
}

// 使用示例
$rateLimiter = new RateLimiter();

// API限流,限制每分钟最多5次请求
$key = 'api:example';
$limit = 5;
$window = 60;

if ($rateLimiter->limitRequests($key, $limit, $window)) {
    // 执行API逻辑
    echo "API请求成功";
} else {
    // 返回请求频率过高的错误信息
    echo "请求频率过高,请稍后再试";
}

?>