- 4
- 0
- 约1.41万字
- 约 20页
- 2021-10-24 发布于上海
- 举报
延迟任务的实现总结
上一篇写了使用 RabbitMQ 来实现延迟任务的实现,其
实实现延迟任务的方式有很多,各有利弊,有单机和分布式
的。在这里做一个总结,在遇到这类问题的时候希望给大家
一个参考和思路。 延迟任务有别于定式任务,定式任务往
往是固定周期的,有明确的触发时间。而延迟任务一般没有
固定的开始时间,它常常是由一个事件触发的,而在这个事
件触发之后的一段时间内触发另一个事件。延迟任务相关的
业务场景如下: 场景一:物联网系统经常会遇到向终端下
发命令,如果命令一段时间没有应答,就需要设置成超时。
场景二:订单下单之后 30 分钟后,如果用户没有付钱,则
系统自动取消订单。 下面我们来探讨一些方案,其实这些
方案没有好坏之分,和系统架构一样,只有最适合。对于数
据量较小的情况下,任意一种方案都可行,考虑的是简单明
了和开发速度,尽量避免把系统搞复杂了。而对于数据量较
大的情况下,就需要有一些选择,并不是所有的方案都适合
了。 1. 数据库轮询 这是比较常见的一种方式, 所有的订
单或者所有的命令一般都会存储在数据库中。我们会起一个
线程去扫数据库或者一个数据库定时 Job ,找到那些超时的
数据,直接更新状态,或者拿出来执行一些操作。这种方式
很简单,不会引入其他的技术,开发周期短。 如果数据量
比较大,千万级甚至更多,插入频率很高的话,上面的方式
在性能上会出现一些问题,查找和更新对会占用很多时间,
轮询频率高的话甚至会影响数据入库。一种可以尝试的方式
就是使用类似 TBSchedule 或 Elastic-Job 这样的分布式的任
务调度加上数据分片功能,把需要判断的数据分到不同的机
器上执行。 如果数据量进一步增大,那扫数据库肯定就不
行了。另一方面,对于订单这类数据,我们也许会遇到分库
分表, 那上述方案就会变得过于复杂, 得不偿失。 2. JDK
延迟队列 Java 中的 DelayQueue 位于 java.util.concurrent
包下,作为单机实现,它很好的实现了延迟一段时间后触发
事件的需求。由于是线程安全的它可以有多个消费者和多个
生产者,从而在某些情况下可以提升性能。 DelayQueue 本
质是封装了一个 PriorityQueue ,使之线程安全,加上 Delay
功能,也就是说,消费者线程只能在队列中的消息“过期”之
后才能返回数据获取到消息, 不然只能获取到 null 。 之所以
要用到 PriorityQueue ,主要是需要排序。 也许后插入的消息
需要比队列中的其他消息提前触发,那么这个后插入的消息
就需要最先被消费者获取,这就需要排序功能。
PriorityQueue 内部使用最小堆来实现排序队列。 队首的, 最
先被消费者拿到的就是最小的那个。使用最小堆让队列在数
据量较大的时候比较有优势。使用最小堆来实现优先级队列
主要是因为最小堆在插入和获取时,时间复杂度相对都比较
好,都是 O(logN) 。 下面例子实现了未来某个时间要触发的
消息。我把这些消息放在 DelayQueue 中,当消息的触发时
间到,消费者就能拿到消息,并且消费,实现处理方法。示
例代码: /*
* 定义放在延迟队列中的对象,需要实现
原创力文档

文档评论(0)