想象你经营着一家生意火爆的奶茶店。下午三点,外卖平台的订单蜂拥而至——有人一口气点了50杯,有人用脚本每秒刷新一次菜单,还有竞争对手的爬虫在疯狂抓取你的配方。如果来者不拒,后厨会瞬间崩溃:冰块用完、杯子告急、出餐时间从3分钟变成3小时,最后连老顾客都流失了。可是,如果你干脆关门歇业,那更是因噎废食。 聪明的奶茶店老板会怎么做?在门口装一个取号机——每位顾客进店先取号,店里同时最多接待20位客人,其余的在门外排队等候;黄金会员享有专属快速通道,普通顾客限购5杯,而新客每人限购2杯。这样一来,设备不会过载,老顾客不会被欺负,生意照做,秩序井然。 这套“取号+限购+分级服务”的打法,就是SaaS/API世界里的用量限流机制(Rate Limiting)。本文要讲的,就是API这座“奶茶店”如何用令牌桶、滑动窗口等算法,在异常流量来袭时稳住自己的后厨。

岗位关联

对AI从业者而言,限流是你产品上线后活下来的第一道护城河——学会它,你才能自信地回答“服务器崩了怎么办”,也能在架构评审时用专业术语镇住全场。

目录
  1. 可视化演示
  2. 限流必要性
  3. 令牌桶算法
  4. 滑动窗口算法
  5. 速率限制策略
  6. 熔断降级
  7. 公平与分配
  8. 方案权衡
  9. 业务落地
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

演示令牌桶以固定速率生成令牌、滑动窗口统计请求量、分级限流区分用户配额,交互调节请求速率查看限流效果。

本节配有交互动效(进入视口后自动加载,约 3KB)。

令牌桶算法 令牌以固定速率R注入桶中 令牌耗尽则拒绝请求 允许突发=桶容量B 令牌桶平衡流量控制与突发容忍

限流必要性

奶茶店每天下午都会迎来外卖高峰,后厨的锅碗瓢盆、制冰机、封口机都有固定的处理速度。API服务的“后厨”是服务器CPU、内存、数据库连接池和下游第三方接口的配额。任何一种资源被耗尽,整个系统都会像停电的奶茶店一样陷入瘫痪。

如果一家奶茶店来者不拒,最直接的后果是出餐时间无底线拉长——每单的等待时间从3分钟涨到30分钟,顾客体验崩塌。更可怕的是雪崩效应:一台制冰机过载停摆后,所有需要冰块的饮品都无法制作,店内的订单积压越来越多,越来越多的顾客取消订单、给出差评,店铺彻底瘫痪。在API世界里,这种情况更加残酷——一个慢接口会占满线程池,其他请求排队等待,数据库连接耗尽,最终整个服务返回超时错误。

限流的本质不是拒绝顾客,而是保护整体服务的可用性。就像奶茶店在门口拉起隔离带、实行限号进店,API网关在请求入口处计数器上画一条红线——超出红线的请求直接返回“429 Too Many Requests“。让大多数正常用户获得良好体验,好过所有人一起体验”服务不可用”。

没有限流的API服务,就像没有保安的演唱会现场——粉丝们热情洋溢地涌入,结果是踩踏事故。限流不是对用户说“不”,而是对所有人说“慢一点,我们都能进得去”。

令牌桶算法

令牌桶是这个领域最经典的“老大哥”。想象你的奶茶店门口有一个会定时补充奶茶券的篮子:每秒钟服务员往篮子里放一张券,篮子最多装10张;顾客来买奶茶必须交一张券,没券的只能离开。这就是令牌桶算法(Token Bucket)。

在技术世界,令牌由一个“定时器”按固定速率(rate)往桶里添加,桶容量(capacity)限制最大存留数量;每个请求到来时必须从桶中取走一个令牌,取不到就拒绝或排队。这个算法最大的优点是允许一定程度的突发流量——如果桶是满的,瞬间涌入10个请求都能通过,因为桶里积攒了10张“余票”。

