go语言后端开发学习(七)——如何在gin框架中集成限流中间件

devtools/2024/9/18 23:39:51/ 标签: golang, 学习, 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")
}

大家可以自己测试一下

结语

上面就是一些常见的限流策略,虽然说现在限流策略已经不再是单体架构而是迈向分布式,但是万变不离其宗,主要还是基于上面所说的策略进行拓展

参考文章:
李文周博客——常用限流策略——漏桶与令牌桶介绍


http://www.ppmy.cn/devtools/112295.html

相关文章

关系型数据库,NoSQL和内存数据库三种数据库的比较

定义 关系型数据库&#xff1a; 关系型数据库是一种基于关系模型的数据存储系统&#xff0c;它使用表格的形式来存储数据&#xff0c;并通过SQL&#xff08;结构化查询语言&#xff09;来进行数据的查询、更新、插入和删除操作。每个表由列&#xff08;属性&#xff09;和行&a…

JS 扩展运算符有哪些使用场景?

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 扩展运算符有哪些使用场景&#xff1f;直接进入正题 一、复制数组 const a1 [1, 2];// 写法一 const a2 [...a1]; // 写法二 const [...a2] a1;二、合并数组 const part1 [1, 2, 3]; const part2 …

每日奇难怪题(持续更新)

1.以下程序输出结果是() int main() {int a 1, b 2, c 2, t;while (a < b < c) {t a;a b;b t;c--;}printf("%d %d %d", a, b, c); } 解析:a1 b2 c2 a<b 成立 ,等于一个真值1 1<2 执行循环体 t被赋值为1 a被赋值2 b赋值1 c-- c变成1 a<b 不成立…

Prompt提示词技巧

文章目录 &#x1f34a; 探索AI内容创作核心&#xff1a;提示词Prompt1 什么是提示词工程?1.1 提示词的原理1.2 提示词工程师的前景1.3 提示词工程师的门槛是否较低&#xff1f;1.4 提示词的未来展望 2 提示词编写的基本技巧3 常见的提示词框架3.1 CO-STAR框架3.2 BORKE框架3.…

JavaScript 基础 - 第17天_AJAX综合案例

文章目录 Day02_AJAX综合案例目录学习目标01.案例_图书管理-介绍目标讲解小结 02.Bootstrap 弹框_属性控制目标讲解小结 03.Bootstrap 弹框_JS控制目标讲解小结 04.案例_图书管理\_渲染列表目标讲解小结 05.案例_图书管理\_新增图书目标讲解小结 06.案例_图书管理\_删除图书目标…

ZYNQ LWIP(RAW API) TCP函数学习

1 LWIP TCP函数学习 tcp_new()–新建控制块 这个函数用于分配一个TCP控制块,它通过tcp_alloc()函数分配一个TCP控制块结构来存储TCP控制块的数据信息, 如果没有足够的内容分配空间,那么tcp_alloc()函数就会尝试释放一些不太重要的TCP控制块, 比如就会释放处于TIME_WAIT、C…

nosql数据库技术与应用知识点

Nosql知识回顾 大数据处理流程 数据采集(flume、爬虫、传感器)数据存储(本门课程NoSQL所处的阶段) Hdfs、MongoDB、HBase等数据清洗(入仓) Hive等数据处理、分析(Spark、Flink等)数据可视化数据挖掘、机器学习应用(Python、SparkMLlib等) 大数据时代存储的挑战(三高) 高并发…

SpringBoot框架下的房产销售系统开发

第一章 绪 论 1.1背景及意义 房产销售也都将通过计算机进行整体智能化操作&#xff0c;对于房产销售系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;首页、个人中心、用户管理、销售经理管理、房源信息管理、房源类型管理、房子户型管理、交易订单管…

[苍穹外卖]-11数据可视化接口开发

ECharts Apache ECharts是一款基于JavaScript的数据可视化图表库, 提供直观, 生动,可交互, 可定制的数据可视化图表 入门案例: 使用Echarts, 前端关注图表的配置, 不同的配置影响展示的效果, 后端关注图表所需要的数据格式 <!DOCTYPE html> <html><head>&l…

