Go 语言结合 Redis 实现固定窗口、滑动窗口、令牌桶和漏桶限流算法的示例代码

固定窗口算法

  • 原理:将时间划分为固定大小的窗口,在每个窗口内对请求进行计数。如果请求数超过设定的阈值,则拒绝后续请求,直到进入下一个窗口。
  • 代码:
package mainimport ("fmt""time""github.com/go-redis/redis/v8"
)// rdb 是全局的 Redis 客户端实例,用于与 Redis 服务器进行交互
var rdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址和端口Password: "",               // Redis 密码,这里为空表示无密码DB:       0,                // 使用默认的数据库编号
})// 定义固定窗口算法的常量
const (windowSize = 60 // 窗口大小,单位为秒limit      = 100 // 窗口内允许的最大请求数
)// fixedWindowRateLimit 函数实现了固定窗口限流算法
func fixedWindowRateLimit(key string) bool {// 获取当前时间的 Unix 时间戳currentTime := time.Now().Unix()// 计算当前窗口的起始时间windowStart := currentTime - (currentTime % windowSize)// 构建当前窗口对应的 Redis keywindowKey := fmt.Sprintf("%s:%d", key, windowStart)// 对 Redis 中的当前窗口计数器进行自增操作,并获取自增后的计数值count, err := rdb.Incr(rdb.Context(), windowKey).Result()if err != nil {// 如果自增操作出现错误,打印错误信息并返回 falsefmt.Println("Error incrementing counter:", err)return false}// 如果计数值为 1,说明是该窗口的第一个请求,为该窗口设置过期时间if count == 1 {rdb.Expire(rdb.Context(), windowKey, time.Duration(windowSize)*time.Second)}// 如果计数值超过了允许的最大请求数,返回 false 表示请求被限流if count > limit {return false}// 否则,返回 true 表示请求通过return true
}func main() {// 调用 fixedWindowRateLimit 函数进行限流判断if fixedWindowRateLimit("api_request") {fmt.Println("请求通过")} else {fmt.Println("请求被限流")}
}
  • 优缺点:实现简单,但可能会出现临界问题,即在窗口切换的瞬间可能会出现流量高峰。

滑动窗口算法

  • 原理:将固定窗口进一步细分,通过多个小窗口来统计请求数,随着时间的推移,窗口不断滑动,从而更精确地控制流量。
  • 代码:
package mainimport ("fmt""time""github.com/go-redis/redis/v8"
)// rdb 是全局的 Redis 客户端实例,用于与 Redis 服务器进行交互
var rdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址和端口Password: "",               // Redis 密码,这里为空表示无密码DB:       0,                // 使用默认的数据库编号
})// 定义滑动窗口算法的常量
const (windowSize = 60 // 窗口大小,单位为秒limit      = 100 // 窗口内允许的最大请求数
)// slidingWindowRateLimit 函数实现了滑动窗口限流算法
func slidingWindowRateLimit(key string) bool {// 获取当前时间的 Unix 时间戳currentTime := time.Now().Unix()// 移除 Redis 有序集合中时间戳小于当前窗口起始时间的元素rdb.ZRemRangeByScore(rdb.Context(), key, "0", fmt.Sprintf("%d", currentTime-windowSize))// 向 Redis 有序集合中添加当前时间戳,分数为当前时间戳rdb.ZAdd(rdb.Context(), key, &redis.Z{Score:  float64(currentTime),Member: currentTime,})// 获取 Redis 有序集合中当前窗口内的元素数量count, err := rdb.ZCard(rdb.Context(), key).Result()if err != nil {// 如果获取元素数量操作出现错误,打印错误信息并返回 falsefmt.Println("Error getting count:", err)return false}// 如果元素数量超过了允许的最大请求数,返回 false 表示请求被限流if count > limit {return false}// 否则,返回 true 表示请求通过return true
}func main() {// 调用 slidingWindowRateLimit 函数进行限流判断if slidingWindowRateLimit("api_request") {fmt.Println("请求通过")} else {fmt.Println("请求被限流")}
}
  • 优缺点:比固定窗口算法更精确地控制流量,但实现相对复杂,且需要更多的 Redis 操作。

令牌桶算法

  • 原理:系统以固定的速率向令牌桶中添加令牌,每个请求需要从令牌桶中获取一个或多个令牌才能被处理。如果令牌桶中没有足够的令牌,则请求被拒绝。
  • 代码:
