延迟任务的场景是?
现有的解决方案是?
存在的问题是什么?
希望达到的目标是?
可以实现的方案有?
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
安装插件