3. 流控算法原理

流量控制里,漏桶算法是个老牌角色。我第一次接触它是在一个日志采集系统里——业务方半夜跑批任务,日志量瞬间暴涨,直接把后端ES集群打挂了。后来我就在采集端加了个漏桶,效果立竿见影。

说白了,漏桶就是让你不管外面怎么狂风暴雨,出去的水滴永远匀速。它不关心你来得有多猛,只关心你走得有多稳。

漏桶的核心原理

想象一个桶,底部有个小洞。你可以往桶里倒水,但水只能从小洞里匀速流出去。如果倒得太快,桶满了,水就溢出来——溢出的部分就是被丢弃的请求。

这个模型对应到网络里:

  • 桶:一个缓冲区,用来暂存突发流量
  • 底部小洞:固定的处理速率,比如每秒100个请求
  • 溢出的水:被拒绝的请求,直接丢掉或返回错误

我习惯用两个参数来描述一个漏桶:

参数 含义 典型值
桶容量(burst) 最多能暂存多少请求 1000个请求
流出速率(rate) 每秒处理多少个请求 100 req/s

算法逻辑其实很简单:

每次请求到达时:
  如果桶未满 → 放入桶中,等待匀速流出
  如果桶已满 → 直接丢弃请求(溢出)
  
后台定时任务:
  每过 1/rate 秒,从桶里取出一个请求处理

嗯,这里要注意——漏桶的“桶”是先进先出的队列。先来的请求先被处理,后来的排队等待。这一点和后面要讲的令牌桶完全不同。

核心结论:漏桶的输出速率是绝对平滑的,不管输入有多大的毛刺。代价是——如果桶满了,请求直接丢。

漏桶 vs 令牌桶:两个“桶”的正面交锋

很多新手容易把漏桶和令牌桶搞混。我当年也踩过这个坑——在一个API网关里用了漏桶,结果发现业务方需要应对突发流量,漏桶直接把突发请求全丢了,被投诉了一下午。

咱们直接对比一下:

维度 漏桶 令牌桶
输出特性 绝对平滑,恒定速率 允许突发,只要令牌够
对突发请求的处理 暂存后匀速处理,满了就丢 积累令牌后一次性放行
是否允许短时超速 不允许,超出桶容量直接丢 允许,只要桶里有令牌
适用场景 需要严格平滑输出的场景 允许短时突发的场景
实现复杂度 简单,一个队列+定时器 稍复杂,需要令牌生成逻辑

你想想看——漏桶像是一个严格的交通警察,每秒钟只放行固定数量的车,后面的车排队等着,等不及的就走别的路。令牌桶则像是一个发券的,你手里有券就能走,没券就等着,但你可以提前攒券。

我的经验:如果下游系统对流量波动极其敏感(比如数据库写入、硬件设备控制),用漏桶。如果下游能扛住短时压力(比如微服务、缓存层),用令牌桶更灵活。

漏桶的适用场景分析

漏桶不是万能的,但它有几个特别擅长的领域:

1. 保护脆弱的下游系统

我曾经维护过一个老旧的计费系统,每秒只能处理50笔交易。业务高峰期流量能冲到500笔/秒。用漏桶把速率卡死在50笔/秒,桶容量设成200,剩下的请求直接返回“系统繁忙”。虽然丢了一些请求,但至少系统没崩。

2. 日志采集与上报

客户端上报日志的场景,我最推荐漏桶。为什么?因为日志丢了不可惜,但日志系统被打崩了才要命。漏桶的“溢出即丢弃”特性在这里非常合适——你想想看,丢了10条日志和丢了整个日志集群,哪个更痛?

3. 硬件设备控制

工业场景里,PLC、传感器这些设备对输入速率有严格限制。我见过一个案例——数据采集频率超过设备上限,直接导致设备死机。用漏桶把数据发送速率卡死,问题就解决了。

4. 网络流量整形

运营商的流量控制里,漏桶是经典方案。比如限制某个用户的带宽为10Mbps,不管用户怎么突发,出口速率始终是10Mbps。超出的数据包要么排队,要么丢弃。

避坑指南:我曾经在一个实时交易系统里尝试用漏桶,结果被业务方骂惨了。为什么?因为交易请求不能丢,丢了就是钱。漏桶的“溢出丢弃”机制在这里完全不可接受。这种场景应该用令牌桶+队列缓冲,或者直接上消息队列。

漏桶算法的流程图

下面这张图把漏桶的完整流程画出来了,我建议你多看几遍:

漏桶算法流程图 漏桶算法流程图 请求到达 桶是否已满? 丢弃请求 放入桶中排队 桶(缓冲区) 暂存请求,先进先出 容量:burst 个请求 匀速流出(rate) 输出速率恒定 超出桶容量直接丢弃

漏桶的优缺点总结

聊了这么多,最后做个总结:

  • 优点:输出绝对平滑,实现简单,对下游系统最友好
  • 缺点:无法利用突发带宽,请求可能被无辜丢弃,不适合需要保证交付的场景

我个人习惯在系统设计时先问自己三个问题:下游能扛突发吗?请求能丢吗?需要平滑输出吗?如果三个问题里有两个指向“是”,漏桶就是你的首选。

嗯,漏桶算法就聊到这儿。它不复杂,但用对地方就是一把利器。下次你遇到流量毛刺把系统打崩的问题,不妨试试这个老办法。