2. 限流基础:限流算法概述与分布式定位

限流(Rate Limiting)是分布式系统抵御流量洪峰的第一道防线。其核心目标是在单位时间内限制请求的并发量或速率,防止后端服务因过载而雪崩。本节将剖析三种经典限流算法的原理、优劣,并明确限流组件在分布式架构中的部署位置。

2.1 三大经典限流算法对比

算法 核心思想 突发处理 平滑度 典型场景
令牌桶 以固定速率向桶中放入令牌,请求需获取令牌才能通过 支持突发(桶内可累积令牌) 中等 允许短时突发的API网关、微服务接口
漏桶 请求以固定速率从桶底漏出,桶满则丢弃新请求 严格拒绝突发 高 数据库写入、消息队列消费、流量整形
滑动窗口 将时间窗口细分为多个小格,统计滑动窗口内的请求总数 受窗口粒度限制 较高 分布式计数器、秒杀系统、防刷
选型建议: 令牌桶是业界最通用的选择(如Guava RateLimiter、Sentinel默认模式);漏桶适合需要严格削峰填谷的场景;滑动窗口在分布式环境下配合Redis实现精确计数,是缓存一致性闭环中的常用方案。

2.2 令牌桶算法详解

令牌桶算法通过两个参数控制流量:桶容量(burst capacity)和令牌生成速率(rate)。系统以恒定速率向桶中添加令牌,每个请求消耗一个令牌。若桶中令牌不足,请求被阻塞或拒绝。

// 伪代码:令牌桶核心逻辑
class TokenBucket {
    long capacity;          // 桶容量
    long tokens;            // 当前令牌数
    long lastRefillTime;    // 上次补充时间
    double refillRate;      // 每秒补充速率

    boolean tryAcquire() {
        refill(); // 按时间差补充令牌
        if (tokens > 0) {
            tokens--;
            return true;
        }
        return false; // 限流
    }

    void refill() {
        long now = System.currentTimeMillis();
        long elapsed = now - lastRefillTime;
        long newTokens = (long)(elapsed / 1000.0 * refillRate);
        tokens = Math.min(capacity, tokens + newTokens);
        lastRefillTime = now;
    }
}
关键特性: 令牌桶允许一定程度的突发流量(桶内累积的令牌可被一次性消费),但长期平均速率受限于 refillRate。在缓存一致性闭环中,令牌桶常用于控制缓存回源请求的并发量,防止缓存雪崩时数据库被打穿。

2.3 漏桶算法与滑动窗口

漏桶算法像一个底部有孔的漏斗:请求以任意速率流入,但以固定速率流出。当桶满时,新请求被丢弃。漏桶强制输出速率恒定,适合需要严格流量整形的场景,如消息队列的消费速率控制。

滑动窗口算法解决了固定窗口的“临界突变”问题。它将时间窗口(如1秒)划分为N个小格子(如10个100ms格子),每个格子维护一个计数器。窗口随时间滑动,统计当前窗口内所有格子的请求总数。滑动窗口的精度由格子数量决定,格子越多,精度越高,但内存开销也越大。

// 伪代码:滑动窗口计数器(基于Redis Sorted Set)
// 每个请求:ZADD rate_limit:{key} {timestamp} {requestId}
// 清理过期数据:ZREMRANGEBYSCORE rate_limit:{key} 0 {now - windowSize}
// 统计当前窗口请求数:ZCARD rate_limit:{key}
分布式陷阱: 滑动窗口在分布式环境下依赖时钟同步。若各节点时钟偏差较大,窗口边界会出现统计误差。生产环境建议使用Redis的Lua脚本保证原子性,或采用NTP时钟同步。

2.4 限流在分布式系统中的位置

限流组件并非孤立存在,它贯穿于分布式系统的多个层级。下图展示了限流在典型微服务架构中的部署位置与数据流关系。

限流在分布式系统中的部署位置 限流在分布式系统中的部署位置 客户端 / 调用方 API 网关(全局限流) 令牌桶 / 滑动窗口 服务 A 接口级限流 服务 B 接口级限流 服务 C 接口级限流 分布式缓存(Redis / 本地缓存) 缓存回源限流 数据库(DB) 连接池限流 说明:限流可部署在网关层(全局)、服务层(接口级)、缓存层(回源控制)和数据库层(连接池保护)

2.5 限流层级与缓存一致性闭环的关联

在缓存一致性闭环中,限流并非独立存在,而是与缓存更新、数据同步形成联动。下表总结了各限流层级的关注点及其与缓存一致性的关系。

限流层级 典型实现 与缓存一致性的关联
网关层 Nginx + Lua / Kong / Spring Cloud Gateway 防止突发流量击穿网关,保护下游缓存集群
服务接口层 Sentinel / Hystrix / Resilience4j 控制回源请求数,避免缓存失效时大量请求直达DB
缓存层 Redis 限流模块 / 本地缓存限流 限制缓存重建频率,防止缓存击穿与雪崩
数据库层 连接池限制 / 数据库代理限流 保护数据库不被缓存回源流量打垮,保障数据一致性
闭环要点: 在缓存一致性闭环中,限流的核心作用是控制回源速率。当缓存失效时,限流确保只有少量请求能穿透到数据库,其余请求等待或降级。这为缓存重建(如分布式锁 + 双写)提供了稳定的时间窗口,避免并发回源导致的数据不一致。

2.6 限流算法选型矩阵

根据分布式系统的不同场景,限流算法的选型可参考以下矩阵:

  • API网关全局限流:推荐令牌桶(允许突发)或滑动窗口(精确控制)。
  • 缓存回源保护:推荐漏桶(严格削峰)或令牌桶(小容量)。
  • 数据库连接池保护:推荐固定窗口 + 排队等待(类似漏桶)。
  • 分布式计数器(如秒杀):推荐滑动窗口(Redis Sorted Set实现)。
  • 消息队列消费速率:推荐漏桶(强制输出速率恒定)。