当前位置: 首页 > 编程笔记 >

golang实现redis的延时消息队列功能示例

易书
2023-03-14
本文向大家介绍golang实现redis的延时消息队列功能示例,包括了golang实现redis的延时消息队列功能示例的使用技巧和注意事项,需要的朋友参考一下

前言

在学习过程中发现redis的zset还可以用来实现轻量级的延时消息队列功能,虽然可靠性还有待提高,但是对于一些对数据可靠性要求不那么高的功能要求完全可以实现。本次主要采用了redis中zset中的zadd, zrangebyscore 和 zdel来实现一个小demo。

提前准备 安装redis, redis-go

因为用的是macOS, 直接

$ brew install redis
$ go get github.com/garyburd/redigo/redis

又因为比较懒,生成任务的唯一id时,直接采用了bson中的objectId,所以:

$ go get gopkg.in/mgo.v2/bson

唯一id不是必须有,但如果之后有实际应用需要携带,便于查找相应任务。

生产者

通过一个for循环生成10w个任务, 每一个任务有不同的时间

func producer() {
 count := 0
 //生成100000个任务
 for count < 100000 {
 count++
 dealTime := int64(rand.Intn(5)) + time.Now().Unix()
 uuid := bson.NewObjectId().Hex()
 redis.Client.AddJob(&job.JobMessage{
 Id: uuid,
 DealTime: dealTime,
 }, + int64(dealTime))
 }
}

其中AddJob函数在另一个包中, 将上一个函数中随机生成的时间作为需要处理的时间戳.

// 添加任务
func (client *RedisClient) AddJob(msg *job.JobMessage, dealTime int64) {
 conn := client.Get()
 defer conn.Close()

 key := "JOB_MESSAGE_QUEUE"
 conn.Do("zadd", key, dealTime, util.JsonEncode(msg))
}

消费者

消费者处理流程分为两个步骤:

  • 获取小于等于当前时间戳的任务
  • 通过删除当前任务来判断谁获得了当前任务

因为在获取小于等于当前时间戳的任务时,可能有多个go routine同时读到了当前任务,而只有一个任务可以来处理当前任务。因此我们需要通过一个方案来判断究竟由谁来处理这个任务(当然如果只有一个消费者可以读到就直接处理):这个时候可以通过redis的删除操作来获取,因为删除指定value时只有成功的操作才会返回不为0,所以我们可以认为删除当前队列成功的那个go routine拿到了当前的任务。

下面是代码:

// 消费者
func consumer() {
 // 启动10个go routine一起去拿
 count := 0
 for count < 10 {
 go func() {
 for {
 jobs := redis.Client.GetJob()
 if len(jobs) <= 0 {
  time.Sleep(time.Second * 1)
  continue
 }
 currentJob := jobs[0]
 // 如果当前抢redis队列成功,
 if redis.Client.DelJob(currentJob) > 0 {
  var jobMessage job.JobMessage
  util.JsonDecode(currentJob, &jobMessage) //自定义的json解析函数
  handleMessage(&jobMessage)
 }

 }

 }()
 count++
 }
}

// 处理任务用函数
func handleMessage(msg *job.JobMessage) {
 fmt.Printf("deal job: %s, require time: %d \n", msg.Id, msg.DealTime)
 go func() {
 countChan <- true
 }()
}

redis部分的代码,获取任务和删除任务

// 获取任务
func (client *RedisClient) GetJob() []string {
 conn := client.Get()
 defer conn.Close()

 key := "JOB_MESSAGE_QUEUE"
 timeNow := time.Now().Unix()
 ret, err := redis.Strings(conn.Do("zrangebyscore", key, 0, timeNow, "limit", 0, 1))
 if err != nil {
 panic(err)
 }
 return ret
}

// 删除当前任务, 用来判断是否抢到了当前任务
func (client *RedisClient) DelJob(value string) int {
 conn := client.Get()
 defer conn.Close()

 key := "JOB_MESSAGE_QUEUE"
 ret, err := redis.Int(conn.Do("zrem", key, value))
 if err != nil {
 panic(err)
 }
 return ret
}

