1. 从一个线上事故说起:为什么需要熔断、降级、限流
做了几年微服务,最怕的不是新需求,而是线上接口突然堵死的那几分钟。之前我们有个订单查询接口,平时毫秒级响应,结果一次大促预热,上游把一批用户数据推送过来,直接打满了数据库连接池。数据库连接拿不到,SQL越积越多,Tomcat线程被一个个卡在等待上。等我把日志打开的时候,线程栈里全都是waiting for connection,下游全部调用开始跟着超时,最后连不相关的用户登录接口也一起变慢,容器健康检查失败,服务被编排平台反复重启,整个链路雪崩。
那次事故之后我们才认真把高可用的三件套补齐:限流、熔断、降级。这三样东西不是同一个层面的解决方案,限流是把冲进来的洪水挡在外面,熔断是发现下游已经不行了赶紧停手,降级是就算真的失败了也给用户一个还能接受的结果。它们单独拎出来任何一环都有短板,只有组合在一起,微服务的入口、链路和兜底才算是完整闭环。
这篇文章就围绕这三个核心点,从设计思路、参数配置、代码实现,到真实的排查经验,完整走一遍。适合正在做微服务改造的团队,尤其是那些已经上了Spring Cloud但还没有把保护机制真正落地的项目。如果你被线上雪崩吓过一次,看完这篇文章应该会有比较强的共鸣。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体设计思路:三种手段的定位与配合
2.1 限流:守住入口,别让流量冲进来
限流做的是“自我防御”。不管下游多健壮,单机或集群的处理能力总有一个天花板。限流就是在这个天花板前面画一条红线,流量到了红线就拦截,让系统始终运行在可控水位上。
常见的算法有令牌桶、漏桶和滑动窗口。令牌桶允许一定的突发流量,因为桶里攒了令牌,冲进来一波也能被放行一部分;漏桶则把请求排成队,处理速度恒定,很像高速收费站一根车道慢慢放车。还有一个容易混淆的概念是滑动窗口,它把时间切成小格子,统计每个格子内的请求数,用来精确控制“每分钟最多6000次”这类窗口级QPS。
在实际系统里,我一般会在两个位置做限流:第一层是网关,按IP、用户ID、接口路径去限,挡住明显异常的流量;第二层是微服务内部的业务方法,按某个核心资源去限,比如“根据订单号查询详情”这个操作。网关层挡不了所有情况,比如两个服务之间互相调用,流量不经过网关,就必须在服务内部有保护。
2.2 熔断:发现下游不行了,及时止损
熔断是面向依赖的“快速失败机制”。它的原型是电路断路器:正常的时候开关闭合,电流畅流;一旦发生故障,开关弹开,后续请求直接走失败分支,不再去触碰故障点。
微服务里的熔断器有三种状态:关闭、打开、半开。关闭时请求正常发往下游;当错误率或慢调用比例超过阈值,熔断器打开,后续请求直接短路,不发起真实调用;过一段时间进入半开状态,放几个探针流量过去试试水温,如果成功了就慢慢恢复关闭,如果还是失败就继续打开。这个机制保证了故障点不会被持续压垮,也给了系统恢复的时间。
很多人觉得超时就能解决一切,但超时只是“等了一会儿再报错”。如果每秒进来1000个请求,每个要等3秒才超时,这3秒内线程、内存、连接全部被占着,资源池很快耗尽。熔断的意义在于失败得足够快,把资源留给还能处理的请求。
2.3 降级:兜底给用户一个可接受的结果
降级是“损失一部分功能,保住核心体验”。它可以是服务层面的,比如推荐接口挂了,就返回一个默认的热门商品列表;可以是数据层面的,比如最新评论查不到,就返回Redis里的缓存数据;也可以是页面层面的,比如下单按钮置灰,提示“活动火爆,请稍后再试”。
降级和熔断经常混在
