GO--基于令牌桶和漏桶的限流策略
至于为什么要限流,字面意思已经很清楚了,就是为了减轻服务器的压力
下面我们将介绍两个限流策略----漏桶和令牌桶。
漏桶
原理介绍
漏桶,顾名思义就是一个漏斗,漏斗嘴的大小是固定的,所以不管漏斗现容量多大,都不会影响漏斗出水的速度。类比到我们的web服务中,我们可以为web服务准备一个固定速率的请求处理器(漏桶),对我们的请求,如果此时桶内请求量超过了桶的最大容量,那么就执行特定的抛弃策略(直接丢弃或者阻塞请求)。与此同时,对我们的请求 ,进行固定速率的处理。这样就会形成一种限流的效果。

使用
基于这个原理,有很多个人或者组织都自己开发了相应的算法实现,我们今天介绍的是github.com/uber-go/ratelimit
https://github.com/uber-go/ratelimit
这个算法比较简单,库的使用也比较清晰,有需要的可以去看一看源代码,下面我将介绍
如何在项目中使用这个库,下面我将以gin框架为例:
导包
go.uber.org/ratelimit
编写中间件
package ratelimit// 基于gin 框架 编写限流中间件 并 使用
// 限流中间件:漏桶 和 令牌桶import ("time""github.com/gin-gonic/gin"loutong "go.uber.org/ratelimit" // 漏桶中间件(具名导入)
)// 漏桶策略限流器
func RateLimitMiddleware(rate int) func(c *gin.Context) {// 使用闭包写中间件,可以在使用中间件的时候,传入指定参数( rate 是请求数,每秒处理请求数 )// 创建(漏桶)限流器,指定处理速率(不限制速率:NewUnlimited())rl := loutong.New(rate)// 返回一个函数return func(c *gin.Context) {// 获得当前的时间now := time.Now()// 尝试从漏桶中获得一个请求去处理rl.Take()// 获得处理请求花费的时间cost := time.Since(now)if cost > 0 {// 如果有等待,那么花费时间大于0c.Abort() // 丢弃请求// 对待等待的请求进行处理:阻塞 或者 丢弃}c.Next()}
}
/*
使用Take()方法的作用:
如果当前请求的流入速度太快,超过了设定的每秒请求数(RPS)的标准,那么这个 “Take” 操作就需要阻塞(暂停、等待),
不让过多的请求一下子涌入后续的处理流程,直到请求流入的速度符合设定好的每秒请求数这个要求,
以此来实现精准的限流控制,保证系统按照既定的请求处理速率稳定运行。
Take()方法返回一个time值,反映的是 请求等待的时间
*/
测试
package mainimport ("component/ratelimit""github.com/gin-gonic/gin"
)func main() {r := gin.Default()r.Use(ratelimit.RateLimitMiddleware(5))r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.NoRoute(func(c *gin.Context){c.JSON(404, gin.H{"msg": "404,您的也页面好像找不到了~",})})r.Run("127.0.0.1:8080")
}
-
优点
- 流量整形效果好
- 漏桶算法能够平滑突发流量。例如,在网络服务中,当有大量用户在短时间内发起请求(如某热门商品限时抢购活动导致大量用户同时访问电商网站),这些请求会先进入 “漏桶” 缓存起来,然后以固定的速率流出进行处理。这就使得后端服务接收到的请求是平稳的,避免了后端服务因突发的高流量而崩溃。
- 易于理解和实现
- 其原理简单直观,类似于生活中的漏斗。从编程角度看,实现一个基本的漏桶算法不需要复杂的代码结构。比如,可以用一个简单的队列来模拟 “漏桶”,记录请求的到达时间,按照固定的时间间隔从队列头部取出请求进行处理。
- 对资源的消耗可预测
- 因为处理请求的速率是固定的,所以系统在单位时间内消耗的资源(如 CPU、内存等)是可以预估的。例如,一个按照每秒 10 个请求速率处理的服务,只要提前评估每个请求处理所需的资源量,就可以大致计算出系统在一段时间内需要的资源总量。
- 流量整形效果好
-
缺点
- 可能导致响应延迟增加
- 当请求流量突发且超过桶的容量时,新的请求可能会被阻塞或丢弃。对于被阻塞的请求,会导致用户等待时间变长。例如,在一个在线支付系统中,如果因为流量限制而导致支付请求被阻塞,用户可能会等待很长时间才能完成支付,这会影响用户体验。
- 不能充分利用系统资源
- 由于漏桶算法是按照固定速率处理请求,即使系统有足够的资源来处理更多的请求,也不会加快处理速度。比如,在深夜时段,服务器资源利用率很低,但是因为漏桶算法限制了请求处理速率,使得服务器不能更快地处理请求,从而导致资源闲置。
- 可能导致响应延迟增加
令牌桶
原理介绍
令牌桶的原理其实和漏桶差不多,漏桶相当于在单位时间(比如1秒内处理多少个请求)。那么令牌桶就是表现的间接了点。他是通过指定每秒的令牌数,从而指定每秒处理请求的个数(每个请求在被处理的时候都会拿取一个令牌)

