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

一.什么是限流

限流又称为流量控制(流控),通常是指限制到达系统的并发请求数。
我们生活中也会经常遇到限流的场景,比如:某景区限制每日进入景区的游客数量为8万人;沙河地铁站早高峰通过站外排队逐一放行的方式限制同一时间进入车站的旅客数量等。
限流虽然会影响部分用户的使用体验,但是却能在一定程度上报障系统的稳定性,不至于崩溃(大家都没了用户体验)。
而互联网上类似需要限流的业务场景也有很多,比如电商系统的秒杀、微博上突发热点新闻、双十一购物节、12306抢票等等。这些场景下的用户请求量通常会激增,远远超过平时正常的请求量,此时如果不加任何限制很容易就会将后端服务打垮,影响服务的稳定性。
此外,一些厂商公开的API服务通常也会限制用户的请求次数,比如百度地图开放平台等会根据用户的付费情况来限制用户的请求数等。

二.常见的限流算法

2.1 漏桶算法

2.1.1 漏桶算法的原理

漏桶法的原理比较简单,假设我们有一个水桶按固定的速率向下方滴落一滴水,无论有多少请求,请求的速率有多大,都按照固定的速率流出,对应到系统中就是按照固定的速率处理请求。原理图如下:
在这里插入图片描述

漏桶法的关键点在于漏桶始终按照固定的速率运行,但是它并不能很好的处理有大量突发请求的场景,毕竟在某些场景下我们可能需要提高系统的处理效率,而不是一味的按照固定速率处理请求。

关于漏桶算法,在开发中我们可以使用三方的开源框架,uber团队有一个开源的github.com/uber-go/ratelimit库,下面网站是由漏桶算法集成以下如何简单的在gin中集成一个由漏桶算法实现的限流中间(这里我用的是我尝试自己编写的一个漏桶算法代码,大家可以选择自己写也可以选择使用上面的开源框架):

//开源框架版
package main