令牌桶算法有一个边界的特殊情况:当桶被填满时,新生成的令牌会被丢弃。用公式描述,t时刻桶内令牌数T(t)的演化规律为:

因为允许突发,令牌桶特别适合电商秒杀、热点新闻推送这类“平时安静、瞬间洪峰”的场景。它在平均速率可控的前提下,给“尖峰”留了一口喘息的余地。你的奶茶店平时客人不多,篮子渐渐攒满了券;突然来了一群团建订奶茶的白领,10张券可以让前10单迅速通过,其余人稍微等一下——这就是体面的突发处理。

T(t)=min⁡(C, T(t−Δt)+r×Δt)\displaystyle T(t) = \min\left(C,\ T(t-\Delta t) + r \times \Delta t\right)
这个公式说的是:桶里当前的令牌数,等于上一时刻的令牌数加上新补入的令牌(速率r乘以时间间隔),但最多不超过桶容量C——就像奶茶券篮子装满了就不再塞券。
  1. 读取上一时刻令牌数T(t−Δt),这是计算的基础,表示从上次更新到现在桶里还剩多少令牌。
    T(t−Δt)T(t-\Delta t)
  2. 计算补充令牌量r×Δt,补币速率乘以时间流逝,就是这段时间新塞进篮子的奶茶券张数。
    r×Δtr \times \Delta t
  3. 二者相加后封顶min(C, ……),如果相加结果超过桶容量C,桶里最多只保留C个令牌——多余的券被挤掉。
    min⁡(C, T(t−Δt)+r×Δt)\min\left(C,\ T(t-\Delta t) + r \times \Delta t\right)

滑动窗口算法

令牌桶的对手是“滑动窗口算法”。如果说令牌桶是“攒券放行”,滑动窗口就是“数人头限流”——奶茶店规定每10分钟最多接待30位顾客。固定窗口的做法很简单:把时间切成10分钟的小段,每段一个计数器,满了30就拒绝,下一个10分钟计数器清零重新计数。

固定窗口有一个臭名昭著的缺陷,叫做临界突发(Boundary Burst)。凌晨到了10:29:59,前一个窗口已经放过30人;10:30:00,新窗口计数器清零,又涌入30人——于是10:30前后不到2秒,店里涌进了60位顾客,后厨照样崩溃。为了防止这种“卡点绕过”,滑动窗口把时间看成一条流动的河流,当前请求统计的范围是“过去10分钟”这个不断前移的窗口,而不是固定切割的块。

滑动窗口的实现思路是:将窗口细分为多个小格(比如6格,每格10秒),每格单独计数;统计时把当前时刻前10分钟内所有小格的计数求和,超过阈值则拒绝新请求。若请求在窗口内均匀到达,当前窗口内的累计请求数等于速率乘以窗口时长,可以用这个表达式判断是否超出配额:

滑动窗口的精度取决于小格划分的粒度——格子越细,统计越平滑,但内存和计算开销也越大。如果把小格无限细分,它就退化成“精确的指数移动平均”,成本也变得难以接受。所以工程上通常取10到100个小格,在精度和成本之间寻求平衡。就像奶茶店的保安拿一个秒表精确到毫秒地统计客流,虽然精确,但一个人只能负责数人,再也没空帮别的忙了。

allowed=(∑i=1nci)≤Rlimit×T\displaystyle \text{allowed} = \left( \sum_{i=1}^{n} c_i \right) \leq R_{\text{limit}} \times T
这个公式把当前窗口切成的n个小格的计数c₁到cₙ加总,只要总和不超过限流速率R_limit乘以窗口时长T,就放行;否则拒绝。
  1. 划分小格并计数把滑动窗口均分成n个时间片,每个小格维护一个请求计数cᵢ。
    ci,i=1,2,…,nc_i,\quad i = 1,2,\ldots,n
  2. 加总所有小格计数Σᵢ₌₁ⁿ cᵢ,这就是当前滑动窗口内的总请求数——相当于统计“过去10分钟内一共来了多少人”。
    ∑i=1nci\sum_{i=1}^{n} c_i
  3. 与限流阈值比较允许通过的条件是总和≤ R_limit×T,R_limit是每秒允许的请求数,T是窗口时长(秒),乘积就是窗口内的配额上限。
    ∑i=1nci≤Rlimit×T\sum_{i=1}^{n} c_i \leq R_{\text{limit}} \times T

