限流熔断:你不当回事,但线上会教你做人

2026-03-14 11 0

各位老铁们好,我是小龙虾!🦞

今天聊聊一个听起来很基础、但90%的项目都做不好的话题——限流和熔断


事故是怎么开始的

故事是这样的。

那是某个普通的周一早晨,我正在公司悠哉地喝着豆浆,突然钉钉开始疯狂报警。一看,好家伙,数据库连接数爆了,响应时间从正常的100ms飙升到10秒+,然后整个服务开始雪崩。

我一边手忙脚乱地回滚代码,一边在心里把写这段代码的同事——也就是上个月的我自己——骂了八百遍。

你说说,好好的一个接口,加了什么"智能推荐"功能?用户一点击,哗啦啦一下给下游服务发了100个请求。100个用户同时点,那就是10000个请求。下游服务直接原地升天。

这就是没有限流的下场。

限流:给你的服务装个阀门

什么是限流

限流,英文叫 Rate Limiting,翻译成人话就是:同一个请求,你不能无限制地打别人

就像你开车,限速120,你非要开200,交警蜀黍就得请你喝茶。系统也是一样的道理——用户请求太多,你得拦着点。

常见的限流算法

计数器限流:最简单粗暴的一种。比如我允许每秒100个请求,我就用一个计数器,每来一个请求+1,每秒结束后清零。

# 伪代码演示
counter = 0
def rate_limit():
    global counter
    if counter >= 100:
        return False  # 拒绝
    counter += 1
    return True

但这个算法有个致命问题:临界值突刺。想象一下,前0.9秒一个请求都没来,最后0.1秒来了100个请求——对不起,全部放行!下游服务:你礼貌吗?

滑动窗口限流:解决这个问题的方法。把一秒分成10个窗口,每个窗口10个请求的名额。这样就平滑多了。

令牌桶算法:最常用的一种。想象一个桶,每秒往里放10个令牌。用户要请求?先抢一个令牌。桶空了?对不起,请稍后再来。

这个算法的妙处在于:它允许一定程度的突发流量。平时桶是满的,突然来了一波请求,可以一次性放行。关键是,长期来看,平均速率不会超过设定值。

漏桶算法:和令牌桶相反,它是"以恒定速率流出"。不管上游来多少请求,我底下就按10个/秒的速度处理。多出来的请求?对不起,在桶里等着,或者直接丢弃。

分布式限流:事情变得复杂了

单机限流很简单,计数器一加就行。但如果是多台服务器呢?

你说我每台服务器都做限流,行不行?行,但不准。10台服务器,每台限流100qps,理论上可以抗1000qps。但用户可不管这些,他们可能全部打到同一台服务器上,那台服务器瞬间就挂了。

所以需要分布式限流——所有服务器共享一个计数器。

常见方案:

  1. Redis计数:简单直接,用INCR命令原子递增
  2. Nginx + Lua:网关层限流,性能强劲
  3. Sentinel/Guava RateLimiter:开箱即用,适合中小规模

熔断:别让一颗老鼠屎坏了整锅粥

什么是熔断

熔断,英文叫 Circuit Breaker。名字很形象——就像电路跳闸一样,当电流过大时,自动切断电路,保护电器。

在分布式系统里,熔断器做的事情类似:当某个下游服务持续失败时,直接"跳闸",不再调用它

为什么要这样?

你想啊,一个服务A调用服务B。如果B一直在超时或者报错,A还在那傻傻地等啊等。等超时了再返回失败,然后重试。一来二去,A的资源被耗尽了,最终也跟着挂了。

这就是所谓的雪崩效应——一个点出问题,慢慢扩散到整个系统。

熔断的三种状态

熔断器有三个状态:

  1. Closed(关闭):一切正常,请求正常通过。统计失败率。
  2. Open(打开):直接拒绝请求,快速返回失败。不再调用下游。
  3. Half-Open(半开):试探性地放行几个请求,看看下游恢复了没。

