假设场景:业务触发之后会更新状态,所以我们需要在业务触发一段时间之后监控业务数据状态是否发生了改变,把没有发生改变的数据抽取出来发送邮件给开发人员。
jdk中DelayQueue可以实现上述需求,DelayQueue就是延时队列。
DelayQueue提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素
DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素。
所以要实现这个功能,我们需要在服务启动之后,开启一个线程不断的去阻塞获取延迟到期的数据,然后在判断这个数据的状态有没有发生改变,如果没有改变则需要发送邮件
参考: springBoot之延时队列
缺点:
对于没有直接支持延迟队列的中间件MQ,可以采用另一种方式来实现延迟队列。
对于支持死信队列的中间件MQ,可以采用死信队列+TTL过期时间来实现延迟消费Message的功能。
以下几种方式会把消息推送到死信队列中:
所以我们可以利用消息过期会把消息移动到死信队列这一特点,来实现延迟队列。

上面图中的例子,消费者不去监听正常的队列这个队列,而是直接监听死信队列队列。那么我们每次发送消息到正常队列队列,由于设置队列消息的TTL,在TTL时间之后,消息进入到死信队列,从而被消费者消费。从而实现了延迟队列。
对于RabbitMQ中间件来说,网上有很多现成的案例可以参考,对于RabbitMQ的实现可以参考如下:
而如果使用了Solace中间件的,网上文档比较少,主要还是以官方文档为主,可以参考如下:
How to delay sending out messages in Solace PubSub+
优点:
缺点:
requeue_count计数器来控制,比如过期时间就是ttl*requeue_count,一旦过期之后判断requeue_count是否为0,如果不是就减1,然后重新投递到正常队列中,这样就能保证正常队列中的过期时间都是一致的,但是最终的过期时间是不一样的,但是缺点就是存在很多次重新投递的动作)参考:
在 RabbitMQ 3.6.x 之前我们一般采用死信队列+TTL过期时间来实现延迟队列,在 RabbitMQ 3.6.x 开始,RabbitMQ 官方提供了延迟队列的插件。
RabbitMQ 官方提供了延迟队列的插件的优点就是解决了之前采用死信队列+TTL过期时间来实现延迟队列的一些缺点。
缺点:
对于使用了Solace中间件,似乎也有类似的功能,但没具体实现过,可以参考官网文档:
在官网中提到了,如果你使用了死信队列的方式来实现延迟队列,可以考虑迁移到新的方式:
If you are currently using DMQs to implement a delivery delay, contact Solace Professional Services for guidance on migrating your implementation to use the delivery-delay option instead.
参考:Configuring Delayed Delivery
对于定时任务我们可能最常用的就是Schedule Quartz Job,也支持分布式。或者是使用现成的一些分布式定时任务的框架,比如美团分布式定时调度框架XXL-Job
缺点:
优点:
2. 可指定corn表达式,自由配置
3. 可以抽取一段时间的数据进行批处理,不存在之前延迟队列一条条处理,邮件发送速率过快的问题也能解决
4. 不需要在业务代码中埋点
其实还是具体业务场景具体分析,并没有哪一套设计就是能解决一切问题的"银弹"。当有分布式定时任务的场景,就可以结合实际场景来考虑一下以上的方案。