时间:2021-05-22
前言
在学习过程中发现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个任务,速度上确实是...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文实例为大家分享了php+redis消息队列实现抢购的具体代码,供大家参考,具体内容如下实现功能:1.基于redis队列,防止高并发的超卖2.基于mysql的
1、利用延迟队列延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取
本文实例讲述了php使用redis的有序集合zset实现延迟队列。分享给大家供大家参考,具体如下:延迟队列就是个带延迟功能的消息队列,相对于普通队列,它可以在指
本文介绍了Java利用Redis实现消息队列的示例代码,分享给大家,具体如下:应用场景为什么要用redis?二进制存储、java序列化传输、IO连接数高
本文实例讲述了Java延迟队列原理与用法。分享给大家供大家参考,具体如下:延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任