go限流、计数器固定窗口算法/计数器滑动窗口算法

go限流、计数器固定窗口算法/计数器滑动窗口算法

一、问题

问题1:后端接口只能支撑每10秒1w个请求,要怎么来保护它呢?
问题2:发短信的接口,不超过100次/时,1000次/24小时,要怎么实现?

二、计数器固定窗口算法

所谓固定窗口,就是只设置了一个时间段,给这个时间段加上一个计数器。 常见的就是统计每秒钟的请求量。 这里就是一个QPS计数器。 在这一秒种内的所有请求,只要给这个计数器递增就可以得到当前的并发量了。 用这个方法也就可以解决前面的问题1。可以直接使用系统的当前UNIX时间戳,精确到秒钟。 这个时间戳作为key,设置一个较短的过期时间,比如:10s。

package mainimport ("fmt""time"
)type SlidingWindow struct {WindowSize        intWindow            []intLastUpdateTime    time.TimeWindowDuration    time.DurationCurrentIndex      intCount             intMaxAllowedRequest int
}func NewSlidingWindow(windowSize int, windowDuration time.Duration, maxRequest int) *SlidingWindow {if windowSize <= 0 {panic("windowSize must be greater than 0")}return &SlidingWindow{WindowSize:        windowSize,Window:            make([]int, windowSize),LastUpdateTime:    time.Now(),WindowDuration:    windowDuration,CurrentIndex:      0,MaxAllowedRequest: maxRequest,}
}func (sw *SlidingWindow) IncrementCount() {now := time.Now()if now.Sub(sw.LastUpdateTime) >= sw.WindowDuration { //比较时间是否超过限定时间sw.ResetWindow()}sw.Count++sw.Window[sw.CurrentIndex] = sw.Count //窗口总量加1if sw.Count > sw.MaxAllowedRequest { //最好用窗口总量去计算。这里为了显示两种效果fmt.Println("Max allowed request exceeded")}sw.CurrentIndex = (sw.CurrentIndex + 1) % sw.WindowSize //窗口往后移动一个位置
}func (sw *SlidingWindow) ResetWindow() {for i := range sw.Window {//将窗口归零sw.Window[i] = 0}sw.Count = 0sw.LastUpdateTime = time.Now() //记录最后一次更新
}func main2() {windowSize := 5 //窗口粒度,问题1的话可以简化掉。windowDuration := time.Second * 10//十秒的访问量maxRequest := 10000//最大访问量slidingWindow := NewSlidingWindow(windowSize, windowDuration, maxRequest)for i := 0; i < 10; i++ {slidingWindow.IncrementCount()time.Sleep(time.Second)}
}

三、计数器滑动窗口算法

固定窗口就一个计数器,而滑动窗口就需要有多个计数器。 具体需要多少个计数器,要看窗口的范围和粒度来决定窗口大小。 比如:时间窗口的范围是24小时,时间窗口的粒度是1小时,那么窗口大小就是24,需要的计数器也就是24个。 我们再来回顾下前面的问题2。 如果我们用上面的固定窗口算法,需要2个计数器,一个是小时的计数器,一个是24小时,也就是天的计数器。 很明显,天的计数器会有很大的误差。 比如:昨天14点前没有任何请求,然后在14点开始,每小时都有100次请求。 到昨天的23点,刚好用完了1000次全天的额度。 但是这时候,还是每小时有100个请求, 那么从昨天的14点到今天10点,总共20小时就会有2000次请求,远远超过了24小时最多1500次的限制。 所以,这里使用滑动窗口替代固定窗口会更加合适。 如果想要限流控制点更加精准,那么就可以把窗口粒度设计的更细。 而代价就是窗口大小增加,需要的存储和计算量都会增加。 所以,这里也是需要对精准度和成本做平衡和选择,难以兼得。
在这里插入图片描述

package mainimport ("fmt""time"
)type RateLimiter struct {perHour     intperDay      inthourWindow  []intdayWindow   []intlastHourIdx intlastDayIdx  int
}func NewRateLimiter(perHour, perDay int) *RateLimiter {return &RateLimiter{perHour:     perHour,perDay:      perDay,hourWindow:  make([]int, 60),dayWindow:   make([]int, 1440),lastHourIdx: 0,lastDayIdx:  0,}
}func (rl *RateLimiter) Allow() bool {now := time.Now()hourIdx := (now.Minute() + now.Hour()*60) % 60  //记录小时的id,dayIdx := now.Hour()*60 + now.Minute()         //记录天的idif rl.hourWindow[hourIdx] >= rl.perHour || rl.dayWindow[dayIdx] >= rl.perDay {return false}rl.hourWindow[hourIdx]++rl.dayWindow[dayIdx]++rl.cleanUpOldEntries(hourIdx, dayIdx)return true
}func (rl *RateLimiter) cleanUpOldEntries(hourIdx, dayIdx int) {if hourIdx != rl.lastHourIdx {//如果小时id更新了需要窗口往右移动rl.hourWindow[hourIdx] = 1 //最新小时id的总量为1rl.hourWindow[rl.lastHourIdx] = 0 //窗口往右移动,上一个归零rl.lastHourIdx = hourIdx //记录最新id}if dayIdx != rl.lastDayIdx {rl.dayWindow[dayIdx] = 1rl.dayWindow[rl.lastDayIdx] = 0rl.lastDayIdx = dayIdx}
}func main3() {limiter := NewRateLimiter(100, 1000)for i := 0; i < 1200; i++ {if limiter.Allow() {fmt.Printf("Request %d allowed\n", i+1)} else {fmt.Printf("Request %d blocked\n", i+1)}time.Sleep(time.Second)}
}

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

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

相关文章

websocket定时推送数据

示例代码 1、添加pom.xml依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId> </dependency> 2、创建websocket配置类 package com.success.socket; import org.springframework.conte…

社交创新的标杆:解读Facebook的社交模式

引言 在当今数字化时代&#xff0c;社交媒体已成为人们日常生活和沟通的重要工具。作为全球最大的社交媒体平台&#xff0c;Facebook不仅改变了我们的社交模式&#xff0c;而且对全球的社交文化、商业活动和公共事务产生了深远的影响。本文将深入探讨Facebook的社交模式&#…

C++算法题 - 矩阵

目录 36. 有效的数独54. 螺旋矩阵48. 旋转图像73. 矩阵置零289. 生命游戏 36. 有效的数独 LeetCode_link 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现…

yolo组件之BottleneckCSP总结

1. 功能解释 Bottleneck CSP&#xff08;Cross Stage Partial&#xff09;是一种常用于计算机视觉任务的卷积神经网络&#xff08;CNN&#xff09;中的瓶颈层类型。它是传统瓶颈层&#xff08;如ResNet中常见的Bottleneck&#xff0c;关于Bottleneck介绍请参考文章yolo组件之Bo…

优斯特:防静电包装解决方案的巧妙运用

在现代电子产品生产与运输领域&#xff0c;防静电包装已成为保障产品安全的必备环节。优斯特凭借其创新的防静电包装解决方案&#xff0c;为客户提供了一种巧妙的方式来确保产品在存储和运输过程中不受静电影响&#xff0c;并且不会被刮花或损坏。 静电对产品的影响 静电对电子…

JAVA_类和对象(1)

认识面向对象 Java是一门纯面向对象的语言(Object Oriented Program, OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面向对象是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情。  面向过程和面相对象并不是一门语言&#xff0c;而是解决…

【项目实战】记录一次PG数据库迁移至GaussDB测试(上)

目录 一、说明 1.1、参考文档 1.2、注意事项 1.3、环境基本情况 二、GaussDB新环境安装 2.1 配置操作环境变量 2.1.1 关闭防火墙 步骤1 执行以下命令&#xff0c;检查防火墙是否关闭。 步骤2 执行以下命令&#xff0c;关闭防火墙并禁止开机启动。 步骤3 修改/etc/sel…

Adobe AE(After Effects)2024下载地址及安装教程

Adobe After Effects是一款专业级别的视觉效果和动态图形处理软件&#xff0c;由Adobe Systems开发。它被广泛用于电影、电视节目、广告和其他多媒体项目的制作。 After Effects提供了强大的合成和特效功能&#xff0c;可以让用户创建出令人惊艳的动态图形和视觉效果。用户可以…

记录Python链接mysql数据的增删改查方法

一、添加方法 db pymysql.connect(hostlocalhost,userroot,password123456,dbpython) cursor db.cursor() sql """insert into EMPLOYEEVALUES(3,张,天爱,35,F,8000) """ try:cursor.execute(sql)db.commit() #提交后&#xff0c;数据才会变 …

Chrome将网页保存为PDF的实战教程

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

欧盟CE认证GPSD指令是什么?符合GPSD指令的产品都有哪些呢?

GPSD指General Product Safety Directive,译为通用产品安全指令。 2006年7月22日欧盟委员会发布第2001/95/EC标准法规GPSD指令的标准清单&#xff0c;由欧洲标准化组织按欧盟委员会指示制定的。 GPSD对产品安全的概念进行了定义&#xff0c;并规定了产品的通用安全要求、符合…

错误分析 (Machine Learning研习十九)

错误分析 您将探索数据准备选项&#xff0c;尝试多个模型&#xff0c;筛选出最佳模型&#xff0c;使用 Grid SearchCV微调其超参数&#xff0c;并尽可能实现自动化。在此&#xff0c;我们假设您已经找到了一个有前途的模型&#xff0c;并希望找到改进它的方法。其中一种方法就…