ssl 协议工作过程

ssl 协议工作过程 ChatGPT 说&#xff1a; ChatGPT SSL&#xff08;Secure Sockets Layer&#xff09;协议是用来确保网络通信安全的加密协议&#xff0c;已被TLS&#xff08;Transport Layer Security&#xff09;取代&#xff0c;但它的工作过程仍然是理解现代加密协议的重…

【软件测试】盒木进销存管理系统 需求说明书

目录 1 引言 2 项目概述 3 平台、角色和权限 3.1 Web端 4 Web端需求 4.1 登录/注册页面 4.1.1 业务描述 4.1.2 需求描述 4.1.3 行为人 4.1.4 UI页面 4.1.5 业务规则 4.2 首页 4.2.1 业务描述 4.2.2 需求描述 4.2.3 行为人 4.2.4 UI界面 4.2.5 业务规则 4.3报…

git reset 几点疑问

疑问&#xff1a;使用 git reset --hard <commit-hash-from-branch-B> 将工作区状态reset为其他branch的某点。 如果当前工作区的分支&#xff08;比如 branch A&#xff09;上使用 git reset --hard 将其状态重置为另一个分支&#xff08;比如 branch B&#xff09;的某…

Apache DolphinScheduler 跨工作流复杂依赖功能详解

大家好&#xff0c;我叫高楚枫&#xff0c;来自阿里云 EMR 团队的开发工程师&#xff0c;同时也是 Apache DolphinScheduler 的 PMC 成员之一。 今天非常高兴能在这里和大家分享关于跨工作流复杂依赖的功能详解。 引言 在现代的数据处理和调度过程中&#xff0c;工作流的依赖…

开思通智网-科技快报20240912:人工智能辅助实现复杂糖苷分子检测

【本周新进展】 人工智能辅助实现复杂糖苷分子检测 https://news.sciencenet.cn/htmlnews/2024/9/529548.shtm IFA2024|元鼎智能推出全新“真智能”泳池机器人 https://tech.gmw.cn/2024-09/07/content_37548570.htm 马斯克宣称的“最强AI训练系统”上线 https://news.science…

Pr 入门系列之三:挑选与添加媒体到序列(上)

掌握如何使用源监视器预览和筛选媒体素材&#xff0c;了解新建序列以及设置序列的方法。 ◆ ◆ ◆ 查看及挑选素材内容 源 Source面板&#xff0c;又称源监视器 Source Monitor。其主要作用是&#xff1a;预览源剪辑、选取片段、插入或覆盖剪辑到序列。 双击项目面板里的素材…

项目:构建高可用、负载均衡的高效Web服务器

一、项目需求 实现高可用&#xff0c;防止单点故障&#xff0c;实现负载均衡 负载均衡&#xff1a;将流量分摊给一个或多个后端服务器&#xff0c;提高公司业务的可用性、方便业务后期的水平动态扩展。 二、项目描述 本项目旨在构建一个集高可用性、负载均衡、高效Web服务及…

打包部署若依(RuoYi)SpringBoot后端和Vue前端图文教程

打包后端‘ 1&#xff0c;打开若依&#xff0c;点击右侧的Maven展开Maven管理&#xff0c;选择ruoyi>Lifecycle 先双击clean清除原本启动项目时生成的文件。然后点击package等待项目打包&#xff0c;切记要取消运行再打包 打包完成后会在ruoyi-admin>src>target里面…

leetcode53.最大子数组和

题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&…

setImmediate() vs setTimeout() 在 JavaScript 中的区别

setImmediate() vs setTimeout() 在 JavaScript 中的区别 在 JavaScript 中&#xff0c;setImmediate() 和 setTimeout() 都用于调度任务&#xff0c;但它们的工作方式不同。 JavaScript 的异步特性 JavaScript 以其非阻塞、异步行为而闻名&#xff0c;尤其是在 Node.js 环境…

HTTP 四、HttpClient的使用

一、简单介绍 1、简介 HttpClient是Apache Jakarta Common下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包&#xff0c;并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中&#xff0c;比如Apache Jakarta上很著…