一口吻说出六种成功延时信息的打算
前言
延时信息(定时信息)指的在散布式异步信息场景下,消费端发送一条信息,宿愿在指定延时或许指定时期点被消费端消费到,而不是立刻被消费。
延时信息实用的业务场景十分的宽泛,在散布式系统环境下,延时信息的性能普通会在下沉到两边件层,通常是 MQ 中内置这特性能或许内聚成一个公共基础服务。
本文旨在讨论经常出现延时信息的成功打算以及打算设计的优缺陷。
成功打算
1.基于外部存储成功的打算
这里讨论的外部存储指的是在 MQ 自身自带的存储以外又引入的其他的存储系统。
基于外部存储的打算实质上都是一个套路,将 MQ 和 延时模块 辨别开来,延时信息模块是一个独立的服务/进程。延时信息先保管到其他存储介质中,而后在信息到期时再投递到 MQ。
当然还有一些细节性的设计,比如信息进入的延时信息模块时曾经到期则间接投递这类的逻辑,这里不倒退讨论。
下述打算不同的是,驳回了不同的存储系统。
基于 数据库(如MySQL)
基于相关型数据库(如MySQL)延时信息表的模式来成功。
经过定时线程定时扫描到期的信息,而后启动投递。定时线程的扫描距离通常上就是你延时信息的最小时期精度。
好处:
缺陷:
基于 RocksDB
RocksDB 的打算其实就是在上述打算上选择了比拟适宜的存储介质。
RocksDB 在笔者之前的文章中有聊过,LSM 树更适宜少量写入的场景。滴滴开源的DDMQ中的延时信息模块 Chronos 就是驳回了这个打算。
DDMQ 这个名目便捷来说就是在 RocketMQ 外面加了一层一致的代理层,在这个代理层就可以做一些性能维度的裁减。延时信息的逻辑就是代理层成功了对延时信息的转发,假设是延时信息,会先投递到 RocketMQ 中 Chronos 公用的 topic 中。
延时信息模块 Chronos 消费失掉延时信息转储到 RocksDB,前面就是相似的逻辑了,定时扫描到期的信息,而后往 RocketMQ 中投递。
这个打算诚恳说是一个比拟重的打算。由于基于 RocksDB 来成功的话,从数据可用性的角度思考,你还须要自己去处置多正本的数据同步等逻辑。
好处:
缺陷:
基于 Redis
再来聊聊 Redis 的打算。上方放一个比拟完善的打算。
这个打算选择 Redis 存储在我看来有几点思考,
然而这个打算其实也有须要琢磨的中央,上述打算经过创立多个 Delayed Queue 来满足关于并发性能的要求,但这也带来了多个 Delayed Queue 如何在多个节点状况下平均调配,并且很或许出现到期信息并发重复处置的状况,能否要引入散布式锁之类的并发控制设计?
在量不大的场景下,上述打算的架构其实可以堕落成主从架构,只准许主节点来处置义务,从节点只做容灾备份。成功难度更低更可控。
定时线程审核的缺陷与改良
上述几个打算中,都经过线程定时扫描的打算来失掉到期的信息。
定时线程的打算在信息量较少的时刻,会糜费资源,在信息量十分多的时刻,又会出现由于扫描距离设置不正当造成延时时期不准确的疑问。可以借助 JDK Timer 类中的思维,经过 wait-notify 来节俭 CPU 资源。
失掉中最近的延时信息,而后wait(口头时期-时期),这样就不须要糜费资源抵达时期时会智能照应,假设有新的信息进入,并且比咱们期待的信息还要小,那么间接notify唤醒,从新失掉这个更小的信息,而后又wait,如此循环。
2. 开源 MQ 中的成功打算
再来讲讲目前自带延时信息性能的开源MQ,它们是如何成功的
RocketMQ 开源版本支持延时信息,然而只支持 18 个 Level 的延时,并不支持恣意时期。只不过这个 Level 在 RocketMQ 中可以自定义的,所幸来说对普通业务算是够用的。自动值为“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,18个level。
深刻的讲,设定了延时 Level 的信息会被暂存在名为SCHEDULE_TOPIC_XXXX的topic中,并依据 level 存入特定的queue,queueId = delayTimeLevel – 1,即一个queue只存相反延时的信息,保障具备相反发送延时的信息能够顺序消费。 broker会调度地消费SCHEDULE_TOPIC_XXXX,将信息写入实在的topic。
上方是整个成功打算的示用意,白色代表投递延时信息,紫色代表定时调度到期的延时信息:
好处:
缺陷:
Pulsar 支持“恣意时期”的延时信息,但成功模式和 RocketMQ 不同。
深刻的讲,Pulsar 的延时信息会间接进入到客户端发送指定的 Topic 中,而后在堆外内存中创立一个基于时期的优先级队列,来保养延时信息的索引信息。延时时期最短的会放在头上,时期越长越靠后。在启动消费逻辑时刻,再判别能否有到期须要投递的信息,假设有就从队列外面拿出,依据延时信息的索引查问到对应的信息启动消费。
假设节点解体,在这个 broker 节点上的 Topics 会转移到其他可用的 broker 上,上方提到的这个优先级队列也会被重建。
上方是 Pulsar 群众号中关于 Pulsar 延时信息的示用意。
乍一看会觉得这个打算其实十分便捷,还能支持恣意时期的信息。然而这个打算有几个比拟大的疑问
关于前面第一点和第二点的疑问,社区也设计了处置打算,在队列中参与时期分区,Broker 只加载较近的时期片的队列到内存,其他时期片分区耐久化磁盘,示例图如下图所示:
然而目前,这个打算并没有对应的成功版本。可以在实践经常使用时,规则只能经常使用较小时期跨度的延时信息,来缩小前两点缺陷的影响。另外,由于内存中存的并不是延时信息的全量数据,只是索引,所以或许要积压上百万条延时信息才或许对内存形成清楚影响,从这个角度来看,官网临时没有完善前两个疑问也可以了解了。
至于第三个疑问,预计是比拟难处置的,须要在数据存储层将延时信息和反常信息辨别开来,独自存储延时信息。
QMQ提供恣意时期的延时/定时信息,你可以指定信息在未来两年内(可性能)恣意时期内投递。
把 QMQ 放到最后,是由于我觉得 QMQ 是目前开源 MQ 中延时信息设计最正当的。外面设计的外围便捷来说就是 多级时期轮 + 延时加载 + 延时信息独自磁盘存储。
QMQ的延时/定时信息经常使用的是两层 hash wheel 来成功的。第一层位于磁盘上,每个小时为一个刻度(默以为一个小时一个刻度,可以依据实践状况在性能里启动调整),每个刻度会生成一个日志文件(schedule log),由于QMQ支持两年内的延时信息(自动支持两年内,可以启动性能修正),则最多会生成 2 * 366 * 24 = 17568 个文件(假设须要支持的最大延时时期更短,则生成的文件更少)。
第二层在内存中,当信息的投递时期行未来到的时刻,会将这个小时的信息索引(索引包含信息在schedule log中的offset和size)从磁盘文件加载到内存中的hash wheel上,内存中的hash wheel则是以500ms为一个刻度。
总结一下设计上的亮点:
总结
本文汇总了目前业界经常出现的延时信息打算,并且讨论了各个打算的优缺陷。宿愿对读者有所启示。