代码大抵如此。最后跑起来之后,大概每3-4秒钟能够处理掉1w个任务,速度上确实是...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 主要内容:什么是Stream?,常用命令汇总,基本命令应用,创建消息ID,创建消费组,消费消息Redis Stream 是 Redis 5.0 版本引入的一种新数据类型,同时它也是 Redis 中最为复杂的数据结构,本节主要对 Stream 做相关介绍。 什么是Stream? Stream 实际上是一个具有消息发布/订阅功能的组件,也就常说的消息队列。其实这种类似于 broker/consumer(生产者/消费者)的数据结构很常见,比如 RabbitMQ 消息中间件、Celery 消息中间

  • 本文向大家介绍RabbitMQ 怎么实现延迟消息队列?相关面试题,主要包含被问及RabbitMQ 怎么实现延迟消息队列?时的应答技巧和注意事项,需要的朋友参考一下 延迟队列的实现有两种方式: 通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能; 使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。

  • 问题内容: 我的总体问题是: 使用Redis for PubSub,当发布者将消息推送到频道中的速度比订阅者能够阅读它们的速度快时,消息会如何处理? 例如,假设我有: 一个简单的发布者以2 msg / sec的速度发布消息。 一个简单的订户以1 msg / sec的速率读取消息。 我天真的假设是订户只会看到发布到Redis上的消息的50%。为了验证这一理论,我编写了两个脚本: pub.py 子py

  • 本文向大家介绍C#调用RabbitMQ实现消息队列的示例代码,包括了C#调用RabbitMQ实现消息队列的示例代码的使用技巧和注意事项,需要的朋友参考一下 前言 我在刚接触使用中间件的时候,发现,中间件的使用并不是最难的,反而是中间件的下载,安装,配置才是最难的。 所以,这篇文章我们从头开始学习RabbitMq,真正的从头开始。 关于消息队列 其实消息队列没有那么神秘,我们这样想一下,用户访问网站

  • 主要内容:Stream 概述,2 Stream基本结构,3 存储数据,3.1 Entry ID,3.2 数量限制,4 获取数据,4.1 范围查询,4.2 独立消费消息,4.3 消费者组,5 永久故障恢复,5.1 XPENDING查看未处理消息,5.2 XCLAIM转移消息,5.3 XAUTOCLAIM自动转移,6 死信队列,7 Stream监控,8 删除消息,9 零长度Stream,10 ACK确认,11 总结详细介绍了 Redis 5.0 版本新增加的数据结构Stream的使用方式以及原理,如

  • 本文向大家介绍php使用redis的有序集合zset实现延迟队列应用示例,包括了php使用redis的有序集合zset实现延迟队列应用示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了php使用redis的有序集合zset实现延迟队列。分享给大家供大家参考,具体如下: 延迟队列就是个带延迟功能的消息队列,相对于普通队列,它可以在指定时间消费掉消息。 延迟队列的应用场景: 1、新用户注册,

  • 可靠消费 Redis:没有相应的机制保证消息的消费,当消费者消费失败的时候,消息体丢失,需要手动处理 RabbitMQ:具有消息消费确认,即使消费者消费失败,也会自动使消息体返回原队列,同时可全程持久化,保证消息体被正确消费 可靠发布 Reids:不提供,需自行实现 Redis的消息队列,如果在从队列pop出去的时候,worker处理失败的话,数据不会回到队列中,需要从业务中手动把失败的处理数据p

  • 为什么已经拥有了共享内存时需要消息队列呢? 这将是多种原因,让我们将其分解为多个点来简化 - 据了解,一旦消息被一个进程接收到,它将不再可用于任何其他进程。 而在共享内存中,数据可供多个进程访问。 如果想使用小信息格式进行通信。 当多个进程同时进行通信时,共享内存数据需要同步保护。 使用共享内存的写入和读取频率很高,那么实现功能将会非常复杂。 在这种情况下不值得使用。 如果所有的进程不需要访问共享