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。超出的数据包要么排队,要么丢弃。
避坑指南:我曾经在一个实时交易系统里尝试用漏桶,结果被业务方骂惨了。为什么?因为交易请求不能丢,丢了就是钱。漏桶的“溢出丢弃”机制在这里完全不可接受。这种场景应该用令牌桶+队列缓冲,或者直接上消息队列。
漏桶算法的流程图
下面这张图把漏桶的完整流程画出来了,我建议你多看几遍:
漏桶的优缺点总结
聊了这么多,最后做个总结:
- 优点:输出绝对平滑,实现简单,对下游系统最友好
- 缺点:无法利用突发带宽,请求可能被无辜丢弃,不适合需要保证交付的场景
我个人习惯在系统设计时先问自己三个问题:下游能扛突发吗?请求能丢吗?需要平滑输出吗?如果三个问题里有两个指向“是”,漏桶就是你的首选。
嗯,漏桶算法就聊到这儿。它不复杂,但用对地方就是一把利器。下次你遇到流量毛刺把系统打崩的问题,不妨试试这个老办法。