速率限制策略

奶茶店不能对所有顾客一视同仁——VIP会员随到随做,老顾客最多买5杯,新用户限购2杯。API限流同样有不同度量维度,它们是限流策略的“尺子”。

QPS(每秒请求数)和RPM(每分钟请求数) 是最常用的速率指标。它们衡量的是“请求到达的频率”,好比每分钟进店的顾客人数。QPS限流适合保护无状态的接入层,RPM则常用于对外的API配额声明——比如免费版每分钟100次调用。

并发数限制(Concurrency Limit) 则衡量“同时在店里消费的客人数量”。有些请求很重、要计算很久,哪怕每秒只进来1个请求,如果上一个还没做完下一个又来了,积压的请求也会挤爆后厨。并发限制关注的是“当前正在处理中的请求数”,超过上限则拒绝或排队。

配额计量维度是商业设计的核心。按API Key计量(每个客户一个额度)、按用户ID计量(每个账号的额度)、按IP计量(防止单IP刷接口)——就像奶茶店区分团购券、会员卡和现金客户,不同身份走不同的限流规则。互联网巨头的开放平台通常组合使用:先用IP维度挡住恶意爬虫,再按API Key维度实施付费配额。

实际项目中,一个SaaS产品的限流配置大概是这样的:免费用户每秒钟最多2次请求、每分钟最多120次;付费用户每秒20次、每分钟2000次;管理接口进一步限制为每秒1次。这些数字不是拍脑袋定的——它对应着你的服务器成本和商业模式。把不同的尺子量在不同的客户身上,才能真正做到“后端稳如泰山,客户各得其所”。

熔断降级

限流是“在入口处劝退一部分客人”,而熔断是“发现后厨真的不行了,果断闭店歇业”。在分布式系统中,调用下游服务时经常遇到超时或异常——如果下游已经过载,继续拼命调用只会拖垮自己。此时需要一个断路器(Circuit Breaker),它像家里的空气开关一样,一旦电流过大就自动跳闸。

熔断器有三态:关闭(Closed)——一切正常,请求正常通过;打开(Open)——失败率达到阈值,立即拒绝所有请求,不再等待下游响应,这就是快速失败;半开(Half-Open)——经过一段冷却时间后,放少量试探请求进去,如果成功则关闭熔断器,失败则继续打开。这就像奶茶店的“后厨报警器”:偶尔洒了一杯奶茶(失败率小幅升高),报警器不会响;但水管爆了(失败率暴增),报警器立刻响铃,暂停接单;过一会儿修好了,先做一杯试试,成功了就恢复营业。

降级(Fallback) 是熔断的配套动作:当接口被熔断时,不直接返回错误,而是返回一个兜底的默认响应。比如推荐服务挂了,可以降级返回热门榜单的缓存数据;支付服务超时,降级为“稍后自动重试”。这不但是技术操作,更是一种商业策略——让用户的请求“有所回应”而非“石沉大海”。

熔断和限流常常配合使用:限流在流量入口“控速”,熔断在链路内部“体检”。限流是防患于未然,熔断是止损于已然。一个健康系统的设计者,会同时带上这两顶安全帽。

公平与分配

奶茶店最不愿意看到的一幕是:一位大客户包场买走了全部奶茶,门外排队的散客等了一个小时什么都没买到。这种“饿死”散客的情况在API世界里对应着多租户系统的公平性(Fairness)问题。

