目录
- 了解需求
- 方案1:数据库轮询
- 方案2:JDK的延迟队列
- 方案3:时间轮算法
- 方案4:Redis缓存
- 方案5:使用消息队列
在开发中,我们经常会遇到一些关于延时任务的需求。例如,生成订单后30分钟未支付则自动取消,或者生成订单后60秒内给用户发短信等。这些延时任务如何实现呢?它们和定时任务的区别又在哪里呢?下面我们就来详细探讨一下。
定时任务有明确的触发时间,而延时任务则没有固定的执行周期,它是在某事件触发后一段时间内执行。定时任务一般执行的是批处理操作,涉及多个任务,而延时任务则通常是单个任务。下面我们将以判断订单是否超时为例,进行方案分析。
方案一:数据库轮询
该方案通常在小型项目中使用。通过一个线程定时地去扫描数据库,根据订单时间来判断是否有超时的订单,然后进行update或delete等操作。虽然该方法简单易行且支持集群操作,但存在对服务器内存消耗大、存在延迟以及数据库损耗极大的问题。
方案二:JDK的延迟队列
该方案是利用JDK自带的DelayQueue来实现。DelayQueue是一个阻塞队列,只有当延迟期满时才能从中获取元素。我们定义一个OrderDelay类实现Delayed接口,并将其放入DelayQueue中。当订单超时时,该订单对应的元素将从队列中取出并执行相应操作。
方案三:时间轮算法
时间轮算法可以类比于时钟。通过设定一定的ticks(的次数)和tick duration(每次的时间),我们可以模拟出类似时钟的延时功能。当有一个任务需要在特定时间执行时,我们可以将其放在时间轮上相应位置。当时间轮到该位置时,即可执行相应任务。
方案四:Redis缓存
利用Redis的Zset(有序集合)功能,我们可以实现延时队列。将订单超时时间戳与订单号分别设置为score和member,系统扫描第一个元素判断是否超时。如果出现多个消费者同时取到同一订单的情况,则需要借助Redis的其他功能如发布订阅、键空间通知等来解决。
方案五:使用消息队列
利用RabbitMQ或Kafka等消息队列也可以实现延时队列的功能。这些消息队列提供了x-message-tt等参数来设置消息的生存时间,当消息超过该时间后将成为死信,我们可以将其路由到另一个队列进行重新处理。
每种方案都有其优缺点和适用场景。在实际开发中,我们需要根据项目的需求和资源情况来选择合适的方案。无论是哪种方案都需要我们进行充分的测试和验证以确保其稳定性和可靠性。
此外在开发过程中我们还需要注意代码的可读性、可维护性以及性能优化等问题以确保项目的质量和效率。
最后需要强调的是无论选择哪种方案都需要对相关技术进行深入学习和理解以便能够熟练地运用到实际项目中。
- 在编程领域,ThreadFactory是一个重要的概念,它负责生成工作线程,通常与线程池结合使用。
- 关于tickDuration和unit的设定,它们定义了时间间隔,通常每格默认是100毫秒。
- ticksPerWheel是有关哈希计算的一个关键参数。默认值为512。若传入值非2的幂次方,系统将自动调整为大于等于该数值的最近一个2的幂次方值,这种做法有利于优化hash值的计算过程。
- TimerTask是定时任务实现的核心接口,它的run方法包装了整个定时任务的逻辑执行过程。
- Timeout是一种定时任务的句柄。当一个定时任务被提交至Timer后,它会返回这个句柄。通过此句柄,外部可取消此定时任务并对其实时状态进行基本的判断。
- Timer是HashedWheelTimer实现的父接口。它主要定义了如何提交定时任务以及如何停止整个定时机制的操作。
从执行结果来看,order3和order3延时任务仅执行了一次,而order2和order1则被设定为定时任务,按照不同的周期重复执行。
为了让大家更易理解,上述代码示例虽然较为简洁,但已将几种实现方式的demo上传至GitHub地址:/chengxy-nds/delayqueue。对此感兴趣的朋友们可以下载并亲自体验一番。