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 限流在分布式系统中的位置
限流组件并非孤立存在,它贯穿于分布式系统的多个层级。下图展示了限流在典型微服务架构中的部署位置与数据流关系。
2.5 限流层级与缓存一致性闭环的关联
在缓存一致性闭环中,限流并非独立存在,而是与缓存更新、数据同步形成联动。下表总结了各限流层级的关注点及其与缓存一致性的关系。
| 限流层级 | 典型实现 | 与缓存一致性的关联 |
|---|---|---|
| 网关层 | Nginx + Lua / Kong / Spring Cloud Gateway | 防止突发流量击穿网关,保护下游缓存集群 |
| 服务接口层 | Sentinel / Hystrix / Resilience4j | 控制回源请求数,避免缓存失效时大量请求直达DB |
| 缓存层 | Redis 限流模块 / 本地缓存限流 | 限制缓存重建频率,防止缓存击穿与雪崩 |
| 数据库层 | 连接池限制 / 数据库代理限流 | 保护数据库不被缓存回源流量打垮,保障数据一致性 |
闭环要点: 在缓存一致性闭环中,限流的核心作用是控制回源速率。当缓存失效时,限流确保只有少量请求能穿透到数据库,其余请求等待或降级。这为缓存重建(如分布式锁 + 双写)提供了稳定的时间窗口,避免并发回源导致的数据不一致。
2.6 限流算法选型矩阵
根据分布式系统的不同场景,限流算法的选型可参考以下矩阵:
- API网关全局限流:推荐令牌桶(允许突发)或滑动窗口(精确控制)。
- 缓存回源保护:推荐漏桶(严格削峰)或令牌桶(小容量)。
- 数据库连接池保护:推荐固定窗口 + 排队等待(类似漏桶)。
- 分布式计数器(如秒杀):推荐滑动窗口(Redis Sorted Set实现)。
- 消息队列消费速率:推荐漏桶(强制输出速率恒定)。