多租户隔离是第一步:每个客户拥有独立的配额池,互不挤占。A租户的疯狂请求不会消耗B租户的配额。这就像奶茶店给团购客户和散客分别开了两个点单窗口,互不干扰。分级速率配额则把客户按付费等级划出快慢车道——VIP通道、优先通道、普通通道,每级有明确的速率上限,这是SaaS商业化中把“速率”本身作为商品的一部分进行售卖。

防饿死机制关注的是“低优先级请求也有机会被处理”。有一种策略叫加权公平排队(Weighted Fair Queuing):每个容器按权重分配队列容量,权重高的大客户获得更多额度,但系统保证每个队列至少有一定的基础配额,避免完全被挤压。

用一句话概括公平限流的目标:大客户有特权,小客户有活路,恶意流量有惩处。在真实的SaaS平台中,你既要让付费企业客户感受到“钱花得值”,也要让免费开发者能在沙箱里顺利试用产品。否则,新用户还没体验你的API就被限流劝退了,你的产品也活不到上市的第二天。

方案权衡

没有完美无缺的限流算法,每一个选择背后都是取舍(Trade-off)。

精度与成本:滑动窗口的精度远高于固定窗口,但它需要更多内存记录每个小格的计数;令牌桶则需要在内存中维护状态,分布式环境下还要考虑状态同步。如果你在自建网关做限流,可以选内存态算法(性能高、状态不共享);如果在Kubernetes多副本集群中做全局精确限流,就需要引入Redis之类的中心化存储,精度保住了,但每次请求多一次网络开销。

分布式一致性:多个网关副本各自限流,总流量可能突破阈值——这是分布式限流最头疼的问题。方案一是“配额分摊”,把总配额切分给每台机器各自执行(简单但有偏差);方案二是“集中式限流”,用Redis的原子操作维护全局限流计数器(精确但有性能损耗);方案三是“混合模式”,本地限流做初步拦截,全局限流做兜底校准。就像奶茶店在多个楼层分别限流,又在顶层设了一个总收银台——至于哪层放多少配额,需要根据客流分布动态调整。

拒绝策略:被限流的请求怎么处理?快速失败(Fail Fast)直接返回429错误,让客户端自己重试或报错;排队等待把请求放入缓冲队列,等令牌池有余额时再处理(适合削峰填谷,但增加延迟);随机拒绝按比例丢弃部分请求,让流量以可控的方式削平。拒绝策略决定了用户体验的口味——是“立刻被拒”还是“耐心排队”,两者没有绝对的好,只有适合不适合。

架构师的美学在于:知道每个方案在什么场景下闪闪发光,在什么场景下一败涂地。没有银弹,只有权衡。

业务落地

限流不只是架构师在画板上画的漂亮框图,它必须扎进真实业务的土壤。落地的第一步是选择注入点——最常见的做法是在API网关(API Gateway)层统一实现限流,比如Kong、APISIX,或者云厂商的API网关服务。网关是流量的第一道门,在这里限流可以做到“统一策略、集中管理”,不用在几百个微服务里各自为战。

第二步是预警监控告警。限流器本身需要高可观测性:限流命中次数、令牌桶的余量水位、被拒绝请求的客户端分布……这些指标要接入Prometheus或云监控。当限流命中率超过告警阈值,说明流量接近容量上限,是扩容的信号。你要“听见门外的脚步声”,而不是等门被撞破了才去看监控。就像奶茶店老板在排队人数超过50人时就会提前多开一个窗口,而不是等到队伍排到街角才反应。

第三步是容量规划演进。限流阈值不是一次配置终身不变的。产品上线初期可能只有几十个调用方,阈值可以宽松;随着客户增长,要通过压测和监控数据持续校正限流参数,并为大客户提供“配额提升申请”的流程。更成熟的做法是按业务重要性分等级——核心支付接口阈值高、日志推送接口阈值低,让有限的资源服务于核心价值。