import (
	"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 main

import (
	"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 limit

import (
	"sync"
	"time"

	"github.com/gin-gonic/gin"
)

type Bucket struct {
	sync.Mutex
	lastAccess  time.Time
	requests    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 = 0
		b.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 limit

import (
	"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 limit

import (
	"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.RequestURI
	index := 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 middleware

import (
	"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 main

import (
	"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")
}

大家可以自己测试一下

结语

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/881559.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32】独立看门狗(IWDG)原理详解及编程实践(上)

本篇文章是对STM32单片机“独立看门狗&#xff08;IWDG&#xff09;”的原理进行讲解。希望我的分享对你有所帮助&#xff01; 目录 一、什么是独立看门狗 &#xff08;一&#xff09;简介 &#xff08;二&#xff09;、独立看门狗的原理 &#xff08;三&#xff09;、具体操…

51.字符串比较实例-用户登录

//已知正确的用户名和密码&#xff0c;请用程序实现模拟用户登录 //总共三次机会&#xff0c;登录之后给出相应的提示 import java.util.Scanner;public class 登录 {public static void main(String[] args) {//1.定义两个变量&#xff0c;记录正确的用户名和密码String righ…

springboot+redis+缓存

整合 添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 连接redis&#xff0c;配置yml文件 主机 端口号 数据库是哪一个 密码 配置类 p…

滑动窗口算法专题(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 目录 滑动窗口算法的简介 209. 长度最小的子数组 3.无重复字符的最长子串 1004. 最大连续1的个数III 1658. 将减到0的最小…

基于python上门维修预约服务数据分析系统

目录 技术栈和环境说明解决的思路具体实现截图python语言框架介绍技术路线性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示操作可行性详细视频演示源码获取 技术栈和环境说明 结合用户的使用需求&#xff0c;本系统采用运用较为广…

Linux相关概念和重要知识点(5)(权限的修改、时间属性)

1.权限的修改 &#xff08;1&#xff09;提权行为 普通用户是会受到权限的限制&#xff0c;但root账户无视一切权限限制&#xff0c;因此当我们要获取更高的权限&#xff0c;有一种办法就是将自己变成root或者短暂拥有和root一样的权力。 普通用户 -> root &#xff1a;s…

robomimic应用教程(一)——模型训练

Robomimic使用集中式配置系统来指定所有级别的(超)参数 本文介绍了配置&#xff08;推荐&#xff09;和启动训练运行的两种方法 目录 一、使用config json&#xff08;推荐&#xff09; 二、在代码中构造一个配置对象 三、查看运行结果 1. 实验结果会存在一个固定文件夹中…

地信、测绘、遥感、地质相关岗位招聘汇总

3s等相关专业25秋招&提前批招聘信息 该岗位信息表&#xff0c;覆盖全国各大省市&#xff0c;招聘岗位主要针对地信、测绘、地质、遥感、城规等专业。 1800WebGIS开发岗位汇总表 该信息表&#xff0c;主要是WebGIS开发岗为主&#xff0c;岗位要求熟悉熟悉Openlayers&#…

Python编码系列—Python桥接模式:连接抽象与实现的桥梁

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

哪个快?用300万个图斑测试ArcGIS Pro的成对叠加与经典叠加

​​​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 在使用ArcGIS Pro的过程中&#xff0c;很多朋友发现&#xff0c;Pro有个成对叠加工具集。很多…

计算机毕业设计之:教学平台微信小程序(

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

全国职业院校技能大赛(大数据赛项)-平台搭建hive笔记

在大数据时代&#xff0c;数据量呈爆炸性增长&#xff0c;传统的数据处理工具已难以满足需求。Hive作为一个开源的数据仓库工具&#xff0c;能够处理大规模数据集&#xff0c;提供了强大的数据查询和分析能力&#xff0c;是大数据学习中的关键工具。在全国职业院校技能大赛&…

Git使用详解:从安装到精通

前言 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html页面等&#xff09;&#xff0c;在软件开发过程中被广泛使用。 可以理解&#xff1a; git是一个管理源代码的工具&#xff0c;主要用于企业团队开…

【数据结构C语言】【入门】【首次万字详细解析】入门阶段数据结构可能用到的C语言知识,一章让你看懂数据结构!!!!!!!

前言&#xff1a;欢迎各位光临本博客&#xff0c;这里小编带你直接手撕入门阶段的数据结构的C语言知识&#xff0c;让你不再看见数据结构就走不动道。文章并不复杂&#xff0c;愿诸君耐其心性&#xff0c;忘却杂尘&#xff0c;道有所长&#xff01;&#xff01;&#xff01;&am…

学习笔记——RegNet:Designing Network Design Spaces

RegNet&#xff1a;Designing Network Design Spaces RegNet&#xff1a;设计一个网络设计空间 论文地址&#xff1a; https://arxiv.org/pdf/2003.13678 1、前言 在这项工作中&#xff0c;作者提出了一种新的网络设计范例。 作者的目标是帮助增进对网络设计的理解并发现跨设置…

网络安全:建筑公司会计软件遭受暴力攻击

黑客正在暴力破解基金会会计服务器上高权限账户的密码&#xff0c;这些账户广泛用于建筑行业&#xff0c;从而侵入企业网络。 这一恶意活动最先被 Huntress 发现&#xff0c;其研究人员于 2024 年 9 月 14 日检测到了此次攻击。 Huntress 已经发现这些攻击对管道、暖通空调、…

元学习的简单示例

代码功能 模型结构&#xff1a;SimpleModel是一个简单的两层全连接神经网络。 元学习过程&#xff1a;在maml_train函数中&#xff0c;每个任务由支持集和查询集组成。模型先在支持集上进行训练&#xff0c;然后在查询集上进行评估&#xff0c;更新元模型参数。 任务生成&…

时间安全精细化管理平台存在未授权访问漏洞

漏洞描述 登录--时间&amp;安全精细化管理平台存在未授权访问漏洞导致与员工信息泄露 FOFA&#xff1a; body"登录--时间&amp;安全精细化管理平台" 漏洞复现 POC: IP/acc/_checkinoutlog_/

Linux开发工具(git、gdb/cgdb)--详解

目录 一、Linux 开发工具分布式版本控制软件 git1、背景2、使用 git&#xff08;1&#xff09;预备工作——安装 git&#xff1a;&#xff08;2&#xff09;克隆远程仓库到本地&#xff08;3&#xff09;把需要提交的代码拷贝到本地仓库&#xff08;4&#xff09;提交本地仓库文…

基于协同过滤+SpringBoot+Vue的剧本杀服务平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤JavaSpringBootV…