当前位置: 首页 > backend >正文

golang 实现基于redis的并行流量控制(计数锁)

在业务开发中,有时需要对某个操作在整个集群中限制并发度,例如限制大模型对话的并行数。基于redis zset实现计数锁,做个笔记。

关键词:并行流量控制、计数锁

package redisutilimport ("context""fmt""math""time""github.com/go-redis/redis/v9"
)// AcquireZSetLock 借助redis zset数据结构实现分布式计数锁。可用于计数任务运行数,防止超限。返回值:zset大小、释放锁的函数、错误信息
func AcquireZSetLock(ctx context.Context, c redis.Client, key string, element string, zsetMaxSize int,expiresIn time.Duration, syncWait time.Duration) (int, func() error, error) {ctx, cancel := context.WithTimeout(ctx, syncWait)defer cancel()for i := 0; ; i++ {select {case <-ctx.Done(): // 接到取消信号,按插入失败处理return -1, func() error { return nil }, ctx.Err()default:}size, err := insertElementToZsetLock(ctx, c, key, element, zsetMaxSize, expiresIn)if err != nil {second := 0.4 + 0.6*math.Exp(-0.17*float64(i)) // f(i=0) = 1.0; f(i=10) = 0.5096,即第10次就会衰减到0.5096秒second = max(second, 0.5)                      // 最小间隔0.5秒,防止过于频繁的请求time.Sleep(time.Duration(second*1000) * time.Millisecond)}releaseFunc := func() error {result, err := c.ZRem(context.Background(), key, element).Result()if err != nil {return fmt.Errorf("redis zrem error: %v. return=%d", err, result)}return nil}return size, releaseFunc, nil}
}// insertElementToZsetLock 插入元素到zset,并删除已过期的元素
func insertElementToZsetLock(ctx context.Context, c redis.Client, key string, element string, zsetMaxSize int, expiresIn time.Duration) (int, error) {luaScript := `local zsetName = KEYS[1]local memberName = ARGV[1]local currentTime = tonumber(ARGV[2])local deadTime = tonumber(ARGV[3])local sizeLimit = tonumber(ARGV[4])-- 删除已过期的元素redis.call("ZREMRANGEBYSCORE", zsetName, "-inf", currentTime)-- 获取集合的大小local setSize = redis.call('ZCard', zsetName)-- 如果集合大小小于限制值,则添加元素,并返回集合大小if setSize < sizeLimit thenredis.call('ZAdd', zsetName, deadTime, memberName)local expireTime = deadTime - currentTimeif expireTime > 0 thenredis.call('EXPIRE', zsetName, expireTime)endreturn setSize+1endreturn -1`currentTime := time.Now().Unix()deadTime := time.Now().Add(expiresIn).Unix() // 过期时间 Unix秒ret, err := c.Do(ctx, "EVAL", luaScript, 1, key, element, currentTime, deadTime, zsetMaxSize).Result()if err != nil {return -1, err}if ret.(int64) < 0 {return zsetMaxSize, fmt.Errorf("zset size reach max size: %d", zsetMaxSize)}return int(ret.(int64)), nil
}

使用示例:

size, release, err := AcquireZSetLock(ctx, client, key, element, 10, 10*time.Second, 3*time.Second)
defer release()
if err != nil {fmt.Println(err)
}
http://www.xdnf.cn/news/10026.html

相关文章:

  • Joern项目第三方库依赖分析
  • 系统架构设计综合知识与案例分析
  • 深入 RAG(检索增强生成)系统架构:如何构建一个能查资料的大语言模型系统
  • MCU STM32搭配存储SD NAND(贴片式T卡)于智能皮电手环(Galvanic Skin Response, GSR 手环)的全方位评测
  • 硬件工程师笔记——运算放大电路Multisim电路仿真实验汇总
  • 三格电子——如何解决消防设备联网问题
  • [JVM] JVM内存调优
  • 黑河流域30弧秒分辨率月尺度地表水及地下水灌溉量数据集(1981-2013)
  • Redis Sorted Set 深度解析:从原理到实战应用
  • GitLens 教学(学习更新中)
  • (一)微服务(垂直API)
  • SpringBoot+vue+SSE+Nginx实现消息实时推送
  • 0-EATSA-GNN:基于图节点分类师生机制的边缘感知和两阶段注意力增强图神经网络(code)
  • grounded_sam2 使用踩坑笔记
  • gbase8s数据库+mybatis问题记录
  • 【JUC】深入解析 JUC 并发编程:单例模式、懒汉模式、饿汉模式、及懒汉模式线程安全问题解析和使用 volatile 解决内存可见性问题与指令重排序问题
  • Java处理动态的属性:字段不固定、需要动态扩展的 JSON 数据结构
  • 爬虫--以爬取小说为例
  • android协程异步编程常用方法
  • B端产品经理如何快速完成产品原型设计
  • 【仿生机器人】机器人情绪系统的深度解析
  • 晨控CK-UR12与西门子PLC配置Modbus TCP通讯连接操作手册
  • Redis 插入中文乱码键
  • Centos7安装gitlab
  • Vehicle HAL(1)--整体介绍
  • InnoDB中的锁
  • 龙虎榜——20250529
  • 2025年业财一体化如何重塑工程项目管理?
  • 下载jdk教程
  • 基于python 将图像上同一行距离相近的矩形框融合