package mainimport ("fmt""time""github.com/go-redis/redis/v8"
)// rdb 是全局的 Redis 客户端实例,用于与 Redis 服务器进行交互
var rdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址和端口Password: "",               // Redis 密码,这里为空表示无密码DB:       0,                // 使用默认的数据库编号
})// 定义令牌桶算法的常量
const (capacity        = 100        // 令牌桶的容量rate            = 1          // 令牌生成速率,每秒生成 1 个令牌requiredTokens  = 1          // 每个请求所需的令牌数
)// tokenBucketScript 是用于执行令牌桶算法的 Lua 脚本
var tokenBucketScript = `
-- 获取传入的令牌桶 key
local tokens_key = KEYS[1]
-- 构建存储上次更新时间的 key
local last_time_key = tokens_key .. ":last_time"
-- 获取令牌桶容量
local capacity = tonumber(ARGV[2])
-- 获取令牌生成速率
local rate = tonumber(ARGV[3])
-- 获取当前时间
local now = tonumber(ARGV[1])
-- 获取每个请求所需的令牌数
local required_tokens = tonumber(ARGV[4])-- 从 Redis 中获取上次更新时间
local last_time = tonumber(redis.call('get', last_time_key))
-- 从 Redis 中获取当前令牌数
local tokens = tonumber(redis.call('get', tokens_key))-- 如果上次更新时间为空,说明是首次请求,初始化相关值
if last_time == nil thenlast_time = nowtokens = capacity
end-- 计算从上次更新到现在生成的令牌数
local generated_tokens = (now - last_time) * rate
-- 更新令牌数,确保不超过令牌桶容量
tokens = math.min(capacity, tokens + generated_tokens)-- 如果令牌数小于请求所需的令牌数,说明令牌不足,拒绝请求
if tokens < required_tokens thenredis.call('set', last_time_key, last_time)redis.call('set', tokens_key, tokens)return 0
else-- 否则,处理请求,扣除相应的令牌数tokens = tokens - required_tokensredis.call('set', last_time_key, now)redis.call('set', tokens_key, tokens)return 1
end
`// tokenBucketRateLimit 函数调用 Lua 脚本来实现令牌桶限流算法
func tokenBucketRateLimit(key string) bool {// 获取当前时间的 Unix 时间戳currentTime := time.Now().Unix()// 执行 Lua 脚本,并获取执行结果result, err := rdb.Eval(rdb.Context(), tokenBucketScript, []string{key}, currentTime, capacity, rate, requiredTokens).Int64()if err != nil {// 如果执行脚本出现错误,打印错误信息并返回 falsefmt.Println("Error executing script:", err)return false}// 如果结果为 1,表示请求通过;否则,表示请求被限流return result == 1
}func main() {// 调用 tokenBucketRateLimit 函数进行限流判断if tokenBucketRateLimit("api_request") {fmt.Println("请求通过")} else {fmt.Println("请求被限流")}
}
  • 优缺点:能够平滑地控制流量,允许一定程度的突发流量,但实现相对复杂。

漏桶限流算法

  • 原理:请求就像水一样注入漏桶,漏桶以固定的速率处理请求。如果请求的注入速率超过漏桶的处理速率,多余的请求将被丢弃。
  • 代码:
