go语言后端开发学习(七)——如何在gin框架中集成限流中间件
一.什么是限流
限流又称为流量控制(流控),通常是指限制到达系统的并发请求数。
我们生活中也会经常遇到限流的场景,比如:某景区限制每日进入景区的游客数量为8万人;沙河地铁站早高峰通过站外排队逐一放行的方式限制同一时间进入车站的旅客数量等。
限流虽然会影响部分用户的使用体验,但是却能在一定程度上报障系统的稳定性,不至于崩溃(大家都没了用户体验)。
而互联网上类似需要限流的业务场景也有很多,比如电商系统的秒杀、微博上突发热点新闻、双十一购物节、12306抢票等等。这些场景下的用户请求量通常会激增,远远超过平时正常的请求量,此时如果不加任何限制很容易就会将后端服务打垮,影响服务的稳定性。
此外,一些厂商公开的API服务通常也会限制用户的请求次数,比如百度地图开放平台等会根据用户的付费情况来限制用户的请求数等。
二.常见的限流算法
2.1 漏桶算法
2.1.1 漏桶算法的原理
漏桶法的原理比较简单,假设我们有一个水桶按固定的速率向下方滴落一滴水,无论有多少请求,请求的速率有多大,都按照固定的速率流出,对应到系统中就是按照固定的速率处理请求。原理图如下:
漏桶法的关键点在于漏桶始终按照固定的速率运行,但是它并不能很好的处理有大量突发请求的场景,毕竟在某些场景下我们可能需要提高系统的处理效率,而不是一味的按照固定速率处理请求。
关于漏桶算法,在开发中我们可以使用三方的开源框架,uber团队有一个开源的github.com/uber-go/ratelimit库,下面网站是由漏桶算法集成以下如何简单的在gin中集成一个由漏桶算法实现的限流中间(这里我用的是我尝试自己编写的一个漏桶算法代码,大家可以选择自己写也可以选择使用上面的开源框架):
//开源框架版
package mainimport ("time""github.com/gin-gonic/gin""go.uber.org/ratelimit"
)func pong(c *gin.Context) {c.JSON(200, gin.H{"code": 200,"message": "pong",})}func LimitHandler() gin.HandlerFunc {return func(c *gin.Context) {r := ratelimit.New(1) //每秒1个请求//每次滴水允许通过的请求数量// 限流if r.Take().Sub(time.Now()) > 0 {c.JSON(200, gin.H{"code": 429,"message": "Server busy",})c.Abort()}}
}func main() {r := gin.Default()r.Use(LimitHandler()){r.GET("/ping", pong)}r.Run(":8080")
}
//自己实现版//main.go
package mainimport ("awesomeProject1/limit""time""github.com/gin-gonic/gin""go.uber.org/ratelimit"
)func pong(c *gin.Context) {c.JSON(200, gin.H{"code": 200,"message": "pong",})}func main() {r := gin.Default()r.Use(limit.LimitMiddleware()){}r.Run(":8080")
}//limit.go
package limitimport ("sync""time""github.com/gin-gonic/gin"
)type Bucket struct {sync.MutexlastAccess time.Timerequests int64 // 当前已经接收请求次数MaxRequests int64 // 最大可接受请求次数interval time.Duration // 时间间隔
}func NewBucket(maxRequests int64, interval time.Duration) *Bucket {return &Bucket{lastAccess: time.Now(),requests: 0,MaxRequests: maxRequests,interval: interval,}
}func (b *Bucket) Allow() bool {b.Lock() //加锁defer b.Unlock()now := time.Now()if now.Sub(b.lastAccess) > b.interval {b.requests = 0b.lastAccess = now}if b.requests < b.MaxRequests {b.requests++return true}return false
}func LimitMiddleware() gin.HandlerFunc {bucket := NewBucket(1, 10*time.Second)return func(c *gin.Context) {if !bucket.Allow() {c.JSON(200, gin.H{"code": 429,"message": "Server busy",})c.Abort()}c.Next()}
}
2.2 令牌桶算法
2.2 令牌桶算法的原理
令牌桶其实和漏桶的原理类似,令牌桶会按固定的速率往桶里放入令牌,并且只要能从桶里取出令牌就能通过,令牌桶支持突发流量的快速处理。原理图如下:
当我们在令牌桶里面取不到令牌时我们就会选择拒绝该次请求。
2.2.2 基于令牌桶实现的限流中间件
和上面的漏桶限流一样,这里有关令牌桶的限流博主还是给出两个版本,一个是开源第三方库,同时博主也会写一个自己实现的限流中间件供大家参考:
- 首先是第三方库,这里我们可以考虑使用github.com/juju/ratelimit这一第三方库,下面我们来看一下如何基于这一第三方库封装出外面的限流中间件:
// filepath:/limit/limiter
package limitimport ("time""github.com/gin-gonic/gin""github.com/juju/ratelimit"
)// 考虑到我们可能会对不同的请求做不同的限流,因此需要一个通用的实现接口
type LimiterInterface interface {Key(c *gin.Context) string //基于context实现获取对应限流器键值对GetBucket(key string) (*ratelimit.Bucket, bool) // 获取限流器AddBuckets(rules ...LimiterBucketRule) LimiterInterface // 添加限流器规则
}type Limiter struct{ // 限流器(用来记录不同接口对应的不同限流策略)LimiterBuckets map[string]*ratelimit.Bucket
}type LimiterBucketRule struct { // 限流器规则Key string //FillInterval time.Duration // 时间间隔Capacity int64 // 容量Quantum int64 // 每次放置的令牌量
}// 接口的具体实现 filepath:/limit/method_limiter.go
package limitimport ("strings""github.com/gin-gonic/gin""github.com/juju/ratelimit"
)type MethodLimiter struct {*Limiter
}func NewMethodLimiter() LimiterInterface {l := &Limiter{LimiterBuckets: make(map[string]*ratelimit.Bucket)}return &MethodLimiter{Limiter: l}
}func (l MethodLimiter) Key(c *gin.Context) string {url := c.Request.RequestURIindex := strings.Index(url, "?")if index != -1 {url = url[:index]}return url
}func (l MethodLimiter) GetBucket(key string) (*ratelimit.Bucket, bool) {bucket, ok := l.LimiterBuckets[key]return bucket, ok
}func (l MethodLimiter) AddBuckets(rules ...LimiterBucketRule) LimiterInterface {for _, rule := range rules {if bucket, ok := l.LimiterBuckets[rule.Key]; !ok {bucket = ratelimit.NewBucketWithQuantum(rule.FillInterval, rule.Capacity, rule.Quantum)l.LimiterBuckets[rule.Key] = bucket}}return l
}// 在gin框架中集成限流中间件 filepath: /middleware/limiter.go
package middlewareimport ("awesomeProject1/limit""fmt""github.com/gin-gonic/gin"
)func LimitHandler(l limit.LimiterInterface) gin.HandlerFunc {return func(c *gin.Context) {key := l.Key(c)if bucket, ok := l.GetBucket(key); ok {count := bucket.TakeAvailable(1)fmt.Println("key", key, "count", count)if count == 0 {c.JSON(429, gin.H{"code": 429,"msg": "too many request",})c.Abort()}}c.Next()}
}//测试样例 main.go
package mainimport ("awesomeProject1/limit""awesomeProject1/middleware""time""github.com/gin-gonic/gin"
)func pong(c *gin.Context) {c.JSON(200, gin.H{"code": 200,"message": "pong",})}func main() {r := gin.Default()limiter := limit.NewMethodLimiter()limiter.AddBuckets(limit.LimiterBucketRule{Key: "/ping",FillInterval: 10 * time.Second,Capacity: 1,Quantum: 1,},)r.Use(middleware.LimitHandler(limiter)){r.GET("/ping", pong)}r.Run(":8080")
}
大家可以自己测试一下
结语
上面就是一些常见的限流策略,虽然说现在限流策略已经不再是单体架构而是迈向分布式,但是万变不离其宗,主要还是基于上面所说的策略进行拓展
参考文章:
李文周博客——常用限流策略——漏桶与令牌桶介绍
相关文章:

go语言后端开发学习(七)——如何在gin框架中集成限流中间件
一.什么是限流 限流又称为流量控制(流控),通常是指限制到达系统的并发请求数。 我们生活中也会经常遇到限流的场景,比如:某景区限制每日进入景区的游客数量为8万人;沙河地铁站早高峰通过站外排队逐一放行的…...

SpringBoot2:web开发常用功能实现及原理解析-整合EasyExcel实现Excel导入导出功能
1、工程包结构 主要是这5个Java类 2、导入EasyExcel包 这里同时贴出其他相关springboot的基础包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><depend…...
CTFShow-信息搜集
Web1: 题目描述:开发注释未及时删除 。 打开题目后提示web1:where is flag? ctrlu读取源码。 Web2: 题目描述:js前台拦截 无效操作 打开题目后显示:无法查看源代码 右键无法用,…...

Facebook的虚拟现实功能简介:社交网络的新前沿
在科技飞速发展的今天,虚拟现实(VR)已经从科幻小说中的梦想变成了触手可及的现实。作为全球领先的社交平台,Facebook(现已更名为Meta)正大力推动虚拟现实技术的发展,以重新定义用户的社交体验。…...

Redis embstr 编码
embstr 编码 是 Redis 中一种优化存储小型字符串的编码方式。它是 Redis 内部存储字符串的多种方式之一,特别适用于存储长度不超过 44 字节的小字符串。...

【Elasticsearch系列二】安装 Kibana
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
中国电子学会202403青少年软件编程(Python)等级考试试卷(三级)真题与解析
202403Python 三级真题 一、选择题 1.在 Python 中,hex(2023)的功能是?( ) A.将十进制数 2023 转化为十六进制数 B.将十进制数 2023 转化为八进制数 C.将十六进制数 2023 转化为十进制数 D.将八进制数 2023 转化为十进制数 2.下列表达式的值与其他三个选项不相…...

k8s 资源管理
文章目录 ResourceQuota什么是资源配额定义一个ResourceQuotaResourceQuota的使用 LimitRangeLimitRange的用途示例1:配置默认的requests和limits示例2:配置requests和limits的范围 QoS什么是服务质量保证示例1:实现QoS为Guaranteed的Pod示例…...

演示:基于WPF的自绘的中国地铁轨道控件
一、目的:演示一个基于WPF的自绘的中国地铁轨道控件 二、效果演示 北京地铁 成都地铁 上海地铁 深圳地铁 南京地铁 长春地铁 哈尔滨地铁 武汉地铁 厦门地铁 香港地铁 三、功能 支持平移、缩放等操作 鼠标悬停显示线路信息和站点信息 按表格显示,按纸张…...
设计模式(Design Patterns)
设计模式(Design Patterns)是软件开发人员在软件设计过程中面临的一般性问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式的目的是为了提高代码的可重用性、可维护性、可读性、可靠性以及灵活性。设…...
C++:opencv生成结构元素用于膨胀腐蚀等cv::getStructuringElement
cv::getStructuringElement 是 OpenCV 库中用于生成结构元素的函数。结构元素在形态学操作中(如膨胀、腐蚀、开运算、闭运算等)扮演着关键角色。这个函数可以创建不同形状和尺寸的结构元素,以适应不同的图像处理需求。 函数原型 cv::Mat cv…...
最大余额法,解决百分比计算相加不等于100%(扇形/饼图百分比使用的此算法)
在开发项目的过程中有时候需要进行计算百分比,例如计算饼状图百分比。有时候在计算的过程中常规四舍五入计算会发生所有计算的值相加不等于100%的情况 这是 get_percent_value 函数的 JavaScript 版本: /*** 最大余额法,解决百分比计算相加不…...

哈希表简单介绍
概念 在顺序结构以及平衡树中,元素关键字与他们存储的位置并没有直接的映射关系,从而会影响查找关键字的效率,顺序结构中查找关键字的时间复杂度为O(N),平衡树查找关键字的时间复杂度为O(log2^…...

kafka 之 本地部署单机版
安装JDK 查看你选择的版本需要安装哪一个版本的jdk 网址 下载 JDK下载 注:如果网页不允许下载,使用wget命令下载即可,下载之后安装。 建议使用rpm安装,之后使用 update-alternatives --config java 控制当前环境使用Java的版…...

开发一款通过蓝牙连接控制水电表的微信小程序
增强软硬件交互 为了更好的解决师生生活中的实际问题,开发蓝牙小程序加强了和校区硬件的交互。 比如通过蓝牙连接控制水电表,减少实体卡片的使用。添加人脸活体检测功能,提高本人认证效率,减少师生等待时间。 蓝牙水电控展示 蓝…...

力扣14.最长公共前缀
思路:将字符串数组中第一个字符串用作参考; 8.将他的长度作为范围,因为超范围了之后就不会再有公共前缀了 9.将字符串数组的长度也作为范围,意思是便利字符串数组中的字符串 11.开始第一层循环,依次遍历第一个字符串的…...

你也喜欢“钓鱼“吗?
免责声明:本文仅做分享! 目录 什么是网络钓鱼 流程 攻击手法 0-隐藏自己 1-office宏 创建xxx.dotm 创建xxx.docx 2-RLO 自解压 3-快捷方式lnk 4-邮件伪造 Swaks Gophish 5-网站克隆 setoolkit nginx反向代理 前端页面克隆 6-wifi钓鱼 7-其他 防御 溯源 反…...

druid jdbc 执行 sql 输出 开销耗时
druid 执行sql输出 参考链接配置_LogFilter alibaba/druid Wiki GitHub 看不太懂的往这里瞅瞅。 1. 别名映射 这个地方 给我们提供了 5 种 logfilter : log4j、log4j2、slf4j、commonlogging和commonLogging 每一种实际上都代表一个日志框架 或 日志门面。 -Ddruid.fil…...
C++如何处理内存碎片问题
目录 一.前言二.什么是内存碎片三.如何处理内存碎片 一.前言 这篇文章简单讨论一下C如何处理内存碎片问题。 二.什么是内存碎片 所谓内存碎片就是系统中存在的不能供进程使用的小块内存,主要包括外部碎片以及内部碎片。 外部碎片:内存分配和回收的过…...
FreeRTOS常用API接口函数
提示:FreeRTOS常用API接口函数:并对部分参数附上自己的解释,后面继续补充 FreeRTOS常用API接口函数 1.任务相关的API1.1 创建任务:xTaskCreate1.2 开启任务调度器函数:vTaskStartScheduler1.3 任务的删除:vTaskDelete1…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...