用HashedWheelTimer + Redis + RocketMQ发送未来任意时间精度的延迟消息

在目前开源的RockeMQ版本中,并不支持发送任意时间精度的延迟消息。上次面试,碰到这样的面试题---如果要发送任意时间精度的延迟消息,该如何做?当时回答的不好。今天通过查资料,把这种操作简单落地了。

具体的做法是:

1. 生产延迟消息:延迟消息由两部分组成--该笔消息的订单号key+业务数据value;

2. 存储消息:当把延迟消息组装好之后,把该消息(key,value)放入redis中并设置一定的超时时间同时存入时间轮数据结构中;

3. 取出消息:当该消息在时间轮数据结构中到期时,取出key,然后根据这个key去redis中取value;

4. 通过RocketMQ的生产者线程,把消息发送出去,若发送成功,则把redis中该key删除;若是发送失败,则记录日志,人工补偿;

每部分的作用是:

1. HashedWheelTimer:存储消息的key,key到期时,自动弹出---起到一个定时器的作用;

2. Redis:将完整的延迟消息存储到内存中时,还把数据持久化到硬盘,当redis重启时,基本不丢数据;

3. RocketMQ:发送延迟消息;

这里有几个问题需要注意:

1. 当系统突然宕机,服务器重启后,时间轮HashedWheelTimer中的key都将消失,并且很难恢复,此时丢失的key对应在Redis中的value只能等待时间到期,这种情况怎么办,即数据丢失问题?也可以不使用Redis存储完整的消息,把完整的消息直接放入时间轮数据结构中或放入延迟队列DelayQueue中;用这种方式也会存在数据丢失的问题:即系统突然宕机,服务器重启后,未到期的数据都将丢失,因为对数据没有进行持久化;

2. 当key从HashedWheelTimer中取出后,根据该key在Redis中没取到数据,这种情况该怎么办,即数据不一致的问题?

3. 当消息到期后,用RocketMQ发送时,发送好几次都失败了,这时候除了记录日志,人工进行补偿之外,还有什么好的解决方案?--解决办法之一是:把这些发送失败的消息,存入数据库表中;然后启动一个定时任务,定时把发送失败的消息,通过RocketMQ再次发送出去,若发送成功,将该消息从数据库中删除;若这次还是发送失败,则下次定时任务执行时,再继续尝试发送。

这里的HashedWheelTimer可以用Delayqueue代替,它两相比较而言,HashedWheelTimer的时间复杂度比Delayqueue要好些。

本文是从以下博客中得到的启发:

1. https://www.jianshu.com/p/a8c1458998aa  延时任务队列的原理与实现总结

2. https://mp.weixin.qq.com/s?__biz=MzU3MTQ4MzEwMw==&mid=2247483703&idx=1&sn=3ee685fa4237fcb0a50c1d103b1ed19f&chksm=fcde3787cba9be9188d1bd11c5d663e81c9170a36ad100824453f1836e53cdfe1c2ef088b21a&mpshare=1&scene=1&srcid=#rd   

QMQ延时消息设计与实现

 

你可能感兴趣的:(redis,rocketmq,时间轮,延迟消息发送,RocketMQ延迟消息发送,发送任意时间精度的延迟消息)