← 返回分布式系统
🚦 弹性治理 · 面试高频

限流(Rate Limiting)

当流量超过系统承载能力,与其被压垮,不如主动拒绝一部分。限流是保护系统的第一道闸门。本文讲清三种经典算法:令牌桶、漏桶、滑动窗口。

🎯为什么需要限流

系统容量有限。突发流量(秒杀、爬虫、重试风暴)会瞬间打满 CPU/连接/线程,引发雪崩。限流 = "量力而行,超了就拒"。

流量洪峰 限流器 放行 拒绝
🧭
核心思想:用有损服务换系统存活。拒绝 10% 的请求,好过 100% 请求都超时、整站不可用。

🎟️令牌桶(Token Bucket)

以固定速率往桶里放令牌,请求来了先拿令牌,拿到才放行,拿不到就拒绝。桶有容量上限,可应对突发。

令牌以 r/s 生成 🎟️🎟️ 令牌桶 请求取令牌 有令牌→放行 桶空→拒绝
特点:允许突发(桶里攒着的令牌可瞬间消费);长期速率受限。最常用(Guava RateLimiter、Sentinel 都支持)。

🪣漏桶(Leaky Bucket)

请求像水倒入桶,桶以恒定速率漏出(处理)。桶满则溢出(拒绝)。强调"恒定输出速率",平滑流量。

特点:不管入水多猛,出水速率恒定 → 绝对平滑,但不支持突发(多余的水直接溢出)。适合"必须匀速"的场景(如网络流量整形)。
⚖️
令牌桶 vs 漏桶:令牌桶"进可突发、出受限";漏桶"进可接受、出恒定"。一个保突发、一个保平滑,按需选用。

🪟滑动窗口(Sliding Window)

把时间切成小格,统计"最近一个时间窗口内"的请求数,超过阈值就拒绝。比固定窗口更精准。

方式说明问题
固定窗口每 1 秒最多 N 次窗口临界处可能双倍放行(如 0.9s 和 1.1s 各 N 次)
滑动窗口统计最近 N 秒请求数更平滑、无临界突刺,但需记录时间戳、稍重
✅
实践:Redis 的 INCR + EXPIRE 可做简单固定窗口;滑动窗口常用 Redis Sorted Set(zset)存时间戳,统计窗口内数量。更省内存的变体是滑动日志/滑动计数器。

⚖️三者对比

算法突发平滑实现难度典型
令牌桶✅ 支持中中Guava/Sentinel
漏桶❌ 不支持强中网络整形
滑动窗口❌强略高Redis zset

🌐分布式限流

单机限流无法约束"集群总流量"。分布式限流需要一个全局计数器,通常借助 Redis 或专门的限流组件。

方案:① 集中式(所有节点向 Redis 原子计数,如 Lua 脚本保证原子);② 网关层限流(在 API 网关统一做,最省心);③ 分桶(按用户/IP 维度分别限流,防单用户打爆)。
🧩
维度:限流可分 QPS(每秒请求数)、并发数(同时处理数)、连接数。热点用户/接口还要做"细粒度限流"(如单用户每分钟 100 次)。

🎯面试要点速记

目的:用有损服务换系统存活,防雪崩。
令牌桶:按速率发令牌,桶有容量→支持突发。
漏桶:恒定速率流出→绝对平滑,不支持突发。
滑动窗口:统计最近窗口请求数,比固定窗口更准,无临界突刺。
分布式:用 Redis/Lua 或网关层做全局计数,可分维度限流。
维度:QPS / 并发 / 连接;按用户/接口/IP 细分。
🔥
必考题:"令牌桶和漏桶的区别?"——令牌桶进可突发(攒令牌瞬间消费)、漏桶出恒定(绝对平滑)。一个适合"允许短时高峰",一个适合"必须匀速"。