我们的Go微服务网关限流策略演进实录

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-09 17:49 ·3 浏览 ·0 回复

从单机到集群:我们的网关限流血泪史

一切要从那个黑色星期五说起。凌晨两点,监控大屏上线上订单超时率突然飙到 40%,数据库连接数直接打满。事后复盘发现,罪魁祸首是某合作方误配了重试策略,每秒向我们的网关灌入上万次请求——而当时的限流组件,因为单机内存计数器在多个副本间各自为战,直接被流量击穿。

那次的教训让我们彻底明白:分布式系统里,限流不是“加个 if 判断”那么简单,而是一场与流量博弈的持久战。

第一版:在 Nginx 层“硬扛”

最初我们用的是最粗暴的方案:在 OpenResty 里用 `lua-resty-limit-traffic`,按 IP + 接口维度做计数器限流。配置简单,性能也够,单机 QPS 阈值写死——每台机器 500。但问题很快暴露:当后端扩容到 5 台时,总承载量变成 5×500,但压测时发现第 3 台机器的 CPU 已经飙到 90%,其他机器却空闲。因为负载均衡策略是哈希,某些热点 IP 被固定打在同一台上,单机限流完全没考虑集群整体水位。

更要命的是,某次全链路压测故意把流量打到某台机器上,直接触发了它的单机阈值,而其他机器还在“闲得发慌”。我们意识到:**单机限流在微服务化后,就像给每个房间单独装水表,却忘了总水管才是瓶颈**。

第二版:引入 Redis 做全局计数器

痛定思痛,我们改用了 Redis 漏斗限流(`INCR` + `EXPIRE`),把阈值统一收口到 Redis 集群。思路很简单:每个用户 + 每个接口一个 key,窗口 1 秒,超过阈值直接 429。初期效果显著,热点 IP 被精准限制,集群总 QPS 再也没突破过设定值。

但新问题来得更快: Redis 成为了新的单点。一次 Redis 主从切换引发的毛刺,让所有限流判断超时——我们当时用的是同步调用,Redis 响应慢了 200ms,网关整体 RT 被拖到 3 秒,连健康检查都差点挂了。后来加了本地缓存做降级,但缓存的过期和一致性问题又搞得人焦头烂额。每到流量高峰,监控页面上 Redis 的 `get` 调用量比业务请求量还高十倍,这太讽刺了。

第三版:令牌桶 + 多层缓冲

被 Redis 拖垮两次后,我们把思路回归到本地 + 集中式混合架构。核心思想是:把限流决策分成两层。第一层是每台网关上的本地令牌桶,按集群平均份额预分配权限——比如集群总 QPS 1 万,5 台实例,每台本地桶容量 2000,但允许瞬时借用 10% 余量。第二层是 Redis 滑动时间窗口作为“总闸”,只做校准,不做每次请求的判决。

这样,90% 的请求走本地桶,毫秒级完成,零网络开销。只有本地桶令牌不足或超过借用量时才去问 Redis。同时我们给 Redis 请求加了超时熔断:如果 Redis 不可用,直接放行——毕竟“不限制”比“拖垮网关”要好,后续靠全局监控来兜底。那个黑色星期五的场景再模拟一次,即使 Redis 挂了,每台机器最多多放行 10% 的流量,整体不会超过 2× 集群容量,系统依然稳得住。

第四版(进行中):动态配额与业务感知

现在的版本已经不只是防“打爆”,而是变得更“聪明”。我们把限流阈值与实时监控数据联动:根据每个微服务的 RT、错误率和 CPU 使用率,动态调整配额。比如订单服务延迟升高,网关自动将其限流阈值下调 20%,多出来的流量转发到证券服务——因为证券服务有缓存,还能扛。

同时,我们为重要客户加了独立配额池,用 `Lua` 脚本在 Redis 里做原子扣减,保证优先级。普通用户流量则共享一个大池,池子满时按随机概率丢弃低优先级请求。这背后的本质是从“固定阈值”转向“目标自适应”。运维同学不用再半夜爬起来调参数了,我们正尝试用简单的 PID 控制器来自动调节配额。

总结:没有万能方案

回顾这段演进,最大的感触是:限流不仅是技术问题,更是架构取舍。单一方案永远不会完美——单机限流简单但不公平,全局限流精确但引入依赖,混合方案平衡了性能和一致,却要求你有很强的监控和运维能力。

对我们来说,最关键的转折点是接受不完美的现实:Redis 会抖动、网络会延迟、机器会故障。所以现在每个方案都预留了降级路径:本地桶保证基础保护,Redis 只做强化配置。这套思路走下来,网关已经平稳运行了半年,再也没出现过那个黑色星期五的惨状。但我知道,下一次流量形态变化时,我们可能又要开启新一轮演进。这就是微服务网关的宿命:永远在路上。

本文转载自 阿乐技术社区,原文地址:https://www.leleweb.cn/thread-174.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~