在开发中,我们会遇到需要延时任务的业务场景,例如:用户下单之后未在规定的时间内支付成功,该订单会自动取消; 用户注册成功15分钟后,发消息通知用户;还有比如到期自动收货,超
在开发中,我们会遇到需要延时任务的业务场景,例如:用户下单之后未在规定的时间内支付成功,该订单会自动取消; 用户注册成功15分钟后,发消息通知用户;还有比如到期自动收货,超时自动退款等都是类似的延时任务的业务问题。 这里主要介绍一下几种方法:
1、定时任务写一个定时任务,定期扫描数据库中的订单,如果时间过期,就取消这个订单。这种实现方法成本低、实现容易。这里使用@Scheduled注解实现,也可以用Quartz框架实现定时任务。
优点:实现容易,成本低,不依赖其他组件。 缺点:
2、JDK延迟队列 DelayQueueDelayQueue是JDK提供的一个无界队列,它的本质是封装了一个PriorityQueue(优先队列), PriorityQueue内部使用完全二叉堆来实现队列排序,在向队列中插入元素时,需要给出这个元素的Delay时间,也就是过期时间,队列中最小的元素会被放在队首,队列中的元素只有到了Delay时间才允许从队列中取出。 具体的实现思路就是:首先创建一个实体类实现Delay接口,然后将它放入DelayQueue队列中。 (1)定义实现Delayed接口的实体类需要实现Delayed接口的两个方法:getDelay()和compareTo()
(2)将延时任务放入队列
优点:不依赖任何第三方组件,实现方便。 缺点:因为DelayQueue是基于JVM的,如果放入的订单过多,会造成JVM溢出。如果JVM重启了,那所有的数据就丢失了。 3、redis过期监听redis是一个高性能的key,value数据库,除了用作缓存之外,它还提供了过期监听的功能。 在redis.conf中配置 配置notify-keyspace-events "Ex" 即可开启此功能。 springboot 项目集成redis配置过期监听 在pom中引入依赖
在yml中配置redis源
编写redis配置类
编写redis工具类
编写监控类 在代码中继承KeyspaceEventMessageListener ,实现onMessage就可以监听过期的数据量
测试
优点:由于redis的高性能,所以在设置以及消费key时的速度可以保证。 缺点: 由于redis的key过期策略的原因,当一个key过期时,无法立刻保证将其删除,自然我们监听事件也无法第一时间消费到这个key,所以会存在一定的延迟。 此外,在redis5.0之前,订阅发布消息并没有被持久化,自然也没有所谓的确认机制,所以一旦消费信息过程中我们的客户端发生了宕机,这条消息就彻底丢失了。 4、Redisson分布式延迟队列Redisson是一个基于redis实现的Java驻内存数据网络,它不仅提供了一系列的分布式Java常用对象,还提供了许多分布式服务。Redisson除了提供我们常用的分布式锁外,还提供了一个分布式延迟队列RDelayedQueue ,它是一种基于zset结构实现的延迟队列,其实现类是RedissonDelayedQueue,在springboot中整合使用Redisson分布式延迟队列的步骤如下: 引入pom依赖,yml中配置redis连接
创建延时队列生产者
创建延时队列消费者
测试
优点:使用简单,并且其实现类中大量使用lua脚本保证其原子性,不会有并发重复问题。 缺点:需要依赖redis 5、RocketMQ延迟消息RocketMQ是阿里巴巴开源的一款分布式消息中间件,基于高可用分布式集群技术,提供低延迟的、可靠的消息发布与订阅服务。下面是在springboot中集成RocketMQ延迟消息的步骤: 安装并启动 RocketMQ 服务 可参考RocketMQ 官方文档进行安装和启动 引入依赖
配置RocketMQ
创建消息生产者
创建消息消费者
测试
优点:系统之间完全解耦,只需要关注生产及消费即可。其吞吐量极高。 缺点:RocketMQ是重量级的组件,引入后,随之而来的消息丢失等问题都增加了系统的复杂度。 6、RabbitMQ死信队列当RabbitMQ中的一条正常信息,因为过了存活时间(ttl过期)、队列长度超限等原因无法被消费时,就会被当成一条死信消息,投递到死信队列。基于这样的机制,我们可以给消息设置一个ttl ,等消息过期就会进入死信队列,我们再消费死信队列即可,这样,就可以达到和RocketMQ一样的效果。springboot集成rabbitMQ的步骤如下: 安装并启动 RabbitMQ 服务 可参考RabbitMQ官方文档进行安装和启动 引入依赖
配置RabbitMQ
配置 RabbitMQ 队列和交换机
创建消息生产者
创建消息消费者
测试
优点:同RocketMQ一样可以使业务解耦。 缺点:RabbitMQ 的 TTL 是基于队列的,而不是基于单个消息的精确时间控制。当队列中有多个消息时,即使某个消息的 TTL 已经过期,也需要等待前面的消息被处理完才能进入死信队列,导致消息的实际处理时间可能会有一定的延迟,无法保证精确的延迟时间。 |
2021-06-05
2021-05-27
2021-05-26
2021-06-05
2021-05-16