分布式系统中,网关限流只是第一道防线。真正消耗资源的是内部服务——线程池、数据库连接、下游调用。只有构建多层级限流体系,才能实现真正的系统保护。
这是一道高频后端面试题,考察的是对分布式系统限流架构的深层理解。
当你说出「网关限流」后,面试官会继续追问:单机限流 vs 集群限流、突发流量处理、核心接口保护——这四个方向缺一不可。
Guava RateLimiter、Semaphore 本地限流,轻量但无全局视角
Redis + Lua 滑动窗口 / 令牌桶,分布式环境下保证总量控制
冷启动、热点事件导致的瞬时流量洪峰,需要预热与缓冲机制
支付、下单等高价值接口需要更严格的独立限流策略
网关是系统的「大门保安」,但它看不到屋子里面发生了什么。
网关限流的本质是按路由维度(如每秒 1000 QPS 到订单服务)做总量控制。但一个请求进入服务后,可能触发多次数据库查询、多个下游 RPC 调用——这些真正的资源消耗点,网关完全不可见。
一个简单查询可能只占 5ms CPU,而一个复杂聚合接口可能占用 500ms + 3次 DB 查询 + 2次 RPC。同样 1 个 QPS,资源消耗差 100 倍。网关无法区分。
微服务架构中,服务 A 调用服务 B 是内部 RPC,不经过 API 网关。如果服务 A 出现故障重试风暴或逻辑 bug 导致循环调用,网关完全感知不到,直接把服务 B 打挂。
「查看商品列表」和「提交订单」不能共用同一个限流阈值。前者可以降级返回缓存,后者涉及库存扣减和资金流转。核心接口需要独立的、更精细的限流策略。
一个请求经过网关后,在内部服务中会触发大量资源操作——这些才是限流的真正目标。
系统中的每一种资源都是有限的:线程池大小、数据库连接数、Redis 连接池、第三方 API 配额。限流的目的是让资源消耗不超过承载能力,而不是简单地限制请求数量。
| 环节 | 配置 | 实际瓶颈 | 问题 |
|---|---|---|---|
API 网关 |
限流 10,000 QPS | 网络带宽 | ✅ 正常工作 |
订单服务 |
无线程池保护 | Tomcat 默认 200 线程 | ❌ 全部阻塞在 DB 查询上 |
MySQL |
连接池 max=100 | 慢 SQL 占满连接 | ❌ 新请求全部排队等待 |
库存服务(RPC) |
无限流 | 被上游打爆 | ❌ 雪崩传播 |
限流阈值不是拍脑袋决定的,而是基于系统容量模型科学计算出来的。
设置限流参数时,必须同时考虑以下三个维度,任何一个被忽略都会导致限流失效:
当前硬件条件下,该接口最大能承受多少并发而不出现明显延迟上升?通过压测获得基准数据。
依赖的 MySQL、Redis、第三方 API 各自有何限制?木桶效应——最短板决定了整体上限。
触发限流后返回什么?429 Too Many Requests?排队等待?降级兜底?直接影响用户满意度。
正确的做法是在系统的每一层都部署适合该层的限流策略,形成纵深防御。
每一层解决不同粒度的问题:网关层挡住恶意流量和全局超载 → 服务层保护单个实例不被击穿 → 接口层为核心 API 提供精细保护 → 资源层作为最后防线防止底层资源枯竭。任何一层失守,下一层还能兜底。
不同算法适用于不同场景,选择错误会导致严重的边界效应。
| 算法 | 原理 | 优点 | 缺点 / 注意事项 | 适用场景 |
|---|---|---|---|---|
| 固定窗口 | 统计固定时间窗口内请求数 | 实现简单,内存占用低 | 边界突刺问题 | 非严格精度要求的场景 |
| 滑动窗口 | 将大窗口切分为小格子滚动 | 精度高于固定窗口 | 内存稍高,实现复杂度中等 | 大多数生产环境首选 |
| 令牌桶 | 以固定速率放入令牌,请求取令牌 | 允许突发流量,平滑限流 | 突发量受桶容量限制 | 允许一定弹性的业务 |
| 漏桶 | 请求进队列,固定速率处理 | 绝对匀速输出 | 不适应突发,可能有延迟 | 需要严格速率控制的场景 |
| 滑动日志 | 记录每个请求时间戳,滑动统计 | 最高精度 | 内存消耗大,O(n)复杂度 | 超高精度要求场景 |
网关层:令牌桶或滑动窗口(兼顾精度与性能)→ 服务层:滑动窗口 + 热点参数限流 → 接口层:Sentinel 的热点规则(支持参数级别)→ 资源层:熔断器(半开/全开/关闭三态自动切换)。
网关限流只能做粗粒度的全局流量控制,真正消耗资源的是内部服务的线程池、数据库连接、下游调用——这些必须在服务内部进行精细化限流保护。正确的架构是多层级限流:网关层 → 服务层 → 接口层 → 资源层,四层协同形成纵深防御。