news 2026/5/16 20:33:49

漏桶算法详解:从原理到实现,为什么它仍是限流的重要一环?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
漏桶算法详解:从原理到实现,为什么它仍是限流的重要一环?

目录

      • 一、什么是漏桶算法?
      • 二、动手实现一个简单的漏桶限流器
      • 三、漏桶的优缺点
      • 四、Q&A:个人思考
        • Q1:严格控制每个请求的执行间隔真的有必要吗?漏桶还有使用场景吗?
        • Q2:令牌桶和漏桶到底有什么区别?
        • Q3:既然漏桶本质是“队列 + 定时任务”,那能不能直接用定时任务替代?
        • Q4:如果限流超过 1000 QPS 怎么办?
        • Q5:“为什么令牌桶允许突发,漏桶不允许?”

在应对突发流量时,限流是我们保障系统稳定性的第一道防线。常用的限流工具如Google Guava RateLimiterAlibaba SentinelRedis + Lua 脚本(分布式限流)Nginx 限流Spring Cloud Gateway等,虽然功能各异,但底层大多基于四种经典算法:计数器(Counter)、滑动窗口(Sliding Window)、漏桶(LeakyBucket)和令牌桶(Token Bucket)。深入理解这些算法的原理,有助于我们更合理地选择和使用限流工具。

上一篇文章我们聊了令牌桶,今天来聊聊它的“孪生兄弟”——漏桶算法(Leaky Bucket)。本文将从以下几个方面展开:

  • 什么是漏桶算法?
  • 手动实现一个简单的漏桶
  • 漏桶的优缺点分析
  • 常见问题与作者思考(Q&A)

一、什么是漏桶算法?

漏桶算法(Leaky Bucket Algorithm)是一种经典的流量整形(Traffic Shaping)和限流(Rate Limiting)算法。它的核心思想非常直观:

无论输入流量多么突发或不均匀,输出流量始终保持恒定速率。

就像一个底部有小孔的水桶:

  • 水可以快速倒入(请求可以瞬间涌入);
  • 但水只能以固定速度从底部小孔漏出(系统以固定速率处理请求);
  • 桶有固定容量,若水倒得太快导致溢出,则多余水流直接丢弃(请求被拒绝)。

这种机制天然具备平滑突发流量、保护下游系统的能力。


二、动手实现一个简单的漏桶限流器

在后端开发中,我们通常用一个有界队列来模拟漏桶:

  • 桶容量= 队列的最大长度;
  • 漏水速率= 后台定时任务以固定间隔从队列头部取出并“处理”请求。

伪代码大概长这样:

