限流是保障系统稳定性的核心机制,也是系统设计面试的高频题目。字节跳动、阿里、美团等大厂后端面试必考限流设计,因为它涉及算法选型、分布式一致性、容错处理等关键工程问题,能全面考察候选人的系统设计能力。
需求澄清:限流维度(用户维度、IP维度、接口维度)、限流规则(100次/分钟、1000次/小时)、超限响应(HTTP 429 + Retry-After头)。
介绍限流算法:令牌桶(允许突发流量)、漏桶(恒定速率输出)、固定窗口计数器(简单但有边界突发问题)、滑动窗口日志(精确但内存大)、滑动窗口计数器(折衷方案)。
分布式实现:Redis集中存储计数,Lua脚本保证原子性操作。
处理边界情况:竞态条件(Redis Lua脚本原子执行)、时钟同步、容错(Redis宕机时Fail Open放行)。
响应设计:HTTP 429状态码,返回X-RateLimit-Remaining和X-RateLimit-Reset头,客户端指数退避重试。
限流器控制客户端在时间窗口内的请求数量,保护后端服务。设计如下:首先明确需求,支持多维度限流(用户级、IP级、接口级),多级限流规则(100次/分钟且1000次/小时),分布式环境下一致,新增延迟<5ms。算法选型上,我推荐令牌桶算法。每个限流维度(如用户ID+接口路径)维护一个令牌桶,桶有最大容量(突发量)和填充速率(持续速率)。比如容量10、填充速率5个/秒,允许瞬间突发10个请求,持续速率5个/秒。请求到来时消耗一个令牌,桶空则拒绝。相比固定窗口计数器,令牌桶没有窗口边界突发问题;相比漏桶,它允许合理的突发流量,更贴合实际业务场景。分布式实现的关键是Redis + Lua脚本。将令牌桶状态存在Redis中(当前令牌数和上次填充时间),用Lua脚本实现原子的"计算填充→检查令牌→消耗令牌"操作,避免竞态条件。Lua脚本在Redis中原子执行,不会被其他命令打断。架构层面,限流在API网关层实现:请求进入 → Nginx/Kong网关 → 限流中间件查询Redis → 未超限则转发到后端服务,超限则返回HTTP 429并携带X-RateLimit-Limit(总限额)、X-RateLimit-Remaining(剩余次数)、X-RateLimit-Reset(重置时间)、Retry-After(建议重试时间)头部信息。容错方面,如果Redis不可用,采用Fail Open策略——放行请求而不是拒绝所有人,同时启用本地内存限流作为降级方案。可以用Sentinel或Redis Cluster保障Redis高可用。多级限流规则的处理:对同一个请求检查所有适用的规则(如分钟级和小时级),全部通过才放行。在超大规模场景下,可以考虑本地计数+定期同步到Redis的方案,进一步降低延迟,代价是限流精度略有下降。
至少掌握三种限流算法并能对比优缺点——面试官会要求你选择并解释原因。
固定窗口的边界突发问题是高频追问——用滑动窗口计数器如何解决。
提到Fail Open还是Fail Closed是加分点——展示你的生产环境思维。
面试中如果忘了令牌桶的具体实现细节,即答侠可以实时提供算法流程。
窗口边界突发:限制100次/分钟,用户在0:59发100次、1:00又发100次,相当于2秒内发了200次。滑动窗口计数器用加权方式解决:当前窗口请求数 + 上一窗口请求数 × 上一窗口剩余比例。
令牌桶允许突发流量(桶满时可以一次性消耗所有令牌),适合API限流。漏桶以恒定速率处理请求,平滑输出但不允许突发,适合流量整形。实际中令牌桶更常用。
建议分层限流:API网关做全局限流(IP级、API Key级)保护所有服务;应用层做业务限流(用户级、功能级)实现精细控制。纵深防御效果最好。