最近在一套服务端渲染的制造执行系统(Spring Boot 2.1 + Spring Cloud 老版本微服务,JSP + EasyUI 前端,十几个业务服务)里做了一个统一接口限流组件。起因不是优化诉求,是一次生产事故:疑似客户侧用脚本对系统做批量数据查询,请求洪峰把业务服务直接打死了。复盘时发现这类风险早有伏笔——一条库存流水查询接口带全表聚合,页脚汇总要六秒多,人手连点就能把它拖垮;另一个条码查询接口早被脚本连点过,业务同事在 Service 里手写了一段”十五秒内只许查一次”的私有判断,散落在业务代码里。事故只是把这些既有脆弱点一次性引爆。
做完整个工程回头盘点,一个感受越来越清晰:Redis 固定窗口计数是全部工作里最简单的部分,两个小时就写完了;真正花时间、真正值得复盘的,是算法之外的一切——拦截器在哪类应用里会静默失效、限流键从哪取身份、被拦的响应长什么样、Redis 挂了限流器怎么办、运维怎么不重启调参、前端怎么消化”被限流”这个新的失败模式。
市面上九成的限流文章在对比算法:固定窗口的边界突刺、滑动窗口的内存开销、令牌桶的突发容忍、再补一段 Lua 脚本保证原子性。这些内容对,但它们回答的是”怎么实现一个限流器”,而不是”怎么在一个跑了十年、前后端契约刻在化石里的系统里落地限流”。这篇文章讲后者。