玩转redis-延时消息队列
上一篇基于redis
的list实现了一个简单的消息队列:玩转redis-简单消息队列
产品经理经常说的一句话,我们不光要有X
功能,还要Y
功能,这样客户才能更满意。同样的,只有简单消息队列是不够的,还要有延时消息队列
才能算是一个完整的消息队列。
看看redis
的命令,放眼望去,的有序集合(sorted set)就是一个很好用的命令,完全可以用他做一个延时消息队列
redis有序集合(sorted set)
redis
有序集合,每个元素都会关联一个double
类型的分数。redis
正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score
)却可以重复。
简单操作
添加数据
127.0.0.1:6379> ZADD testSet1 5 a
(integer) 1
127.0.0.1:6379> ZADD testSet1 1 b 8 c 7 d
(integer) 3
读取
127.0.0.1:6379> ZRANGEBYSCORE testSet1 0 3
1) "b"
127.0.0.1:6379> ZRANGEBYSCORE testSet1 0 5
1) "b"
2) "a"
也可以把score
打出来
127.0.0.1:6379> ZRANGEBYSCORE testSet1 -inf 5 WITHSCORES
1) "b"
2) "1"
3) "a"
4) "5"
查出所有的数据
127.0.0.1:6379> ZRANGEBYSCORE testSet1 -inf inf
1) "b"
2) "a"
3) "d"
4) "c"
删除数据
ZREMRANGEBYSCORE testSet1 0 2
延时队列的实现思路
总体的思路很简单,就是每一个value
的score
保存的是时间,也就是说,在添加一个元素时他的score
是当前时间+延时的时间。轮循获取数据时,查找小于或等于当前时间的数据项,就是具体的延时消息。
还有一个问题,就是
ZRANGEBYSCORE
和list
的pop
不同,pop
是取出元素并且会把元素在list
中删除。ZRANGEBYSCORE
只会取出数据不会把数据从sorted set
中删除。解决方法1,利用redis
的事务
,先ZRANGEBYSCORE
取出数据,然后再用ZREMRANGEBYSCORE
把数据删除。
具体实现-code
添加延时消息,参数delay
就是我们要延时多久:
func (p *Producer) PublishDelayMsg(topicName string, body []byte, delay time.Duration) error {
if delay <= 0 {
return errors.New("delay need great than zero")
}
tm := time.Now().Add(delay)
msg := NewMessage("", body)
msg.DelayTime = tm.Unix()
sendData, _ := json.Marshal(msg)
return p.redisCmd.ZAdd(topicName+zsetSuffix, redis.Z{Score: float64(tm.Unix()), Member: string(sendData)}).Err()
}
使用,比如我们想过1秒再处理
producer.PublishDelayMsg(topicName, body, time.Second)
读取消息并处理
这就比较简单了,就是在一个ticker
里循环读取小于或等于当前时间的数据:
func (s *consumer) startGetDelayMessage() {
go func() {
ticker := time.NewTicker(s.options.RateLimitPeriod)
defer func() {
log.Println("stop get delay message.")
ticker.Stop()
}()
topicName := s.topicName + zsetSuffix
for {
currentTime := time.Now().Unix()
select {
case <-s.ctx.Done():
log.Printf("context Done msg: %#v \n", s.ctx.Err())
return
case <-ticker.C:
var valuesCmd *redis.ZSliceCmd
_, err := s.redisCmd.TxPipelined(func(pip redis.Pipeliner) error {
valuesCmd = pip.ZRangeWithScores(topicName, 0, currentTime)
pip.ZRemRangeByScore(topicName, "0", strconv.FormatInt(currentTime, 10))
return nil
})
if err != nil {
log.Printf("zset pip error: %#v \n", err)
continue
}
rev := valuesCmd.Val()
for _, revBody := range rev {
msg := &Message{}
json.Unmarshal([]byte(revBody.Member.(string)), msg)
if s.handler != nil {
s.handler.HandleMessage(msg)
}
}
}
}
}()
}
玩转redis-延时消息队列的更多相关文章
- Redis 做消息队列
一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现.定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...
- Redis作为消息队列服务场景应用案例
NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例 一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更 ...
- redis resque消息队列
Resque 目前正在学习使用resque .resque-scheduler来发布异步任务和定时任务,为了方便以后查阅,所以记录一下. resque和resque-scheduler其优点在于功能比 ...
- 【springboot】【redis】springboot+redis实现发布订阅功能,实现redis的消息队列的功能
springboot+redis实现发布订阅功能,实现redis的消息队列的功能 参考:https://www.cnblogs.com/cx987514451/p/9529611.html 思考一个问 ...
- 【Redis】php+redis实现消息队列
在项目中使用消息队列一般是有如下几个原因: 把瞬间服务器的请求处理换成异步处理,缓解服务器的压力 实现数据顺序排列获取 redis实现消息队列步骤如下: 1).redis函数rpush,lpop 2) ...
- spring boot Rabbitmq集成,延时消息队列实现
本篇主要记录Spring boot 集成Rabbitmq,分为两部分, 第一部分为创建普通消息队列, 第二部分为延时消息队列实现: spring boot提供对mq消息队列支持amqp相关包,引入即可 ...
- Lumen开发:结合Redis实现消息队列(1)
1.简介 Lumen队列服务为各种不同的后台队列提供了统一的API.队列允许你推迟耗时任务(例如发送邮件)的执行,从而大幅提高web请求速度. 1.1 配置 .env文件的QUEUE_DRIVER选项 ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- sping+redis实现消息队列的乱码问题
使用spring支持redis实现消息队列,参考官方样例:https://spring.io/guides/gs/messaging-redis/ 实现后在运行过程中发现消费者在接收消息时会出现乱码的 ...
随机推荐
- springboot项目中接口入参的简单校验
.katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...
- frida(hook工具)的环境搭建
一.简介 frida 是一款基于 python+javascript 的 hook 框架,可运行在 android.ios.linux.win等各个平台,主要使用的动态二进制插桩技术. Frida官网 ...
- celery订单定时回滚
目录 订单回滚 控制执行(多少时间后执行) celery异步定时任务 订单回滚 用celery异步,定时任务.可以设置:如果下单15分钟后没有支付,则取消订单.做反向操作 控制执行(多少时间后执行) ...
- 聊一聊React中虚拟DOM
1. 什么是虚拟 DOM 在 React 中实际上是 render 函数中return 的内容会生成 DOM,return 中的内容由两部分组成,一部分是 JSX ,另一部分就是 state 中的数据 ...
- 谈谈SQL优化
写SQL是学习数据库必须掌握的非常重要的技能之一.在学习过程当中,我们会尝试写各种各样满足需求的SQL语句.在实际项目中,优秀的SQL语句和普通的SQL语句的执行速度差别非常大.对于一个数据量很大的系 ...
- oracle中plsql练习题-----编写一个PL/SQL块,输出所有员工的员工姓名、员工号、工资和部门号
一.思路:首先输出需要变量接收,需要声明变量,于是考虑什么变量类型比较合适,在这我用的是table类型,最后,查询出来,循环输出即可. 二.具体实现 -- 编写一个PL/SQL块,输出所有员工的员工姓 ...
- Core3.1WebApi_ 同源策略_如何支持跨域(转载)
原文:https://mp.weixin.qq.com/s/id3fOyGrZI9lLx7PKbVYlg
- 大数据软件安装之Azkaban(任务调度)
一.安装部署 1.安装前准备 1)下载地址:http://azkaban.github.io/downloads.html 2)将Azkaban Web服务器.Azkaban执行服务器.Azkaban ...
- .NET Core技术研究-HttpContext访问的正确姿势
将ASP.NET升级到ASP.NET Core之后,相信大家都会遇到HttpContext.Current无法使用的问题.这也是我们迁移ASP.NET Core必须解决的问题. 本文我们详细讨论一下, ...
- DIV常用属性大全
目录 一.属性列表 二.常用属性 三.一些特殊效果 四.定位和控制 一.属性列表 color : #999999 文字颜色 font-family : 宋体 文字字型 font-size : 10pt ...