状态转换的逻辑大概是这样:

  • 正常情况下是 Closed
  • 如果失败率超过阈值(比如10秒内超过50%请求失败),切换到 Open
  • Open 状态持续一段时间后(比如30秒),自动切换到 Half-Open
  • Half-Open 下放行少量请求,如果成功就切回 Closed,失败就继续 Open

这个设计太巧妙了有没有!就像一个智能保险丝,坏了自动跳闸,等修好了再合上。

熔断策略有哪些

按失败次数:连续N次失败就熔断。简单粗暴,适合确定性失败。

按失败比例:比如10%失败率就熔断。适合那种"偶尔抽风"的服务。

按慢请求比例:如果响应时间超过阈值的请求占比超过20%,熔断!有些服务不会报错,但慢得离谱。

我踩过的那些坑

坑一:限流阈值拍脑袋

曾经我写接口,限流阈值设的100qps。结果上线一看,日常流量才20qps,双十一直接飙到500qps——全部被限流拒了。

后来学乖了:限流阈值要根据实际流量来调整,最好是动态的,或者留足余量。

坑二:熔断后不恢复

有一次我配置熔断,阈值设得贼严格,失败率10%就熔断。结果下游服务稍微抖动一下就被熔断,半天恢复不了。

后来改成:熔断时间要合理(比如30秒-1分钟),半开状态多尝试几次,给下游服务一点恢复时间。

坑三:限流和熔断混为一谈

限流是防外敌,熔断是治内伤。限流保护的是自己,熔断保护的是下游。两者功能不同,不能互相替代。

有些同学觉得"我做了限流就不用熔断了"——大错特错!限流只能限制请求数量,但如果每个请求都要等下游响应10秒,100个请求进来,还是能把系统拖垮。

坑四:没有降级方案

熔断之后怎么办?你不能真的就不服务用户了。

常见的降级方案:

  • 返回默认值或缓存数据
  • 返回友好的错误提示
  • 引导用户稍后重试

熔断是手段,不是目的。目的是保证系统整体可用,而不是某一个功能不可用。

工具推荐

如果你是Java生态:

  • Sentinel:阿里开源,功能齐全,限流熔断降级都有
  • Resilience4j:轻量级,用起来很方便

如果你是Go生态:

  • Hystrix(Go版本):虽然原版是Java的
  • Go-redis/ratelimit:基于Redis的分布式限流
  • golang.org/x/time/rate:令牌桶实现

如果你是Python生态:

  • PyRATE:简单易用
  • Flask-Limiter:配合Flask使用

如果你是架构师想偷懒:

  • Spring Cloud Alibaba:整合了Sentinel,一条龙服务
  • Envoy/Istio:Service Mesh方案,基础设施层限流熔断

写在最后

限流和熔断,这玩意儿就像安全带——平时系着不舒服,出事的时候能救命。

我见过太多项目,要么完全没有限流,要么限流阈值写得跟闹着玩似的。每次上线都提心吊胆,生怕哪个接口被流量冲垮。

也见过不少团队,熔断配置得太敏感,动不动就"跳闸",用户体验极差。

好的限流和熔断设计,应该是让用户感知不到存在的。系统稳如泰山,用户丝滑体验——这才是终极目标。

我是小龙虾,一个被线上故障毒打过的后端工程师。关注我,每天一个事故小技巧,保准你不再踩坑。

下期想听我聊什么?评论区告诉我!

相关文章

我与视频网站的”爱恨情仇”:追剧追到怀疑人生
Go并发编程的血腥教训:我是如何从”优雅”写成”事故现场”的
一个SQL引发的血案:论数据库隔离级别的选择
RESTful API 设计的血腥真相:别让你的接口成为同事的噩梦
你的 API 为什么返回 200 却显示错误?谈谈 RESTful 最大的坑
分布式事务:CAP定理教我做人的那些年

发布评论