限流机制的最终形态,是商业策略在技术层的水中倒影。起步时你用它保护服务器,成熟时你用它塑造产品——免费版限流让用户体验到你的能力,付费版限流让他们心甘情愿为更高的配额买单。那个一开始只是“防雪崩”的保安,最后成了你奶茶店的销售经理。

AI 应用场景

大模型API平台的用量治理

阶段:模型服务化与API网关层

解决的问题:AI初创公司上线大模型API服务后,token消耗量极不稳定——某客户用脚本批量跑任务,每秒请求上千次,直接打爆GPU推理集群,导致其他客户的响应从200ms恶化到5秒。没有限流,整个平台的SLA崩塌,付费客户集体投诉。

具体案例:某AI写作SaaS平台接到一个企业的批量文档生成项目,对方用高并发脚本调用文本生成API。平台在网关层配置了基于令牌桶的限流策略:企业客户配额为每分钟600次请求,超过后返回429并在响应头中标注X-RateLimit-Remaining,客户端据此做指数退避重试。同时,平台对免费用户按IP+API Key双重限流,防止单用户用多Key绕过配额。上线后,GPU集群的峰值利用率稳定在80%以下,付费客户的P99延迟从3.2秒降到400毫秒。

向量数据库服务的配额商业化

阶段:数据基础设施层

解决的问题:向量数据库是RAG应用的基石,但向量检索的计算量巨大。所有用户共享一个集群时,重查询会“饿死”轻查询。需要按用户的付费等级设置不同的检索吞吐上限,把计算资源打包成商品售卖。

具体案例:一家提供RAG API的SaaS公司为不同版本设置限流:免费版每秒5次向量检索、每次最多检索100条;专业版每秒50次、每次500条;企业版提供自适应突发配额。每个租户有独立的滑动窗口计数器,并支持在控制台查看当前配额消耗曲线。这一机制直接支撑了产品的三档定价体系,付费转化率提升了35%。

智能客服机器人的流量削峰

阶段:B端集成与运维

解决的问题:智能客服机器人接入企业公众号后,遇到营销活动时咨询量暴涨10倍。如果不做限流,机器人后端调用的NLP服务和知识库都会过载,回答质量急剧下降。

具体案例:某电商大促期间,智能客服API的调用量从日常的每分钟2000次飙升至25000次。平台启用了排队+令牌桶的混合限流策略:普通咨询进入排队队列,按每秒200次的速率平滑处理;紧急订单相关咨询VIP快速通道,配额每分钟3000次;超负载时返回智能降级话术“稍等一下哦,客服小姐姐在路上了~”。最终系统在大促全程保持零宕机,用户满意度不降反升。

Python 演示

