延迟队列实现

分布式延迟消息队列实现分析与设计

介绍

延迟队列,顾名思义它是一种带有延迟功能的消息队列。 那么,是在什么场景下我才需要这样的队列呢?

很多时候我们会有延时处理一个任务的需求,比如说:

2个小时后给用户发送短信。
15分钟后关闭网络连接。
2分钟后再次尝试回调。

下面我们来分别探讨一下几种实现方案:

1、Java中的DelayQueue

Java中的DelayQueue位于java.util.concurrent包下,本质是由PriorityQueue和BlockingQueue实现的阻塞优先级队列。

见《延时队列:Java中的DelayQueue

2、使用Redis实现

前文我们看到,可以通过优先级队列来实现延迟队列的功能。Redis提供了很多数据结构,其中的zset是一种有序的数据结构;我们可以通过Redis中的zset来实现一个延迟队列。

基本的方法就是使用时间戳作为元素的score存入zset。

几种延时队列

延时队列就是一种带有延迟功能的消息队列。下面会介绍几种目前已有的延时队列:
1.Java中java.util.concurrent.DelayQueue
优点:JDK自身实现,使用方便,量小适用
缺点:队列消息处于jvm内存,不支持分布式运行和消息持久化
2.Rocketmq延时队列
优点:消息持久化,分布式
缺点:不支持任意时间精度,只支持特定level的延时消息
3.Rabbitmq延时队列(TTL+DLX实现)
优点:消息持久化,分布式
缺点:延时相同的消息必须扔在同一个队列

根据自身业务和公司情况,如果实现一个自己的延时队列服务需要考虑一下几点:

* 消息存储
* 过期延时消息实时获取
* 高可用性

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

mafka 延迟消费队列

延迟消费即为消息在延迟一定时间之后,消费者才能对该消息进行消费。 目前 mafka 并不具备延迟消费的功能,在每个消息添加时间戳之后,我们有条件根据消息的时间戳来进行延迟消费

可以看到 consumer thread 在处理消息的过程中会从 block queue 里面不断取出消息进行消费, 我们根据消息上带有的时间戳来判断是否已经达到延迟条件,决定是否对消息进行消费即可。

采用这样的方式存在一个问题,就是同一个 block queue 里面的消息可能会来着多个 partition,这些 parition 之间的消息并没有时间上的先后关系,如果 consumer thread 因为 partition 1 中的消息未达消费时间点就阻塞,会导致 partition 2 中可能已经达到时间点的消息并不能得到及时的处理,这样 partition 2 中的处理延迟会变得比较大。如果我们限定 consumer thread 的数量大于等于partition 的数量, 就可以简单的规避这个问题。否则我们需要重构整个 consumer 的关于处理消息的逻辑。

这里我们首先首先时间比较简单的方式, 即我们限定消费线程的数量等于 partition 的数量(目前很多时候我们也是这么做的), 这样我们只需在 consumer 的配置项里添加 consume.message.delay 字段,然后在消费的时候根据消息上带有的时间戳进行延迟处理即可。

3.4 到期消息读取发送模块

     此模块主要有两部分:到期消息发送和失败消息处理,其中失败消息的处理类似与到期消息发送,首先介绍到期消息的发送。

     3.4.1 到期消费发送

     在到期消息获取中,对于到期的时刻,延迟一定时间后(此时间大于Tair写入耗时即可,防止正在写入中的消息漏读),用此时间做为主key,通过此主Key可以获取到即将到期的所有消息。根据此Key通过Tair的GetRange接口,会返回一批数据,逐条取出进行发送。发送时从连接池中取出此Topic的Producer进行发送。如果发送失败,将此消息删除,重新存入Tair中的失败队列,由专门的线程读取发送;如果发送成功,将此消息从tair中删除。如果此Timestamp数据全部读取完毕,将TimeStamp存入Tair,以便重启时知道起始位置,由于多个线程同时读取不同的timestamp发送,存储时选取当前做小的进行存贮。

3.4.2 失败消息再次发送

    对于发送失败的消息,会存储到特定的主Key下,读取发送流程与到期消息类似,特定的线程读取此key下的内容,然后重新发送,发送成功后删除,不成功的等待下一轮发送。

猜你喜欢

转载自blog.csdn.net/kuaipao19950507/article/details/106097425