你真的了解延时队列吗(二)

3,913 阅读5分钟

接着上一个专题[你真的了解延时队列吗(一)](https://juejin.cn/post/6844903648397525006),我们分析一下其他的延时队列实现方式以及相关比较。

时间轮(kafka)

上一个专题讲到了时间轮的概念,TimingWheel并非简单的环形时间轮,而是多层级时间轮,每个时间轮由多个时间格组成,每个时间格为一个时间间隔,底层的时间格跨度较小,然后随着延迟任务延迟时间的长短逐层变大。

默认情况下,各个层级的时间轮的时间格个数为20,第一层时间轮每一个时间格跨度为1ms,整个时间轮跨度为20ms,跨度不够。第二层时间轮每一个时间格跨度为20ms,整个时间轮跨度为400ms,跨度依然不够,第三层时间轮每一个时间格跨度为400ms,整个时间轮跨度为8000ms,现在跨度够了,此任务就放在第三层时间轮的第一个时间格对应的TimerTaskList,等待被执行,此TimerTaskList到期时间是400ms,随着时间的流逝,当此TimerTaskList到期时,距离该任务到期时间还有45ms,不能执行该任务,我们将重新提交到时间轮,此时第一层时间轮跨度依然不够,不能执行任务,第二层时间轮时间格跨度为20,整个世间轮跨度为400,跨度足够,放在第三个时间格等待执行,如此往复几次,高层时间轮最终会慢慢移动到低层时间轮上,最终任务到期执行。

那么时间轮和DelayQueue有什么区别呢?

Øjava.util.Timer与java.util.concurrent.DelayQueue的插入和删除时间复杂度都为对数阶O(log 

ØKafka实现了基于时间轮的定时任务组件,该时间轮定时任务实现的插入与删除(开始定时器与暂停定时器)的时间复杂度都为常数阶O(1)

共同缺点:自己要处理分布式横向扩展的问题,因为数据是放在内存里,需要自己写持久化的备案以达到高可用。

Koala(美团)

整体架构为在RocketMQ的基础上,增加了一个基于LevelDB的延时消息处理引擎,从而实现延时消息的处理。

Ø生产者:生产消息

Ø消费者:消费消息ØBroker: 存储和转发消息

ØCommitLog:使用了RocketMQ,持久化存储消息

ØNameServer:管理broker,producer和consumer从它那获取broker信息

ØDelayEngine:延时消息处理引擎(使用LevelDB进行消息存储)

延迟消息的特点:

  • 延迟消息可能会堆积----需要持久化
  • 需要排序的支持
  • 支持性能高的随机写入和顺序读取

LevelDB的特点:

  • KV系统且数据保存在磁盘
  • 存储数据按key有序存储
  • LevelDb性能非常突出,官方网站报道其随机写性能达到40万条记录每秒,而随机读性能达到6万条记录每秒。总体来说,LevelDb的写操作要大大快于读操作,而顺序读写操作则大大快于随机读写操作。

爱奇艺任务调度服务JCrontab

JCronTab 借鉴了 crontab 的语法,其区别在于 command 不再是 unix/linux 的命令,而是一个 Java 类。

如果该类带参数,例如“com.ibm.scheduler.JCronTask2#run”,则定期执行 run 方法;如果该类不带参数,则默认执行 main 方法。此外,还可以传参数给 main 方法或者构造函数。

例如“com.ibm.scheduler.JCronTask2#run Hello World“表示传两个参数 Hello 和 World 给构造函数。

Jcrontab VS Quartz


阿里分布式任务调度服务SchedulerX


需求:将一个耗时很长的定时任务进行拆解

由于每次迁移的数据量巨大,如果单台机器去迁移的话是没办法在一天之内完成迁移任务的,所以必须将这样一个迁移任务拆分成多个子任务分片,然后用多台机器去执行不同的子任务分片。

特点

  • 分布式:多台客户端机器中任何一台宕机,服务端自动选择正常运行的客户端继续执行
  • 支持Quartz 时间表达式
  • 用户不需要关心调度逻辑,只需要实现业务逻辑

应用场景

  • 固定时间点触发的任务
  • 周期性触发的任务
  • 通过控制台手动触发的任务

有赞延迟队列设计

  • Job:需要异步处理的任务,与具体的Topic关联在一起。
  • Topic:一组相同类型Job的集合(队列)供消费者来订阅。
  • TTR(time-to-run):Job执行超时时间。单位:秒。

整个延迟队列由4个部分组成:

  • Job Pool 存放job
  • Delay Bucket 时间维度有序队列(存放JobId)
  • Timer 扫描Bucket,delay时间结束,可以执行了,放入ready queue
  • Ready Queue 供消费程序消费(存放JobId,无需有序)



有赞延迟队列-缺点

1、多实例部署时,Timer程序并发执行,如何保证放入ready queue的job是唯一的?

2、 timer是通过独立线程的无限循环来实现,在没有ready job的时候会对CPU造成一定的浪费,如何解决?

延迟技术对比


到此,延迟技术基本都介绍完了!

> 如果大家觉得这篇文章对你有帮助,或者你有什么疑问想提供1v1免费vip服务,都可以关注我的公众号,关注即可免费领取海量最新java学习资料视频,以及最新面试资料,你的关注和转发是对我最大的支持,O(∩_∩)O: ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/7/22/164c2ad786c7cfe4~tplv-t2oaga2asx-image.image)