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

 更新时间:2019年11月25日 16:08:33   作者:菜菜jjj  
这篇文章主要介绍了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个任务,速度上确实是...

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

相关文章

  • 在 Golang 中使用 Cobra 创建 CLI 应用

    在 Golang 中使用 Cobra 创建 CLI 应用

    这篇文章主要介绍了在 Golang 中使用 Cobra 创建 CLI 应用,来看下 Cobra 的使用,这里我们使用的 go1.13.3 版本,使用 Go Modules 来进行包管理,需要的朋友可以参考下
    2022-01-01
  • 浅谈Go语言中字符串和数组

    浅谈Go语言中字符串和数组

    这篇文章主要简单介绍了Go语言中字符串和数组的使用方法和申明方式,需要的朋友可以参考下
    2015-01-01
  • 基于Golang开发一个轻量级登录库/框架

    基于Golang开发一个轻量级登录库/框架

    几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西。所以本文就来用Golang开发一个轻量级登录库/框架吧
    2023-05-05
  • 解决 Golang VS Code 插件下载安装失败的问题

    解决 Golang VS Code 插件下载安装失败的问题

    这篇文章主要介绍了解决 Golang VS Code 插件下载安装失败,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 解决go echo后端处理跨域的两种操作方式

    解决go echo后端处理跨域的两种操作方式

    这篇文章主要介绍了解决go echo后端处理跨域的两种操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 详解Go语言的context包从放弃到入门

    详解Go语言的context包从放弃到入门

    这篇文章主要介绍了Go语言的context包从放弃到入门,本文通过实例演示给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Golang如何快速构建一个CLI小工具详解

    Golang如何快速构建一个CLI小工具详解

    这篇文章主要为大家介绍了Golang如何快速构建一个CLI小工具详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 一个简单的Golang实现的HTTP Proxy方法

    一个简单的Golang实现的HTTP Proxy方法

    今天小编就为大家分享一篇一个简单的Golang实现的HTTP Proxy方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • 一文带你深入了解Golang中的Mutex

    一文带你深入了解Golang中的Mutex

    这篇文章主要为大家详细介绍了Golang中Mutex的相关知识,知其然,更要知其所以然。文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-03-03
  • Go语言Gin框架前后端分离项目开发实例

    Go语言Gin框架前后端分离项目开发实例

    本文主要介绍了Go语言Gin框架前后端分离项目开发工程化实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11

最新评论