package mainimport ("fmt""time""github.com/go-redis/redis/v8"
)// rdb 是全局的 Redis 客户端实例,用于与 Redis 服务器进行交互
var rdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址和端口Password: "",               // Redis 密码,这里为空表示无密码DB:       0,                // 使用默认的数据库编号
})// 定义漏桶算法的常量
const (capacity     = 100        // 漏桶的容量rate         = 1          // 漏桶的流出速率,每秒流出 1 个单位requestSize  = 1          // 每个请求的大小
)// leakyBucketScript 是用于执行漏桶算法的 Lua 脚本
var leakyBucketScript = `
-- 获取传入的漏桶 key
local bucket_key = KEYS[1]
-- 构建存储上次更新时间的 key
local last_time_key = bucket_key .. ":last_time"
-- 获取漏桶容量
local capacity = tonumber(ARGV[2])
-- 获取漏桶流出速率
local rate = tonumber(ARGV[3])
-- 获取当前时间
local now = tonumber(ARGV[1])
-- 获取每个请求的大小
local request_size = tonumber(ARGV[4])-- 从 Redis 中获取上次更新时间
local last_time = tonumber(redis.call('get', last_time_key))
-- 从 Redis 中获取当前漏桶中的水量
local water = tonumber(redis.call('get', bucket_key))-- 如果上次更新时间为空,说明是首次请求,初始化相关值
if last_time == nil thenlast_time = nowwater = 0
end-- 计算从上次更新到现在流出的水量
local outflow = (now - last_time) * rate
-- 更新漏桶中的水量,确保不小于 0
water = math.max(0, water - outflow)-- 如果漏桶中的水量加上当前请求的大小超过了漏桶容量,说明漏桶已满,拒绝请求
if water + request_size > capacity thenredis.call('set', last_time_key, last_time)redis.call('set', bucket_key, water)return 0
else-- 否则,处理请求,增加漏桶中的水量water = water + request_sizeredis.call('set', last_time_key, now)redis.call('set', bucket_key, water)return 1
end
`// leakyBucketRateLimit 函数调用 Lua 脚本来实现漏桶限流算法
func leakyBucketRateLimit(key string) bool {// 获取当前时间的 Unix 时间戳currentTime := time.Now().Unix()// 执行 Lua 脚本,并获取执行结果result, err := rdb.Eval(rdb.Context(), leakyBucketScript, []string{key}, currentTime, capacity, rate, requestSize).Int64()if err != nil {// 如果执行脚本出现错误,打印错误信息并返回 falsefmt.Println("Error executing script:", err)return false}// 如果结果为 1,表示请求通过;否则,表示请求被限流return result == 1
}func main() {// 调用 leakyBucketRateLimit 函数进行限流判断if leakyBucketRateLimit("api_request") {fmt.Println("请求通过")} else {fmt.Println("请求被限流")}
}
  • 优缺点:可以平滑地处理请求,保证请求以固定的速率被处理,但无法应对突发流量。

总结

算法实现复杂度限流精度突发流量处理适用场景
固定窗口算法简单流量平稳、精度要求低的场景
滑动窗口算法较复杂一般流量波动大、精度要求高的场景
令牌桶算法较复杂可容忍突发流量、需平滑限流的场景
漏桶算法较复杂对流量稳定性要求极高的场景

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

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

相关文章

linux之perf(17)PMU事件采集脚本

Linux之perf(17)PMU事件采集脚本 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Perf性能分析_Once_day的博…

兰州百合基因组(36.68 Gb)-文献精读113

The evolutionary tale of lilies: Giant genomes derived from transposon insertions and polyploidization 百合的进化故事&#xff1a;由转座子插入和多倍化导致的巨型基因组 百合&#xff08;Lilium spp.&#xff09;&#xff0c;被誉为“球根花卉之王”&#xff0c;因其…

macos sequoia 禁用 ctrl+enter 打开鼠标右键菜单功能

macos sequoia默认ctrlenter会打开鼠标右键菜单&#xff0c;使得很多软件有冲突。关闭方法&#xff1a; end

UE5.3 C++ TArray系列(一)

一.TArray概述 它们就相当于C动态数组Vector&#xff0c;但是被UE封装了&#xff0c;懂得都懂反射嘛&#xff0c;要不一不小心就被回收了。 它真的非常常见&#xff0c;我所用的容器中&#xff0c;它绝对排名第一&#xff0c;第二是TMap。 同类好理解&#xff0c;我平时也常用…

Docker+Dify部署DeepSeek-r1本地知识库

安装配置Docker Desktop 软件下载 Docker Desktop版本:4.38.0.181591 Docker Desktop下载地址:Docker: Accelerated Container Application Development 或者从这里下载:DockerDesktop-4.38.0.181591资源-CSDN文库 点击图下所示位置,下载windows-AMD64版本软件 启用Hy…

MySQL数据库——表的约束

1.空属性&#xff08;null/not null&#xff09; 两个值&#xff1a;null&#xff08;默认的&#xff09;和not null&#xff08;不为空&#xff09; 数据库默认字段基本都是字段为空&#xff0c;但是实际开发时&#xff0c;尽可能保证字段不为空&#xff0c;因为数据为空没办法…

腿足机器人之十一- 深度强化学习

腿足机器人之十一- 深度强化学习 机器人能力腿足机器人RL问题建模强化学习解决方案 强化学习算法库选择建议 深度学习技术已经在语音、图像、视频、文本等领域应用广泛&#xff0c;其和强化学习的结合使得基于深度学习的大模型能力更是上升一个台阶。因而用在腿足机器人的运动中…

如何教计算机识别视频中的人类动作

作者简介: 高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 =============…

Redisson分布式锁java语法, 可重入性实现原理 ,(还有可重试性,超时不释放,主从一致性)