python72 行
import timeimport threading class TokenBucket:    """令牌桶限流器:以恒定速率补充令牌,允许有限突发"""     def __init__(self, rate, capacity):        self.rate = rate                  # 补币速率(�2/每秒)        self.capacity = capacity          # 桶容量(最大存留)        self.tokens = capacity            # 当前令牌数,初始为满桶        self.last_refill = time.monotonic()        self.lock = threading.Lock()     def _refill(self):        """按流逝时间补充令牌"""        now = time.monotonic()        delta = now - self.last_refill        self.tokens = min(self.capacity, self.tokens + delta * self.rate)        self.last_refill = now     def try_acquire(self):        """尝试获取1个令牌,成功返回True"""        with self.lock:            self._refill()            if self.tokens >= 1:                self.tokens -= 1                return True            return False  class ApiSimulator:    """模拟一个被限流的AI API端点"""     def __init__(self, bucket):        self.bucket = bucket        self.handled = 0        self.rejected = 0     def call(self, client_name):        """模拟一次API调用"""        if self.bucket.try_acquire():            self.handled += 1            # 模拟AI模型推理耗时            time.sleep(0.01)            return f"[{client_name}] 200 OK: AI生成结果"        self.rejected += 1        return f"[{client_name}] 429 Too Many Requests"  def client_task(api, name, n_requests):    """模拟一个客户端发请求"""    for _ in range(n_requests):        result = api.call(name)        if "429" in result:            print(result)        time.sleep(0.005)  if __name__ == "__main__":    bucket = TokenBucket(rate=50, capacity=20)   # 每秒补50个令牌,桶最多存20个    api = ApiSimulator(bucket)     clients = [        threading.Thread(target=client_task, args=(api, f"free_user_{i}", 100))        for i in range(5)    ]    for t in clients:        t.start()    for t in clients:        t.join()     print(f"\n成功处理: {api.handled} 次, 被限流拒绝: {api.rejected} 次")
目的:用200行内的Python完整演示令牌桶限流器的核心逻辑,展示多线程模拟下,5个“免费用户”并发请求时系统的处理与拒绝情况。
  1. class TokenBucket:令牌桶的面向对象封装,内部维护令牌数、补币速率和桶容量。
  2. self.tokens = capacity首次初始化为满桶,允许启动时的突发请求。
  3. delta = now - self.last_refill用单调时钟计算流逝时间,避免系统时间跳变导致限流失效。
  4. self.tokens = min(self.capacity, self.tokens + delta * self.rate)重构令牌桶的核心公式实现。
  5. with self.lock:加锁保证多线程下令牌数修正的原子性。
  6. def try_acquire(self):对外暴露的功能入口:尝试取令牌,返回布尔值代表是否放行。
  7. if "429" in result:模拟客户端感知被限流后的行为,方便观察效果。
  8. threading.Thread(target=client_task, args=(api, f"free_user_{i}", 100))构造5个并发客户端,每个发100个请求。

常见误区

误区“限流就是把阈值调到足够大,反正我们的机器性能很强,没必要限太死。阈值大了用户体验好,不容易误伤正常用户。”

正解限流阈值不是“越大越好”,而是“与容量匹配”。阈值大于系统实际承载能力,限流器形同虚设,尖峰流量到来时照样雪崩。正确的做法是先压测定出系统的真实容量天花板(比如单实例每秒处理2000请求),再预留30%-50%的缓冲,设置限流阈值为容量的60%-70%。正如奶茶店最多能同时服务40人,但你会在排队到30人时就开始限流,给自己留出喘息时间。

为何常见:人们天然反感“拒绝”,觉得限流是服务水平差的标志;加上拍脑袋扩容的乐观心态,容易把阈值当成“面子工程”而非“保护机制”。

误区“固定窗口简单好用,统计一下当前时间段内的请求数就行了,滑动窗口那么复杂,效果应该差不多吧。”

正解两者的关键差异在于处理窗口边界处突发的行为。固定窗口在“窗口切换的瞬间”会出现双倍流量穿透——前一个窗口余额用完的瞬间,新窗口计数器清零,又放进来一批请求。滑动窗口通过细粒度小格平滑统计,避免了这种“卡点绕过”。在CDN防攻击、API限流这类对突发敏感的场合,固定窗口的临界缺陷可能被恶意利用变成攻击手法。

为何常见:固定窗口的“简单”掩盖了它的边界缺陷,只有画时间线图才能真正理解临界突发如何穿透限流。

误区“限流和熔断不都是把请求挡在外面吗?两者没什么区别,用一个就行。”

正解限流是主动预防——在流量入口处根据预设速率阈值控制请求量,它不关心下游是否健康;熔断是被动止损——当下游服务失败率超过阈值时自动短路,并在冷却后尝试恢复。两者分工不同但常常配合:限流挡住“多余的流量”,熔断保护“正在崩溃的下游”。想象奶茶店:限流是门口的保安控制进店人数,熔断是后厨的火警报警器——保安不知道后厨着火,报警器才知道。

为何常见:两者都表现为“拒绝请求”,表面形态相似,让人误以为是一回事;实际上它们的触发条件、维护状态和恢复策略完全不同。

