延迟任务调度系统—技术选型与设计(上篇)

延迟任务的场景是?

现有的解决方案是?

存在的问题是什么?

希望达到的目标是?

可以实现的方案有?

RabbitMQ实现

通过死信和死信路由实现

通过延迟消息插件来实现

Redis实现

DelayQueue实现

时间轮实现

单表时间轮

分层时间轮

之前的设计(DB/DelayQueue/ZooKeeper)

另一种方案(DB/DelayQueue/ZooKeeper/MQ

延迟任务的场景是?

习题考试截止前3天,给未提交用户发送消

学习项目开课前2小时,给参与用户发送通知

问卷开始收集时,才对用户可见

问卷结束收集时,触发一些操作

指定时间发布课件

课程结束时,开始计算用户结业信息

直播时间到了,给用户发送消息

用户下单后,30分钟内未付款,关闭订单

用户付款后,24小时内未发货,提示发货

用户打车后,48小时后自动评价为5星

这类业务的特点是:延迟执行。一种比较简单的方法是使用后台线程扫描符合条件的业务数据,逐一处理。 这种方法扫描间隔时间不好设置,间隔时间过大影响精确度,过小则影响效率和性能。

现有的解决方案是?

通过linux的crontab触发定时任务

扫描业务表,筛选出符合条件的数据对其进行操作

存在的问题是什么?

由于每种类型的任务都设有扫描间隔,任务不能精确处理

扫描业务库,影响业务正常操作

任务的执行过于密集,容易导致服务器间隔性压力

存在系统单点,触发定时调度的服务挂了,所有任务都不会执行

系统不具容错能力,一旦错过了,任务就不会再被执行

没有统一的视图来查看任务的执行情况

没有告警来提示失败的任务

希望达到的目标是?

精确性(可在指定时间触发任务处理)

通用性

高性能(集群能力不少于1000TPS)

高可用(支持多实例部署)

可伸缩(增加和减少服务时,任务会重新分配)

可重试(任务失败可重试)

多协议(支持http\dubbo调用)

可管理(业务使用方可修改、删除任务)

能告警(失败次数达到阈值可触发告警)

统一视图(方便查看任务执行情况,可手动干预任务执行)

 

下面所讨论技术方案的前提是精确触发,所以我们不讨论目前业界的一些分布式调度系统如:elastic-job,xxl-job,tbschedule等, 这些系统解决不了延迟任务精确触发问题。

可以实现的方案有? RabbitMQ实现

通过死信和死信路由实现

原理如下:

延迟任务调度系统—技术选型与设计(上篇)

何为死信:

消息被拒绝

消息已过期

队列达到最大长度

RabbitMQ可以对队列和消息设置x-message-tt、expiration来控制消息的存活时间,如果超时,消息变为死信。

 

何为死信路由:

RabbitMQ可以对队列设置x-dead-letter-exchange和x-dead-letter-routing-key两个参数。
当消息在一个队列中变成死信后会按这两个参数路由,消息就可以重新被消费。

 

实例操作:

创建延迟队列(设置死信路由)

创建就绪队列

创建死信路由

绑定死信路由与就绪队列

发送延迟消息

消息过期后进入就绪队列

 

优点:

高效,可以利用RabbitMQ的分布式特性轻易进行横向扩展,且支持持久化

缺点:

不支持对已发送的消息进行管理

一个消息比在同一队列中的其他消息提前过期,提前过期的消息也不会优先进入死信队列。

所以需要确保业务上每个任务的延迟时间是一致的。如果有不同延时的任务,需要为每种不同延迟的任务单独创建消息队列,缺乏灵活性。

通过延迟消息插件来实现

原理如下:

延迟任务调度系统—技术选型与设计(上篇)

核心代码流程:

 

延迟任务调度系统—技术选型与设计(上篇)

其原理是延迟消息会被保存到Mnesia表,在Exchange中根据每个message头设置的延迟时间x-delay,消息过期后才路由到对应队列。

实例操作:

下载插件
https://bintray.com/rabbitmq/community-plugins/download_file?file_path=rabbitmq_delayed_message_exchange-0.0.1.ez

安装插件

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfjpf.html