使用
同样,这个算法也有很多人去实现,我们这里以下面这个为例:
github.com/juju/ratelimit
https://github.com/juju/ratelimit这个库支持多种令牌桶模式,并且使用起来也比较简单。
导包
github.com/juju/ratelimit
编写中间件
package ratelimit// 基于gin 框架 编写限流中间件 并 使用
// 限流中间件:漏桶 和 令牌桶import ("net/http""time""github.com/gin-gonic/gin"lingpai "github.com/juju/ratelimit"
)// 令牌桶中间件
// 限流中间件: 每 fillInterval 的时间,可最多处理 1 个请求(相当于对处理请求的 打点器)
// 特点: 如果一段时间内不请求,token会存储,直到桶的最大容量cap,应对突发多个请求
func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {// 创建令牌桶 : 参数: 1. 填充时间(单位时间) 2. 容量bucket := lingpai.NewBucket(fillInterval, cap)return func(c *gin.Context) {// 判断是否限流(如果取不到 token 就限流)if bucket.TakeAvailable(1) <= 0 {c.String(http.StatusOK, "你点击的太快了,请慢点点击~")// 终止执行c.Abort()return}// 取到令牌继续执行c.Next()}
}
创建令牌桶的方法:
// 创建指定填充速率和容量大小的令牌桶
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
// 创建指定填充速率、容量大小和每次填充的令牌数的令牌桶
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
// 创建填充速度为指定速率和容量大小的令牌桶
// NewBucketWithRate(0.1, 200) 表示每秒填充20个令牌
func NewBucketWithRate(rate float64, capacity int64) *Bucket
取出令牌的方法如下:
// 取token(非阻塞)
func (tb *Bucket) Take(count int64) time.Duration
func (tb *Bucket) TakeAvailable(count int64) int64// 最多等maxWait时间取token
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)// 取token(阻塞)
func (tb *Bucket) Wait(count int64)
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool
测试
package mainimport ("component/ratelimit""time""github.com/gin-gonic/gin"
)func main() {r := gin.Default()r.Use(ratelimit.RateLimitMiddleware(1*time.Second, 10))r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.NoRoute(func(c *gin.Context){c.JSON(404, gin.H{"msg": "404,您的也页面好像找不到了~",})})r.Run("127.0.0.1:8080")
}
-
优点
- 支持突发流量处理
- 令牌桶算法允许流量在一定范围内突发。因为令牌是以固定速率生成并积累在桶中的,所以在短时间内,如果桶中有足够的令牌,就可以允许高于平均速率的突发流量通过。例如,一个令牌桶每秒产生 10 个令牌,桶的容量为 100 个令牌。在某一时刻,桶内积累了 90 个令牌,此时如果有 50 个请求同时到达,只要令牌足够,这 50 个请求就可以立即通过,这对于应对突发的高流量情况(如限时抢购活动)非常有效。
- 有效利用系统资源
- 相比于漏桶算法,令牌桶算法能够更好地利用系统资源。当系统资源充足且有足够的令牌时,请求可以快速通过,不会像漏桶算法那样限制请求只能以固定的较低速率通过。例如,在服务器负载较低的时段,大量请求可以利用积累的令牌快速处理,充分发挥系统的处理能力。
- 流量控制灵活
- 可以通过调整令牌生成速率和桶的容量来灵活地控制流量。比如,对于不同重要性的服务或者不同的用户级别,可以设置不同的令牌桶参数。对于高级别的用户或者重要的服务接口,可以设置较大的令牌生成速率和桶容量,以保证其流量优先通过。
- 平均速率限制准确
- 虽然允许突发流量,但令牌桶算法依然能够保证在较长的时间范围内,请求通过的平均速率不会超过令牌生成的速率。这就使得系统可以在允许一定程度的灵活性的同时,维持一个稳定的整体流量水平。
- 支持突发流量处理
-
缺点
- 实现相对复杂
- 与漏桶算法相比,令牌桶算法的实现较为复杂。它需要维护令牌的生成、存储和消耗的逻辑。在代码层面,需要考虑如何准确地按照固定速率生成令牌,如何有效地存储令牌(可能涉及到数据结构的选择,如队列、计数器等),以及如何正确地在请求到达时消耗令牌。
- 参数调整难度较大
- 令牌桶算法的性能高度依赖于令牌生成速率和桶容量这两个参数的设置。如果参数设置不合理,可能会导致流量控制效果不佳。例如,若令牌生成速率设置过高,可能无法有效地限制流量,导致系统过载;若桶容量设置过小,可能无法充分利用系统允许的突发流量特性,频繁地拒绝请求。而且在实际的复杂系统中,要准确地确定这两个参数的合适值需要进行大量的测试和性能评估。
- 实现相对复杂
相关文章:
GO--基于令牌桶和漏桶的限流策略
至于为什么要限流,字面意思已经很清楚了,就是为了减轻服务器的压力 下面我们将介绍两个限流策略----漏桶和令牌桶。 漏桶 原理介绍 漏桶,顾名思义就是一个漏斗,漏斗嘴的大小是固定的,所以不管漏斗现容量多大&#…...
MongoDB性能监控工具
mongostat mongostat是MongoDB自带的监控工具,其可以提供数据库节点或者整个集群当前的状态视图。该功能的设计非常类似于Linux系统中的vmstat命令,可以呈现出实时的状态变化。不同的是,mongostat所监视的对象是数据库进程。mongostat常用于…...
Axure设计之模拟地图人员移动轨迹
在产品原型设计时,为了更好的表达和呈现预期的效果,让客户或开发看一眼就能理解要实现的功能,往往需要在产品设计时尽量去接近现实,这就需要我们在使用Axure制作原型时应具有高度细节和逼真度的原型设计。原型设计不仅包含了产品的…...
Android环境搭建
Android环境搭建 第一步:安装 Homebrew 执行以下命令来安装 Homebrew: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"检测是否安装成功: brew --version第二步:安装 No…...
前端工程化面试题(一)
如何使用 Docker 部署前端项目? 使用 Docker 部署前端项目通常涉及以下几个步骤: 创建项目:首先,需要在本地创建并配置好前端项目。 准备 Docker 文件: .dockerignore:这个文件用于排除不需要上传到 Dock…...
模型案例:| 手机识别模型!
导读 2023年以ChatGPT为代表的大语言模型横空出世,它的出现标志着自然语言处理领域取得了重大突破。它在文本生成、对话系统和语言理解等方面展现出了强大的能力,为人工智能技术的发展开辟了新的可能性。同时,人工智能技术正在进入各种应用领…...
期权懂|个股期权交割操作流程是什么样的?
期权小懂每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 个股期权交割操作流程是什么样的? 一、行权申报: 期权买方在行权日通过其经纪商提交行权指令,表明其决定行使期权权利。 二、行权匹配…...
【openGauss】openGauss execute执行update语句,获取更新的行数
【openGauss】openGauss execute执行update语句,获取更新的行数 在openGauss中,可以使用execute语句执行update语句,并通过GET DIAGNOSTICS语句获取更新的行数。下面是一个示例: DO $$ DECLAREupdated_rows INTEGER; BEGINEXECUT…...
P8780 [蓝桥杯 2022 省 B] 刷题统计
题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 𝑎道题目,周六和周日每天做 𝑏 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 𝑛 题? 输入格式 输入一行包含三…...
切比雪夫不等式:方差约束下的概率估计
切比雪夫不等式:方差约束下的概率估计 背景 在概率分析中,切比雪夫不等式是一个常用的工具,它通过引入随机变量的 方差信息,给出了偏离均值的概率界限。这一不等式是对 马尔科夫不等式 的自然扩展,结合了更丰富的分布…...
使用CancellationTokenSource来控制长时间sql查询中断
前端 <!-- 透明的覆盖层,显示在页面上方,包含进度条 --><Grid Visibility"{Binding IsLoading}" Background"Transparent" HorizontalAlignment"Stretch" VerticalAlignment"Stretch" ZIndex"1&…...
小红薯最新x-s 算法补环境教程12-06更新(下)
在上一篇文章中已经讲了如何去定位x-s生成的位置,本篇文章就直接开始撸代码吧 如果没看过的话可以看:小红薯最新x-s算法分析12-06(x-s 56)(上)-CSDN博客 1、获取加密块代码 首先来到参数生成的位置&…...
wazuh-modules-sca
wazuh中安全配置评估模块主线程执行wm_sca_main最后在wm_sca_start中循环执行,不会返回 // Module main function. It wont return #ifdef WIN32 DWORD WINAPI wm_sca_main(void *arg) {wm_sca_t *data (wm_sca_t *)arg; #else void * wm_sca_main(wm_sca_t * dat…...
Uniapp的App环境下使用Map获取缩放比例
概述 目前我试过的就是你用vue后缀是拿不到比例的你可以用nvue当然uniapp的uvue应该是更加可以的我使用的是高德所以你得在高德的后台声请原生的Android的key才可以如果是vue3的开发模式的话不用使用this来获取当前对象使用scale对象来接受和改变缩放比例会比较友好然后直接走…...
微信小程序配置less并使用
1.在VScode中下载Less插件 2.在微信小程序中依次点击如下按钮 选择 从已解压的扩展文件夹安装… 3.选中刚在vscode中下载安装的插件文件 如果没有修改过插件的安装目录,一般是在c盘下C:\用户\用户名.vscode\extensions\mrcrowl.easy-less-2.0.2 我的路径是…...
“全面支持公路数字化转型升级四大任务”视频孪生解决方案
数字经济的加速布局,对交通领域数字化转型、智能化升级提出明确要求。2024年上半年,为深入贯彻落实中共中央、国务院关于加快建设交通强国、数字中国等决策部署,推进公路水路交通基础设施数字转型、智能升级、融合创新,加快发展新…...
顶顶通电话机器人开发接口对接大语言模型之实时流TTS对接介绍
大语言模型一般都是流式返回文字,如果等全部文字返回了一次性去TTS,那么延迟会非常严重,常用的方法就是通过标点符号断句,返回了一句话就提交给TTS。随着流TTS的出现,就可以直接把大模型返回的文字灌给流TTS࿰…...
P3379 【模板】最近公共祖先(LCA)
【模板】最近公共祖先(LCA) https://www.luogu.com.cn/problem/P3379 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。 输入格式 第一行包含三个正整数 N , M , S N,M,S N,M,S,分别表示…...
2030. gitLab A仓同步到B仓
文章目录 1 A 仓库备份 到 B 仓库2 B 仓库修改main分支的权限 1 A 仓库备份 到 B 仓库 #!/bin/bash# 定义变量 REPO_DIR"/home/xhome/opt/git_sync/zz_xx_xx" # 替换为你的本地库A的实际路径 REMOTE_ORIGIN"http://192.168.1.66:8181/zzkj_software/zz_xx_xx.…...
网易博客旧文-----如何在WINDOWS下载安卓(android)源代码并和eclipse做关联
如何在WINDOWS下载安卓(android)源代码并和eclipse做关联 2013-02-05 17:27:16| 分类: 安卓开发 | 标签: |举报 |字号大中小 订阅 编写安卓程序时,有时想看看安卓某些类的实现,但默认情况下环境是不带的。…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
