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…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