publicclassLeakyBucketLimiter{//用队列模拟桶(注意:实际要存的是要执行的任务,作者用Boolean代替了)privatefinalArrayBlockingQueue<Boolean>queue;//定时器privatefinalScheduledExecutorServicescheduler;//构造函数publicLeakyBucketLimiter(intcapacity,intrate){//参数校验if(capacity<=0||rate<=0){thrownewIllegalArgumentException("参数需要大于0");}//初始化桶this.queue=newArrayBlockingQueue<Boolean>(capacity);//计算漏水间隔,确保每个请求漏出的时间间隔相同longinterval=1000L/rate;//创建一个单线程调度器this.scheduler=Executors.newSingleThreadScheduledExecutor(r->{Threadt=newThread("LeakyBucketLimiter-Thread");//线程设置为守护线程,确保JVM退出时,线程也能退出t.setDaemon(true);returnt;});scheduler.scheduleAtFixedRate(this::leak,//要执行的任务interval,//初始延迟interval,//间隔时间TimeUnit.MILLISECONDS);}//提交请求publicbooleantryAcquire(){//实际要把任务加入队列,这里只是模拟returnqueue.offer(true);}privatevoidleak(){//从队头移除一个元素,模拟漏水queue.poll();}//关闭定时器,并清空队列publicvoidshutdown(){scheduler.shutdown();queue.clear();}}

关于守护线程:守护线程(如 GC、JIT 编译线程)为用户线程服务。当所有非守护线程结束时,JVM 会直接退出,不再等待守护线程完成。

使用示例:

// 每秒最多 3 个请求,桶容量 = 3SimpleLeakyBucketlimiter=newSimpleLeakyBucket(3,3);for(inti=0;i<8;i++){if(limiter.tryAcquire()){//实际要把任务放进队列!!!System.out.println(" 请求 "+i+" 被接受");}else{System.out.println(" 请求 "+i+" 被拒绝(桶满)");}Thread.sleep(100);// 每 100ms 发一个请求(10 QPS > 3 QPS)}

是不是很简单?核心逻辑就是:请求先入队,后台线程匀速消费


三、漏桶的优缺点

优点:请求稳定,能很好利用性能,保护下游系统。

缺点:无法应对突发流量,容易积压请求,大量请求被拒绝。

正因为这些限制,纯漏桶在现代 Web 服务中较少单独使用,更多被令牌桶滑动窗口取代。但在某些特定场景(如硬件对接、音视频帧率控制、流量整形网关),漏桶仍是不可替代的选择。


四、Q&A:个人思考

Q1:严格控制每个请求的执行间隔真的有必要吗?漏桶还有使用场景吗?

我觉得没必要,只有定时执行的场景(音/视频控制帧率)能用到,绝大部分都用令牌桶和滑动窗口。(你们怎么看?)

Q2:令牌桶和漏桶到底有什么区别?
  • 漏桶:把请求放到缓冲队列,让它按时执行,队满拒绝。
  • 令牌桶:按时往桶里发放令牌,到桶里拿令牌,能拿到令牌就能执行,否则拒绝。
Q3:既然漏桶本质是“队列 + 定时任务”,那能不能直接用定时任务替代?

漏桶 =有界队列 + 定时消费 + 入口拒绝。如果只用定时任务处理无界队列,会导致请求堆积、内存爆炸、延迟飙升——这已经不是限流,而是“慢消费”。真正的限流必须在入口快速拒绝超量请求

Q4:如果限流超过 1000 QPS 怎么办?

rate > 1000时,interval = 1000 / rate可能小于 1ms,超出操作系统调度精度。此时应放弃高频定时器,改用懒更新 + 纳秒时间戳的方式(类似令牌桶的实现),在每次请求时动态计算可释放的配额。

Q5:“为什么令牌桶允许突发,漏桶不允许?”

令牌桶可以预存令牌,空闲时攒下的令牌可用于突发;
漏桶的队列只能排队等待,即使空闲,新请求也必须等系统按节奏“漏水”才能腾出空间。
但注意:漏桶允许“有限突发”——只要不超过桶容量!


🐾 欢迎留言讨论!作者爱思考,也爱和你一起探索技术本质~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 22:26:42

vgmstream音频解码:游戏音频格式转换完整指南

vgmstream音频解码&#xff1a;游戏音频格式转换完整指南 【免费下载链接】vgmstream vgmstream - A library for playback of various streamed audio formats used in video games. 项目地址: https://gitcode.com/gh_mirrors/vg/vgmstream 快速上手&#xff1a;5分钟…

作者头像 李华
网站建设 2026/5/13 1:45:58

Python入门篇【列表基础使用巩固练习】

文章目录题目代码题目 # 定义学生列表 # 追加数字31到列表的尾部 # 追加一个新列表[29,33,30]到列表的尾部 # 取出第一个元素 # 取出最后一个元素 # 查找元素31&#xff0c;在列表中的下标位置 # 写出最后列表的长度是多少代码 # 定义学生列表 age_stu_list [21, 25, 21, 23…

作者头像 李华
网站建设 2026/5/14 22:08:56

Wan2.2-T2V-5B模型部署指南:基于OpenSpec的容器化方案

Wan2.2-T2V-5B模型部署指南&#xff1a;基于OpenSpec的容器化方案 在短视频内容需求呈指数级增长的今天&#xff0c;从一条广告语生成一段动态视频&#xff0c;已不再是影视工作室的专属能力。越来越多的企业希望将文本到视频&#xff08;Text-to-Video, T2V&#xff09;技术集…

作者头像 李华