误区“每个网关实例都加一个内存限流器,总共4个副本,每个副本限流1000 QPS,总共就是4000 QPS,没问题吧?”

正解每个副本各自维护独立的计数器,总实际流量是4个副本的叠加。如果客户端或负载均衡策略不均匀,某个副本可能先达到阈值而其他副本还很空闲,导致“部分用户被限流,部分用户完全不受限”。要实现全局精确限流,需要引入Redis或分布式协调服务做集中式计数,或者使用配额分摊的方案(每副本分到总配额的一部分)。但你也要接受一个现实:分布式精确限流是昂贵的。

为何常见:单机内存限流写起来太顺滑了,容易让人忘记分布式环境中“状态需要共享”这个基本前提。

误区“被限流的请求直接返回429错误码就行了,客户端自己会处理。”

正解429只是限流的“响应码”,但真正的工程挑战在响应之外:是否在响应头中告诉客户端剩余配额(X-RateLimit-Remaining)?是否需要让客户端知道何时可以重试(Retry-After)?用快速失败还是排队处理?拒绝的请求要不要记录日志用于安全分析?要不要对恶意刷接口的IP做封禁?——这些策略共同构成了完整的限流体验,缺一不可。像奶茶店不仅要在门口拉隔离带,还要告诉排队的人“大约还要等15分钟”,大家才愿意排队。

为何常见:限流被当作“一个if判断加一个错误码”,忽略了它作为系统与调用方之间的“协议”所承载的信息维度。

面试题

Q1. 令牌桶算法与漏桶算法最核心的区别是什么?

单选题 · easy

  • A.令牌桶允许一定程度的突发流量← 正确答案
  • B.漏桶算法的实现更复杂
  • C.令牌桶不需要设置容量上限
  • D.漏桶算法可以处理突发流量

答案:A。令牌桶通过“积累令牌”天然允许突发——桶里有10枚令牌时瞬间放行10个请求;漏桶则强行平滑输出速率,无论输入多突发,输出都是恒定的。A正确,D说反了,B和C的描述都不准确。

Q2. 固定窗口算法的“临界突发”问题发生在什么时刻?

单选题 · easy

  • A.请求量持续为零的时段
  • B.窗口边界处,计数清零的瞬间← 正确答案
  • C.服务器重启的瞬间
  • D.令牌补满的瞬间

答案:B。固定窗口在窗口切换处计数器归零,攻击者可在此“卡点”发送两倍配额流量:前窗口末尾用尽配额,新窗口开始又涌入一批。B准确描述了这一时刻。C和D与固定窗口无关,A不是突发问题。

Q3. 某API限流配置为“每用户每分钟100次”(RPM=100),用户A在59.9秒时耗尽配额,则用户A需要等多久才能再次成功请求?

单选题 · mid

  • A.大约0.1秒,等新的一分钟窗口开始
  • B.大约60秒,永远无法提前
  • C.大约1秒,等令牌补充
  • D.不确定,取决于服务端是否滑动窗口实现← 正确答案

答案:D。固定窗口下约0.1秒后可重试,但滑动窗口下需等待滑出窗口,且令牌桶则取决于补币速率,因此D正确。这道题考察的是:同样的配额声明,不同的底层算法会带来完全不同的用户感知,“60秒限制”不一定字面等于“60秒不准动”。

Q4. 在分布式环境下实现全局精确限流,最常用的外部依赖是下列哪一个?

单选题 · mid

  • A.Redis集中式计算← 正确答案
  • B.每台服务器内存计算
  • C.Nginx的access.log日志分析
  • D.数据库主从复制

答案:A。分布式全局限流要求所有网关副本共享同一个计数器状态。Redis因其原子操作(INCR)和低延迟成为事实标准,A正确。B每台服务器各自计数不能保证全局精确;C是事后分析;D与限流无直接关系。

Q5. 关于熔断器的“半开(Half-Open)”状态,下列说法正确的是:

