漏桶算法核心是恒定速率放行与队列缓冲:请求入桶,桶以固定速率漏水(处理),超容积则拒绝;关键参数为桶容量和漏速换算的单位时间最大请求数。

漏桶算法的核心逻辑是什么
漏桶限流本质是「恒定速率放行 + 队列缓冲」:请求进来先入桶(队列),桶以固定速率(如 100 req/s)匀速“漏水”(处理请求),桶满则拒绝。它不关心突发流量大小,只看是否超容积和是否能持续漏出——所以实现关键不是时间精度,而是「桶容量」和「漏速换算成单位时间允许的最大请求数」。
手写时最容易错的是把「漏速」当成每秒 sleep 一次去模拟漏水,这既不准又阻塞线程。正确做法是用「上次漏水时间戳 + 当前时间差」推算已漏出多少,从而得出当前可用容量。
LeakyBucket 类怎么设计才不丢精度
必须用高精度时间(std::chrono::steady_clock),不能用 time() 或 clock();容量和水位用整型(如 int64_t),避免浮点累积误差;漏速统一换算为「纳秒内允许通过的请求数」,例如 100 QPS → 每 10ms 漏 1 个,即漏速 = 1'000'000'000 / 100 纳秒/请求。
实操建议:
- 用
last_leak_time记录上一次更新水位的时间点,每次allow()先调用私有函数leak()更新当前水位 - 水位按「请求数」建模,不是字节或权重,简化计算;桶满判断用
current_water_ > capacity_,不是 >= - 不要在构造函数里启动后台线程去定期 leak —— 这会引入竞态且无必要;所有更新都在
allow()中惰性完成
allow() 方法里怎么算当前可用水位
核心是两步:先根据时间差补漏,再判断加新请求后是否溢出。注意「补漏量」必须向下取整(floor),因为漏是离散事件,不能漏出 0.7 个请求。
示例逻辑片段(关键步骤):
int64_t now = now_ns(); int64_t elapsed = now - last_leak_time_; int64_t leaked = elapsed / leak_interval_ns_; // 向下取整 current_water_ = std::max(0L, current_water_ - leaked); last_leak_time_ = now; <p>if (current<em>water</em> + 1 water++; return true; } return false;</p>
常见错误:
- 用
double算elapsed / leak_interval_ns_再转 int → 可能因浮点舍入多漏或少漏 1 个 - 漏完没重置
last_leak_time_→ 下次计算 elapsed 巨大,瞬间清空桶 - 把
capacity_设为 0 或负数,导致allow()永远返回 false,且无提示
多线程环境下要不要加锁
要。漏桶的 current_water_ 和 last_leak_time_ 是共享可变状态,allow() 不是纯函数。用 std::atomic 不够——因为 leak + 判断 + 加水是三步复合操作,必须原子化整个临界区。
推荐方案:
- 用
std::mutex最稳妥,别为了性能过早用std::shared_mutex(读多写少也不适用,因为allow()总是写) - 如果性能瓶颈真出现在锁上(比如单桶扛百万 QPS),才考虑分片桶(sharded bucket):按请求 key 哈希到 N 个独立
LeakyBucket实例 - 千万别用
std::atomic直接增减current_water_—— 它无法保证 leak 和 allow 的时序一致性,会导致水位虚高
真正难的不是写对单个桶,而是当桶被复用在多个服务模块时,忘记初始化 last_leak_time_ 为构造时刻,或者误把不同粒度的漏速(如 100 QPS vs 1000 QPS)混用在同一实例上。这些地方一错,限流就形同虚设。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











