Golang编写自定义IP限流中间件

目录

  • 基于令牌桶的限流算法
  • 实现高并发限流(使用golang官方限流器)
    • Go代码
    • 测试记录
      • ab -t 1 -c 1 http://127.0.0.1:8080/api/resource
      • 结果预测:1秒内最多生成10个令牌,而总共有20个串行的请求,结果应该是1个成功(在50ms结束),1个失败(后50ms内还未有新的令牌生成),1个成功,1个失败。。。
      • 结果输出(符合预期)
  • 升级:根据每个IP地址进行限流
    • Go代码
    • 测试记录
      • ab -t 1 -c 1 http://127.0.0.1:8080/api/resource
      • 结果预测:1秒内最多生成2个令牌,桶容量为5 代表在1/2秒内的最大并发量是5,总共有20个串行的请求,结果应该是先成功5个(桶容量全使用成功), 之后剩余成功的是1个,其余全部失败
      • 结果输出(符合预期)

基于令牌桶的限流算法

  • r:每秒钟向桶内放入 r 个令牌,即每隔 1/r秒放一个令牌
  • b:桶的最大容量是 b,桶满后试图再放入的令牌会被丢弃掉
  • 当有人请求 n 个令牌时,如果桶中的令牌数小于n,则请求放阻塞或直接放弃,否则顺利从桶中取走 n 个令牌
    • 当 b > 1 时,任意 1/r 秒内最多可以取走 b 个令牌
      • 限制很短的一个瞬间的一个最高的并发量,b=最高的并发量,r=最短的时间段
    • 当 b = 1 时,每秒钟最多可被取走 r 个令牌
      • 限制每秒钟的最高的QPS:b=1,r=最高的QPS
      • 限制每分钟的最高请求量:把每分钟的请求量/60=转换成1秒钟,赋给 r 就可以了
      • 限制每5分钟、每10分钟,同理

实现高并发限流(使用golang官方限流器)

Go代码

源码地址: GitHub-golang版本

middleware/rateLimiterMiddleware.go

