外观
限流
约 3303 字大约 11 分钟
2025-07-14
简介
限流是通过控制系统处理请求的速率,来保障上下游服务稳定运行的一种服务保护策略,能够防止后端的业务系统因为请求过多而崩溃。在限流技术中,有两个主要概念:阈值和拒绝策略
阈值指的是在指定时间范围内的允许通过的最大请求数量。
拒绝策略指的是对超出阈值请求的处理方式,在系统设计中主要有以下两种拒绝策略:
- 直接拒绝:超出阈值的请求直接返回错误,不允许通过。
- 排队等待:超出阈值的请求进入队列,等待前面的请求处理完成后再通过。
限流的好处
- 防止高负载场景下服务器资源被耗尽,保护业务正常运行
- 提升系统稳定性,预防雪崩,减少故障传播
- 应对业务突发流量,恶意请求防护(Dos攻击等)
限流算法
固定窗口计数器
原理
固定窗口计数器(Fixed window counter)算法工作原理如下:
- 将时间轴划分为固定大小的时间窗口,并为每个窗口分配一个计数器。
- 每个请求将计数器增加一。
- 一旦计数器达到预定的阈值,新的请求就会被放弃,直到新的时间窗口开始。
该算法存在一个明显的问题:在时间窗口边缘的突发流量可能会导致通过的请求数量超过阈值。
假如系统允许每分钟最多有10个请求,计数器在每一分钟开始时重置,则会出现如下情况:在 1:00:00 和 1:01:00 之间有10个请求,且所有均发生在这个窗口的后半分钟,在 1:01:00 和 1:02:00 之间也有10个请求,均发生在这个窗口的前半分钟。则在 1:00:30 和 1:01:30 之间的1分钟窗口,有20个请求通过了,相当于阈值的两倍。
优缺点
优点:
- 实现简单:只需一个整数计数器和时间判断;单线程场景可以无锁,分布式也易于通过 Redis INCR/EXPIRE、AtomicLong、Memcached add 等原子计数实现
- 计算/内存开销低:每个键(IP、用户、API 组合等)只存一条记录
- 快速截断流量:当请求高速突增时,只要计数超过阈值即可立即限流
缺点:
- 临界失衡:窗口边界可能会出现双倍阈值的流量
- 公平性差:窗口前段的用户请求会挤占后段用户配额
- 窗口对齐依赖系统时钟:机器时钟漂移、分布式节点时间不同步会造成误判
- 无法平滑降速:计数一旦超阈值就立即拒绝,无法按比例“削峰”或队列化;体验突兀
- 多个粒度窗口时存储爆炸:若需要同时支持 1 s、1 min、1 h 三档限流,需要维护三套计数器
适用场景
- 小型或原型系统:单机、单节点服务,追求“能用即可”,不想引入复杂组件。
- 后台管理接口:访问频率低,偶发高峰可接受,简单限流即可。
- 任务调度批次触发:如每分钟最多生成 N 个批处理任务;窗口边界正好与调度周期对齐。
- 日志、指标采集:对实时性要求适中,可接受单窗口内突刺。
- 分布式锁或队列入口:利用 Redis INCR + EXPIRE 实现“1 min 只允许启动 1 次全量同步”等操作。
滑动窗口计数器
原理
滑动窗口计数器(Sliding Window Counter)是对固定窗口计数器的一种改进,它解决了固定窗口临界爆发问题,实现限流的同时,提供更平滑的请求控制:
- 将整个统计周期再细分成多个小窗口(时间片),例如 1 分钟可以划分为 6 个 10 秒小窗口。
- 每个时间片都维护一个计数器,记录该时间片内的请求数。
- 每次请求到来时,先判断当前时间片并更新该片段的计数;再对过去一个周期内的所有片段求和,得到总请求数。
- 若总和超过阈值,则限流。
优缺点
优点:
- 缓解临界突发问题:请求不会因窗口边界重置而瞬时翻倍,能更平滑地限制单位时间内的请求总量。
- 更精细的时间控制:可以设定多个粒度的滑动窗口(例如秒级、分钟级)以适应不同流量特性。
- 内存占用可控:相比滑动日志法只记录“时间戳列表”,此算法只需固定数量的时间片计数器。
- 适用于分布式:可以基于 Redis Sorted Set 或 Map + TTL 实现分布式滑动窗口限流。
缺点:
- 实现复杂度略高于固定窗口:需要划分小窗口、清理过期窗口、维护计数器结构。
- 时间片粒度与性能有权衡:时间片越小,精度越高,但内存和计算开销越大。
- 仍有细粒度的边界问题:若滑动粒度不够小,仍可能出现小范围突刺。
- 分布式时钟同步要求高:各节点需统一时间片划分方式,避免误差累计。
滑动窗口日志算法
滑动窗口日志(Sliding Log)是最精确、最细粒度的限流算法之一,核心思想是:为每一次请求记录精确的时间戳,通过不断滑动时间窗口来判断单位时间内的请求数是否超过阈值。
原理
滑动窗口日志算法的工作原理如下:
- 每次请求到来时,将其时间戳记录到一个列表中(如队列或链表)。
- 清理掉早于当前时间 - 时间窗口大小的旧时间戳。
- 判断剩下的请求时间戳数量是否超过限流阈值:
- 如果未超出 → 允许请求;
- 如果超出 → 拒绝请求;
例如:限流阈值是 100 次/60 秒,如果当前时间戳列表中包含过去 60 秒内的 100 个请求,则拒绝第 101 个请求。
优缺点
优点:
- 精度最高:记录每次请求的精确时间戳,限流精确到毫秒级,基本无误差。
- 完美解决窗口临界突发问题:没有任何“窗口边界”,真正意义上的连续时间滑动控制。
- 行为公平性好:所有请求都基于实际发生时间判断是否限流,无“抢占”问题。
- 流量形态分析基础:可精确统计行为频率,辅助审计、安全检测等功能。
缺点:
- 内存占用高:每个请求都需要记录一个时间戳,100 QPS 级别用户将生成 6000 条记录/分钟。
- 计算复杂度高:每次请求都需遍历并清理过期日志,维护一个时间有序结构(队列、链表等)。
- 不适合高并发系统:在高 QPS 系统中可能导致内存和 CPU 压力骤增,尤其是分布式多用户场景。
- 实现复杂:相比简单计数器/桶算法,需要手动管理时间精度、数据清理等逻辑。
适用场景
- 小体量系统或局部高价值接口:精度要求极高的场景,如支付、防刷、验证码请求控制。
- 反爬虫、防刷机制:攻击者请求频率不规则,需要毫秒级追踪行为。
- 风控系统或安全审计:需要记录行为详细时间线的限流策略。
- 后端服务出口控制:限制对某个外部依赖系统的调用频率,防止封 IP。
- 某些分布式网关控制:若通过 Kafka + Redis 实现用户行为分析时可借助滑动日志机制限流。
漏桶算法
漏桶算法(Leaky Bucket)是一种平滑限流的算法,形象地说,它就像一个装水的桶:水(请求)以任意速率倒入,但只能以固定速率流出。
原理
- 请求进来后尝试进入“桶”(队列),桶有固定容量。
- 桶内的请求按照固定速率(例如 10 req/s)均匀流出,即处理。
- 如果桶已满(即队列达到容量上限),新请求直接丢弃或等待(排队)。
- 因为“漏水速度恒定”,请求即使突发进入,也会被强制排队,起到削峰填谷的作用。
优缺点
优点:
- 平滑流量:请求总是匀速处理,天然防止突发请求冲垮系统。
- 实现简单、逻辑清晰:只需队列 + 定时处理器即可完成基本实现。
- 避免流量尖刺影响下游服务:对接外部依赖(如数据库、API)时非常有用。
- 适用于匀速服务消费场景:比如日志收集器、异步任务执行、批处理服务等。
缺点:
- 无法处理突发高峰:请求激增时超出桶容量的请求会直接被丢弃(或排队等待时间过长)。
- 处理速度固定,可能浪费系统能力:即使系统当前资源充足,也只能以恒定速度处理请求,利用率不高。
- 延迟不可控:在突发请求时,虽然不丢弃,但排队延迟可能变得很大。
- 不适合需要突发能力的场景:如果业务允许“短时间爆发”请求,漏桶会让你“白白浪费突发能力”。
适用场景
- 系统出口保护:控制系统向下游依赖(如数据库、第三方 API)发送请求的频率,防止过载。
- 异步任务调度:将大量并发任务以固定频率调度处理,比如限频发送短信、推送等。
- 日志/指标收集器:日志写入或打点系统需平滑处理流量,避免抖动。
- 批处理消费器:如 Kafka 消费端、工作队列消费等要求固定节奏处理的服务。
- 嵌入式系统:对资源要求稳定性高但能力有限的系统。
令牌桶算法
令牌桶(Token Bucket)是限流算法中最常用、最灵活的方案之一,兼顾了流量平滑控制和突发请求处理能力,非常适合大多数互联网系统限流需求。
原理
- 系统以固定速率 r/s 向桶中添加令牌(Token),每个令牌代表允许一次请求。
- 桶有最大容量 b,超出部分的令牌将被丢弃(防止无限积累)。
- 每次请求到来时尝试从桶中获取一个令牌:
- 有令牌:请求允许;
- 没有令牌:请求被拒绝或排队等待。
- 因为令牌可以累计,所以允许一定程度的突发请求(只要之前积攒了足够令牌)。
优缺点
优点:
- 允许突发流量:可在短时间内处理一波超过限速的请求(“突发容忍”)。
- 可控的平均速率 + 峰值上限:通过令牌产生速率和平滑速率双重限制。
- 灵活适应不同业务流量模型:既可保障系统平稳,又不会过于保守限制瞬时流量。
- 实现简单、性能高:实现中只需两个变量(当前令牌数、上次填充时间)即可支持高并发限流。
- 分布式易实现:可以通过 Redis + Lua 实现原子限流控制。
缺点:
- 令牌速率计算需准确:不合理的速率和容量配置可能导致体验不佳(如积攒过多突发量)。
- 突发行为仍需保护下游服务:如果积攒大量令牌,突发请求可能仍对后端服务造成压力。
- 令牌状态需持续更新:实现时需精确记录时间并实时补充令牌(尤其是高频应用)。
适用场景
- 互联网 API 接口限流:支持高峰突发访问,但整体限速,保证接口稳定性。
- 移动端/前端用户请求限流:用户可能操作频繁,允许瞬时高并发请求,但整体要限速。
- 微服务之间调用保护:允许一定突发,但不让服务压垮下游。
- 防止恶意攻击或刷接口:有突发耐受,又能长期限速,适合人机识别策略。
- CDN 回源、边缘限流:回源服务器通常能力有限,可在边缘用令牌桶限流保护。
- 带宽/资源配额控制:比如限制每秒最大数据传输量、任务执行频率等。
高频面试题
- 什么是限流,有什么作用?
- 请详细比较固定窗口、滑动窗口、令牌桶、漏桶等算法的优缺点与适用场景。
- 令牌桶和漏桶的区别是什么?
- 固定窗口和滑动窗口有什么差异?