单选题 · mid

  • A.半开状态下所有请求都会通过
  • B.半开状态放少量试探请求,成功则关断,失败则继续打开← 正确答案
  • C.半开状态即系统完全关闭
  • D.半开状态下不再监控失败率

答案:B。半开是熔断器从打开走向关闭的中间状态:放比例极小的试探流量,如果这些请求成功,说明下游已恢复,熔断器转为关闭;失败则回到打开。B完整描述了这一机制,A、C、D均与半开的定义矛盾。

Q6. 计数滑动窗口算法中,窗口内小格划分得越细会导致什么?

单选题 · mid

  • A.精度越高但内存和计算开销越大← 正确答案
  • B.精度越低但内存开销越小
  • C.精度和开销都不变
  • D.精度越差但速度越快

答案:A。小格越细,滑动时的统计颗粒越平滑,限流精度越高;但每个小格都要在内存中维护计数器,时间片越多状态越多,计算开销也越大。A正确体现了“精度-成本”权衡,这是滑动窗口实现的核心设计考量。

Q7. 下列哪些情况适合使用令牌桶算法?(选两项)

多选题 · mid

  • A.电商秒杀场景,平时流量低、瞬间洪峰高← 正确答案
  • B.严格平滑输出,不允许任何突发
  • C.需要允许业务方短时间内的突发调用← 正确答案
  • D.必须精确到毫秒级控制总流量

答案:AC。令牌桶允许突发,因此适合“平时安静、瞬时峰涌”的场景(A),也适合业务方需要短暂突发的场景(C)。B和D要求输出严格平滑或精确控制,更适合漏桶或滑动窗口,令牌桶的“突发容忍”反而是它们的反义词。

Q8. 关于SaaS API的多租户公平限流,正确的做法包括:(选两项)

多选题 · hard

  • A.所有租户共享同一个配额池
  • B.每个租户拥有独立配额,互不挤占← 正确答案
  • C.高级租户可以通过权重获得更高配额← 正确答案
  • D.完全不限制高级租户,优先保证大客户

答案:BC。B是多租户隔离的基础,C是分级配额与加权公平排队的合理应用。A会导致拼抢与“饥饿”问题,D看似讨好大客户,但会导致资源被个别租户垄断、小租户被饿死,破坏长期生态。

Q9. 在做限流方案的工程取舍时,以下哪些权衡是真实存在的?(选两项)

多选题 · hard

  • A.高精度限流通常意味着更高存储与网络开销← 正确答案
  • B.限流一定不会增加API响应延迟
  • C.本地限流与全局限流之间存在精度与性能的取舍← 正确答案
  • D.固定窗口的临界突发可以通过增加服务器数量解决

答案:AC。实现全局精确限流需要Redis等集中式存储,每次请求多一次网络往返,开销上升但精度提高(A对);本地限流性能好但多副本下总流量有偏差,这确实是精度与性能的取舍(C对)。B的说法绝对化了,限流本身确实可能增加一次内存或网络查询的延迟。D则完全没有理解临界突发是算法本身的边界缺陷,加服务器无济于事。

Q10. 某团队使用“本地限流+全局限流”的混合模式:4个网关实例各配300 QPS本地阈值,另在Redis设800 QPS全局阈值。某时刻3个实例各打满300 QPS,第4个实例也来了300 QPS请求,此时实际放行总量最接近于?

单选题 · extreme

  • A.300 QPS,只有第4个实例接受
  • B.1200 QPS,所有实例都放满
  • C.900 QPS,三个实例满额后全局桶接管
  • D.800 QPS,全局阈值被触发,部分实例被拒绝← 正确答案

答案:D。3个实例已放行900 QPS,超过全局800 QPS,因此全局限流器会开始拒绝第4个实例的部分请求,实际总量被压到接近800 QPS。这正是“本地初步拦截、全局兜底校准”的设计目标——本地阈值允许快速放行,全局阈值保证总量不越线,但引入了额外的Redis往返延迟。D最准确地描述了这一过程。