API 限流策略设计:令牌桶、滑动窗口与漏桶的比较

发布时间:2026/7/17 15:17:34
API 限流策略设计:令牌桶、滑动窗口与漏桶的比较 API 限流策略设计令牌桶、滑动窗口与漏桶的比较一、没有限流的 API 就像没关的水龙头独立产品的 API,在早期阶段通常没有限流。用 API 密钥或简单的认证就够了——用户量少,没人会滥用。但当产品成长后,API 的滥用场景开始出现:某个用户写了个脚本连续请求了几万次,把数据库拖慢;一个爬虫程序对着公开端点循环请求;或者仅仅是某个客户的错误集成,把本来一分钟一次的请求变成了一秒十次。限流(Rate Limiting)的作用不是在「流量合理时」做事,而是在「流量不合理时」保护你的后端。它不是增值功能,而是基础的安全机制。二、三种主流限流算法固定窗口(Fixed Window)。最简单的算法:将时间分为固定窗口(如 1 分钟),每个窗口内最多允许 N 次请求。实现简单——用 Redis 的INCREXPIRE就能实现。缺点:窗口边界处有「突刺」问题——假设每分钟限制 100 次,用户在 12:00:59 发了 100 次,12:01:01 又发了 100 次,在 2 秒内实际发出了 200 次请求,但两个窗口的计数都没有超限。滑动窗口(Sliding Window)。改进版固定窗口:不按自然分钟计算,而是按「过去 60 秒」内的请求次数计算。用 Redis 的 Sorted Set 实现:每个请求记录时间戳,查询过去 60 秒内的元素数量。滑动窗口更平滑,但存储和计算成本略高于固定窗口。令牌桶(Token Bucket)。最灵活的算法:桶中最多存放 N 个令牌,以固定速率(r 个/秒)向桶中投放令牌。每个请求需要消耗一个令牌,如果桶中没有令牌,请求被限流。令牌桶允许「突发流量」——如果桶中有积攒的令牌,突发请求可以一次性消耗多个令牌。三、独立产品的限流选型建议对于独立产品,建议按场景选择:场景一:API 级别的总限流。适用于限制「整个产品」的总调用量。用固定窗口就够——每分钟 N 次,用 RedisINCREXPIRE实现,简单可靠。即使有窗口边界突刺,在「全站总流量」这个粒度上影响不大。场景二:用户级别的限流。适用于限制「每个用户」的调用频率,防止单个用户滥用。用滑动窗口更好——用户级别的限流需要更精准,不能因为窗口边界让某个用户临时获得两倍的配额。场景三:AI API 调用限流。AI 调用的特点是「成本高、延迟大」。用令牌桶更适合——因为 AI API 本身就有速率限制(如 OpenAI 的 RPM/TPM),令牌桶的「固定速率生产令牌」模型与外部 API 的限流逻辑天然匹配。场景四:登录/注册限流。适用于防止暴力破解或批量注册。用固定窗口 硬限制——如「同一 IP 每分钟最多 5 次登录尝试」。超过限制后,直接拒绝,不需要平滑策略。四、限流的工程实现与用户体验限流不仅是一个算法问题,也是一个用户体验问题。返回什么状态码和信息。标准做法是返回 HTTP 429 Too Many Requests。同时,在响应头中提供限流状态信息:X-RateLimit-Limit(总限额)、X-RateLimit-Remaining(剩余次数)、X-RateLimit-Reset(下一次重置的时间戳)。这些信息让 API 消费者(或前端)可以提前做流量控制,而不只是被动等待 429 错误。前端怎么处理 429。前端在收到 429 时,不要直接展示「请求过于频繁」并让用户手动重试。应该在捕获 429 后,根据Retry-After响应头自动等待,然后自动重试。用户体验上,可以展示一个短暂的提示(如「请求排队中...」),而不是显示错误。限流的层级管理。可以设置多级限流:全局(全站总流量)、路由级(某个 API 端点的流量)、用户级(特定用户的流量)、IP 级(特定 IP 的流量)。每一级独立限流,服务器资源不会因为任何单一原因被耗尽。五、总结API 限流策略的设计,核心原则是:在最简单的算法能解决问题的场景,用最简单的算法。对于大多数独立产品,固定窗口 RedisINCR是最佳的起点——实现代码不过 10 行,覆盖 90% 的限流需求。当固定窗口的边界突刺成为实际问题时(如用户级别的限流),再升级到滑动窗口。对于有外部 API 依赖的场景(如 AI 调用),令牌桶与外部 API 的限流模型天然匹配。用户体验层面的细节同样重要:返回标准的 429 状态码和限流信息头,让前端能自动处理限流而不是让用户感知。好的限流策略,用户不需要知道它的存在,只需要在流量异常时得到合理的引导(如「请稍后再试」),而不是一堵冷冰冰的 429 错误墙。