Redisson在java的使用方法 Redisson分布式锁不可重入的实现原理 设置一个HSET key为锁的名字&#xff0c;field为当前获取锁的线程名字&#xff0c;value为可重入锁的当前已经重入次数 追踪源码发现RedissonClient类的tryLock就是用lua脚本和上图逻辑实现的加锁解锁&#xf…

YOLOv8与DAttention机制的融合:复杂场景下目标检测性能的增强

文章目录 1. YOLOv8简介2. DAttention (DAT)注意力机制概述2.1 DAttention机制的工作原理 3. YOLOv8与DAttention (DAT)的结合3.1 引入DAT的动机3.2 集成方法3.3 代码实现 4. 实验与结果分析4.1 实验设置4.2 结果分析推理速度性能对比 5. 深度分析&#xff1a;DAttention在YOLO…

MAC快速本地部署Deepseek (win也可以)

MAC快速本地部署Deepseek (win也可以) 下载安装ollama 地址: https://ollama.com/ Ollama 是一个开源的大型语言模型&#xff08;LLM&#xff09;本地运行框架&#xff0c;旨在简化大模型的部署和管理流程&#xff0c;使开发者、研究人员及爱好者能够高效地在本地环境中实验和…

springboot+dubbo+zookeeper的注册服务和调用实践

目录 zookeeper为什么可作为注册中心zookeeper注册中心优缺点启动zookeeper编写springboot项目提供dubbo服务1. 服务接口2. Springboot引入dubbo实现服务接口2.1 工程目录和依赖2.2 启动程序和application.properties2.3 DubboService 实现服务接口2.4 测试api&#xff0c;用于…

GTSAM 库详细介绍与使用指南

GTSAM 库详细介绍与使用指南 一、GTSAM 概述 GTSAM&#xff08;Georgia Tech Smoothing and Mapping&#xff09;是由佐治亚理工学院开发的 C 开源库&#xff0c;专注于 概率图模型&#xff08;尤其是因子图&#xff09;的构建与优化&#xff0c;广泛应用于机器人定位与建图&a…

Missing required prop: “maxlength“

背景&#xff1a; 封装一个使用功能相同使用频率较高的input公共组件作为子组件&#xff0c;大多数长度要求为200&#xff0c;且实时显示统计子数&#xff0c;部分input有输入提示。 代码实现如下&#xff1a; <template><el-input v-model"inputValue" t…

基于YOLOv8的人脸识别系统

文章目录 一.前言 二.原理阐述 三.源代码 四.代码改进 五.流程概述 一.前言 原开源项目是对于某时段校园门口学生出入的视频,使用YOLOv8目标检测算法以及yolov8l-face模型将目标换算成只检测人脸, 通过该模型中的track技术实现检测出的人脸进行自动跟踪, 通过跟踪到的…

✨ 索引有哪些缺点以及具体有哪些索引类型

索引的定义与原理 索引是数据库中用于提高数据检索效率的数据结构。它就像是书籍的目录&#xff0c;通过目录可以快速定位到所需内容的页码&#xff0c;而在数据库中&#xff0c;索引可以帮助数据库系统快速找到符合查询条件的数据行&#xff0c;而不必对整个表进行扫描。 其…

TCP...

什么是TCP&#xff1f; TCP是面向连接的、可靠的、基于字节流的传输层通信协议 面向连接&#xff1a;一定是「一对一」才能连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息&#xff0c;也就是一对多&#xff0c;是无法做到的&#xff1b;字节流&#xff…

工业通信协议 EtherNet/IP 全面解析

工业通信协议 EtherNet/IP 全面解析 EtherNet/IP&#xff08;以太网工业协议&#xff09;是一种基于标准以太网的工业自动化通信协议&#xff0c;由 ODVA&#xff08;开放设备网供应商协会&#xff09; 管理。它融合了 CIP&#xff08;通用工业协议&#xff09; 和以太网技术&…

react+typescript,初始化与项目配置

1&#xff0c;创建项目 npx create-react-app music --template typescript 2&#xff0c;配置项目别名 npm install craco/cracoalpha -D 1&#xff0c;最外层与src平级创建 craco.config.jsconst path require(path)const resolve (dir) > path.resolve(__dirname, d…

JAVA Kotlin Androd 使用String.format()格式化日期

在以前的开发中&#xff0c;日期格式化一直使用的是SimpleDateFormat进行格式化。今天发现String.format也可以格式化。当 然&#xff0c;两种方式的优劣没有进行深入分析。 val date Date()//月&#xff0c;日&#xff0c;星期&#xff0c;AM/PM//Fue 1 (Sat) pmval fullDate…