我是3y,一年CRUD经验用十年的markdown程序员常年被誉为职业八股文选手
前阵子,有个小伙伴找到问我,如果要实现延时发送,那是基于什么来做的。
我看到这个问题之后,稍微思考了下,觉得确实也是austin平台所需要实现的功能。对于前端而言,只要让业务方在创建模板的时候填选屏蔽类型,后端根据这个字段增添一点点细节,这个需求就做完了,简单!
延迟消息如何实现?
延迟消息就是字面上的意思:当接收到消息之后,我需要隔一段时间进行处理(相对于立马处理,它隔了一段时间,所以他叫延迟消息)。
在原生的Java有DelayQueue供我们去使用,在使用的时候,我们add进去的队列的元素需要实现Delayed接口(同时该接口继承了Comparable接口,所以我们DelayQueue是有序的)
publicinterfaceDelayedextendsComparableDelayed{longgetDelay(TimeUnitunit);}
从poll的源码上可以清晰地发现本质上就是在取数的时候判断了下时间
longdelay=first.getDelay(NANOSECONDS);if(delay=0)returnq.poll();
有的人就反驳到:这不是废话吗?肯定要判断时间啊,不判断时间怎么知道我要延迟的消息什么时候执行。
明白了这点之后,我们再来别的方案。因为在生产环境中是不太可能使用JDK原生延迟队列的,它是没有持久化的,重启就会导致数据丢失。
当austin项目使用内存队列去解耦处理数据已经有人提出服务器重启的时候该怎么办,我的解决思路就是通过优雅关闭服务器这种手段去尽量避免数据丢失,而延迟队列这种就不能这么干了,我们等不了这么久的。
稍微想想还有什么存储适合当队列且有持久化机制的呢?
答案显而易见:Redis和消息队列(Kafka/RocketMQ/RabbmitMQ等)
我们先来看Redis里提供了一种数据结构叫做zset,它是可排序的集合并且Redis原生就支持持久化。有赞的延迟队列就是基于通过zset进行设计和存储的。整体架构如下图:
简单理解这张图就是:将需要延迟的消息放置Redis,通过Timer轮询得到可执行的消息,将可执行的消息放置不同的Topic供业务方自行消费。
更多的设计思路可以参考有赞的技术原文,这里我不再赘述: