码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 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 middleware
    
    import (
    	"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()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试记录

    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 middleware
    
    import (
    	"net/http"
    	"sync"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	"golang.org/x/time/rate"
    )
    
    var IPLimiter *IPRateLimiter
    
    func NewIPRateLimiter() *IPRateLimiter {
    	return &IPRateLimiter{
    		limiter: make(map[string]*rate.Limiter),
    	}
    }
    
    type IPRateLimiter struct {
    	mu      sync.Mutex
    	limiter 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个请求,桶容量为5
    		i.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()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    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")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试记录

    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. 之后的请求以此类推,需要等待新令牌生成。

    结果输出(符合预期)

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

  • 相关阅读:
    【LeetCode-面试经典150题-day20】
    随机生成指定位数的字母数字组合的账号或密码
    2022派卧底去阿里、京东、美团、滴滴带回来的面试题及答案
    分布式架构的监控与指标
    重塑计算:ICP 打造无限智能合约云解决方案
    Linux性能优化--性能工具:特定进程CPU
    OpenCV读取ESP32CAM的视频流
    【AI】PyTorch入门(六):自动微分torch.autograd
    实现简易负载式均衡在线判题系统
    广州移动中兴B860AV3.1-M2_S905L3安卓9.0线刷包
  • 原文地址:https://blog.csdn.net/trinityleo5/article/details/132889703
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号