package middlewareimport ("net/http""time""github.com/gin-gonic/gin""golang.org/x/time/rate"
)var Limiter *rate.Limiter// 定义一个中间件函数来进行限流
func RateLimiterMiddleware() gin.HandlerFunc {return func(c *gin.Context) {if !Limiter.AllowN(time.Now(), 1) {c.JSON(http.StatusTooManyRequests, gin.H{"message": "Rate limit exceeded"})// 设置休眠和业务时长一样,为了更好从日志出看出规则time.Sleep(50 * time.Millisecond)c.Abort()return}c.Next()}
}

main.go

func main() {r := gin.Default()// 创建一个限流器,每秒允许最多10个请求middleware.Limiter = rate.NewLimiter(rate.Limit(10), 1)// 使用限流中间件r.Use(middleware.RateLimiterMiddleware())r.GET("/api/resource", func(c *gin.Context) {time.Sleep(50 * time.Millisecond)c.JSON(http.StatusOK, gin.H{"message": "Resource accessed"})})r.Run(":8080")
}

测试记录

ab -t 1 -c 1 http://127.0.0.1:8080/api/resource

使用ab压力测试,并发量为1的(相当于串行),在1秒内不断发出请求(算下来,每个请求50ms,总共能发出20个请求)

结果预测:1秒内最多生成10个令牌,而总共有20个串行的请求,结果应该是1个成功(在50ms结束),1个失败(后50ms内还未有新的令牌生成),1个成功,1个失败。。。

结果输出(符合预期)

在这里插入图片描述
在这里插入图片描述

升级:根据每个IP地址进行限流

Go代码

源码地址: GitHub-golang版本

middleware/ipRateLimiterMiddleware.go

package middlewareimport ("net/http""sync""time""github.com/gin-gonic/gin""golang.org/x/time/rate"
)var IPLimiter *IPRateLimiterfunc NewIPRateLimiter() *IPRateLimiter {return &IPRateLimiter{limiter: make(map[string]*rate.Limiter),}
}type IPRateLimiter struct {mu      sync.Mutexlimiter map[string]*rate.Limiter
}func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {i.mu.Lock()defer i.mu.Unlock()limiter, exists := i.limiter[ip]if !exists {limiter = rate.NewLimiter(2, 5) // 每秒2个请求,桶容量为5i.limiter[ip] = limiter}return limiter
}// 定义一个中间件函数来进行限流
func IPRateLimiterMiddleware() gin.HandlerFunc {return func(c *gin.Context) {ip := c.ClientIP()limiter := IPLimiter.GetLimiter(ip)if !limiter.Allow() {c.JSON(http.StatusTooManyRequests, gin.H{"message": "Rate limit exceeded"})// 设置休眠和业务时长一样,为了更好从日志出看出规则time.Sleep(50 * time.Millisecond)c.Abort()return}c.Next()}
}

main.go

func main() {r := gin.Default()// 创建IP限流器middleware.IPLimiter = middleware.NewIPRateLimiter()// 使用限流中间件r.Use(middleware.IPRateLimiterMiddleware())r.GET("/api/resource", func(c *gin.Context) {time.Sleep(50 * time.Millisecond)c.JSON(http.StatusOK, gin.H{"message": "Resource accessed"})})r.Run(":8080")
}

测试记录

ab -t 1 -c 1 http://127.0.0.1:8080/api/resource

使用ab压力测试,并发量为1的(相当于串行),在1秒内不断发出请求(算下来,每个请求50ms,总共能发出20个请求)

结果预测:1秒内最多生成2个令牌,桶容量为5 代表在1/2秒内的最大并发量是5,总共有20个串行的请求,结果应该是先成功5个(桶容量全使用成功), 之后剩余成功的是1个,其余全部失败

  1. 当程序启动时,限流器初始化,桶是空的,没有令牌。
  2. 在每秒的前两次令牌生成中,每次生成2个令牌,并放入桶中。
  3. 在第3秒,生成的2个令牌中只有1个能够被放入桶中,因为桶已经满了。
  4. 当使用ab发出请求时,前5个请求依次拿到了令牌并成功,每个请求耗时50ms。此时总耗时为250ms。
  5. 第6个请求在300ms,第7个请求在350ms,第8个请求在400ms,第9个请求在450ms,第10个请求在500ms。
  6. 因为限流器的速率是每秒2个请求,也就是每500ms生成一个令牌。 第500ms时新的令牌生成,所以第6-10请求期间,是没有令牌的,所以都请求失败,而11个请求时有新令牌生成,所以能成功。
  7. 之后的请求以此类推,需要等待新令牌生成。

结果输出(符合预期)

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 7.6使用mysql-8.0.31-1.el7.x86_64.rpm-bundle.tar安装Mysql 8.0

https://downloads.mysql.com/archives/community/是社区版的官网,可以选择版本下载。 cat /etc/redhat-release可以看到系统版本是CentOS Linux release 7.6.1810 (Core),uname -r可以看到版本是3.10.0-957.el7.x86_64。 yum remove -y mysql-libs把…

来看看Python MetaClass元类详解

MetaClass元类,本质也是一个类,但和普通类的用法不同,它可以对类内部的定义(包括类属性和类方法)进行动态的修改。可以这么说,使用元类的主要目的就是为了实现在创建类时,能够动态地改变类中定义…

Python3.10 IDLE更换主题

前言 自定义主题网上有很多,3.10IDLE的UI有一些新的东西,直接扣过来会有些地方覆盖不到,需要自己测试着添几行配置,以下做个记录。 配置文件路径 Python安装目录下的Lib\idlelib\config-highlight.def。如果是默认安装&#xf…

轻松鲨-AI文案写作 人工智能聊天

轻松鲨AI助手使用地址:http://www.qingsongsha.com?utm_sourcenavigation_website 或前往苹果App Store下载“轻松鲨APP” 支持AI连续对话聊天,帮你解答各种疑问... 让AI帮你画思维导图写文案,提升工作效率... 内置多场景专业模板&#xff0…

解决 tesserocr报错 Failed to init API, possibly an invalid tessdata path : ./

问题描述 我们在初次使用tesserocr库的时候,可能会报以下错误: RuntimeError: Failed to init API, possibly an invalid tessdata path: ./ 这是因为我们在使用 conda 创建的环境中找不到"tessdata"这个文件夹。 解决办法 这时候把Tessera…

品牌是什么?品牌加分秘笈曝光

作为一个多年的品牌人,这几年,历经迷惘和探索,迷惘在于: 1.品牌部是花钱部门,不直接创造营收,不受老板重视、钱少。 2.品牌作业难以衡量,一般以曝光、阅读、声量指数等指标来衡量,…

026-从零搭建微服务-文件服务(二)

写在最前 如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 源码地址(后端):https://gitee.com/csps/mingyue 源码地址(前端):https://gitee.com/csps…

鼠标拖拽拖动盒子时,与盒子内某些点击事件冲突问题解决

目录 问题解决思路解决代码(标注【主要代码】的为重点) 问题 拖动该悬浮球时,鼠标弹起可能会触发悬浮球内事件 解决思路 鼠标拖动盒子时,将 isMove 设为 true 意为正在拖动盒子,此时将 class"btns_move" 遮…

利用 SOAR 加快事件响应并加强网络安全

随着攻击面的扩大和攻击变得越来越复杂,与网络攻击者的斗争重担落在了安全运营中心 (SOC) 身上。SOC 可以通过利用安全编排、自动化和响应 (SOAR) 平台来加强组织的安全态势。这一系列兼容的以安全为中心的软件可加快事…

单元格法近似求解多边形最大内接矩形问题【思路讲解+java实现】

文章目录 问题描述问题解决方案多边形网格化区分每个单元格是在多边形内部还是外部根据已标记单元格寻找最大内接矩形剪枝优化多角度旋转 案例测试代码实现说明 问题描述 给定一个多边形的点集,希望找出多边形内部面积最大的矩形。该问题可能出现在,从一…

聊聊Spring事务同步器TransactionSynchronization

在一些业务场景中可能我们需要去对某一个spring事务的生命周期进行监控,比如在这个事务提交,回滚,被挂起的时候,我们想要去执行一些自定义的操作,这怎么去做呢?其实spring作为一个高扩展性的框架&#xff0…

关系型三大范式与BCNF有什么用呢

学的时候就知道是一堆公式。 实际中在设计表的时候可能会用到。 前提是关系型数据库,比如mysql。 (实际中oracle比mysql更好用。但是他收费啊。) 第一范式:每个属性都是原子的(需要做到每个属性都是不可分割的。&…

02 java ---- Android 基础app开发

目录 相对布局 显示一个美女 显示两个美女 安卓APP启动过程 安卓布局控件 常用布局之相对布局 常用布局之相对布局 padding和margin 按键美化 常用布局之线性布局 安卓按键响应的几种方式 直接设置按键的onClick绑定的函数 自定义类实现按键监听事件的接口 匿名内…

[NLP] LLM---<训练中文LLama2(一)>训练一个中文LLama2的步骤

一 数据集 【Awesome-Chinese-LLM中文数据集】 【awesome-instruction-dataset】【awesome-instruction-datasets】【LLaMA-Efficient-Tuning-数据集】Wiki中文百科(25w词条)wikipedia-cn-20230720-filteredBaiduBaiKe(563w词条) …

4.后端·新建子模块与开发(传统模式)

文章目录 学习资料新建子模块与各层查询entity的列表entitymapper层service层controller层 测试 学习资料 https://www.bilibili.com/video/BV13g411Y7GS?p8&spm_id_frompageDriver&vd_sourceed09a620bf87401694f763818a31c91e b站的学习视频 新建子模块与各层 在r…

Linux Ubuntu20.04深度学习环境快速配置命令记录

一、驱动安装 1、更新系统包 sudo apt-get updatesudo apt-get upgrade 2、安装显卡驱动 使用apt方式安装驱动,多数情况不容易成功, 使用一下方法更佳: 1.查看合适显卡的驱动版本 ubuntu-drivers devices NVIDIA GeForce 驱动程序 - …

POJ 3684 Physics Experiment 弹性碰撞

一、题目大意 我们有N个半径为R厘米的球,固定在距离地面高度为H的管道上,刚开始释放第一个,之后每过一秒释放一个,释放下面的球不会影响到上面的球的高度,忽略一切阻力,认为球之间的碰撞为弹性碰撞&#x…

【电子元件】常用电子元器件的识别之电容器

目录 前言1. 电容器的简介2.电容器的识别1. 铝电解电容器2.钽电解电容器3.固态电解电容器4.瓷介电容器5. 贴片陶瓷电容器6. 聚丙烯电容7. 金属化聚丙烯薄膜电容器8. 独石电容器9. 涤纶电容器10. 超小型金属化聚酯薄膜电容器11. 可变电容器11.1 空气可变电容器11.2 薄膜介质可变…

JMeter基础 —— 使用Badboy录制JMeter脚本!

1、使用Badboy录制JMeter脚本 打开Badboy工具开始进行脚本录制: (1)当我们打开Badboy工具时,默认就进入录制状态。 如下图: 当然我们也可以点击录制按钮进行切换。 (2)在地址栏中输入被测地…

国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词

目录 一、使用深度学习中的循环神经网络(RNN)或长短时记忆网络(LSTM)生成诗词二、优化:使用双向 LSTM 或 GRU 单元来更好地捕捉上下文信息三、优化:使用生成对抗网络(GAN)或其他技术…