文章目录
- 概述
- 种处理方案
- 任务调度实现(定时任务)
- 基于redis 如何实现
- 1redis过期key实现(键通知机制)
- 基于redis延迟队列
- Redisson实现一个延迟队列
- 基于MQ的延迟队列实现
概述
订单30分钟未支付自动取消怎么实现
日常开发中,我们经常遇到这种业务场景,如:外卖订单超 30 分钟未支付,则自动取订单;用户注册成功 15 分钟后,发短信息通知用户等等。这就延时任务处理场景。
在电商,支付等系统中,一设都是先创建订单(支付单),再给用户一定的时间进行支付,如果没有按时支付的
话,就需要把之前的订单(支付单)取消掉。这种类以的场景有很多,还有比如到期自动收货,超时自动退款,下
单后自动发送短信等等都是类似的业务问题。
种处理方案
定期轮询(数据库定时任务 Quartz)
JDK DelayQueue
JDK Timer
ScheduledExecutorService 周期性线程池
时间轮(kafka)
时间轮(Netty的HashedWheelTimer)
redis过期监听
redis的zset
redisson
zookeeper之curator
kafka和rocketmq延迟消息
RabbitMQ(延迟队列和死信队列)
Quartz,xxljob等定时任务框架
Koala(考拉)
JCronTab(仿crontab的java调度器)
SchedulerX(阿里)
有赞延迟队列
任务调度实现(定时任务)
使用 Java 定时任务框架(如 Quartz、Spring Task 等)创建一个定时任务,定时检查订单的到期时间。
在订单创建时,将订单的到期时间保存在数据库中或缓存中。
在定时任务中,获取当前时间,查询数据库或缓存,找出已到期但尚未关闭的订单。
针对每个到期订单,执行关闭订单的操作,例如更新订单状态为关闭或发送关闭订单的消息。
定时任务可以根据需求设置执行频率,以保证及时关闭到期订单。
缺点:效率非常低,消耗服务器性能,对数据库造成压力,
基于redis 如何实现
redis过期监听
redis的zset
redisson
1redis过期key实现(键通知机制)
1)用户下单的时候,生成一个令牌(有效期)30分钟,存放到我们redis;
2)redis.set(orderToken ,orderID) 下单时候存放到redis,并存储id入库,30分钟过期,
3)redis客户端监听,过期获取到orderId,拿orderId去查订单,没有支付则,订单关闭,库存增加
缺点: 1) 非常冗余 ,会在表中存放一个冗余字段 2) 键通知机制是一种并不可靠的消息机制,如果系统需要需要很好的可靠性,那么它并不是一种很好的选择。
基于redis延迟队列
优点:可以满足吞吐量
缺点:存在任务丢失的风险(当 Redis 实例挂了的时候)。因此,如果对性能要求比较高,同时又能容忍少数情况下任务的丢失,那么可以使用这种方式来实现。
使用 sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生
产消息,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。
Redis延迟队列是一种常用的消息队列模式,用于延迟处理任务或消息。其原理通常基于两个主要组件:有序集合(Sorted Set)和定时任务轮询。
- 有序集合(Sorted Set):
○ Redis中的有序集合是一种数据结构,其中的每个成员都关联了一个分数(score)。有序集合的特性之一是按照分数从小到大排序。在延迟队列中,可以将消息或任务的执行时间作为分数,将消息内容作为成员存储在有序集合中。 - 定时任务轮询:
○ 定时任务轮询是一种机制,用于定期检查有序集合中是否有到期的任务或消息。通过定期轮询有序集合,可以检查是否有任务的执行时间已到达,从而将其取出并执行。
Redisson实现一个延迟队列
Redisson是一个基于Redis的分布式Java对象和服务框架,它提供了一系列的高级特性,如分布式对象、分布式锁、分布式消息队列等。其中,分布式消息队列可以非常方便地实现延迟队列。
基于Redisson实现延迟队列的一种常见方式是使用Redis的sorted set来存储任务,并使用Redis的定时器功能触发任务的执行。具体实现步骤如下:
- 定义一个Java类来表示任务,该类需要包含任务的执行时间、任务内容等属性。
- 使用Redisson获取一个RedissonClient对象,该对象用于与Redis进行交互。
- 创建一个RedissonDelayedQueue类,该类包含以下几个方法:
addTask:将一个任务添加到delayed queue中,使用Redis的zadd命令将任务的执行时间作为score,任务内容作为value添加到sorted set中。
removeTask:从delayed queue中删除指定的任务,使用Redis的zrem命令从sorted set中删除指定的value。
pollTask:从delayed queue中取出最早可执行的任务,使用Redis的zrangeByScore命令获取score小于当前时间的最小value,并使用Redis的zrem命令删除该value。 - 使用Redis的定时器功能,每隔一段时间(例如1秒钟),调用RedissonDelayedQueue的pollTask方法,将需要执行的任务取出并执行。
这样,就可以基于Redisson实现一个简单的延迟队列了。需要注意的是,这种方式只适用于任务数量较少的情况,如果任务数量较大,可以考虑使用更高级的方案,比如使用Redis的Lua脚本等。
基于MQ的延迟队列实现
- 延迟队列方案:
使用消息队列(如 RabbitMQ、Kafka 等)创建一个延迟队列,并设置过期时间为订单的到期时间。
在订单创建时,将订单信息发送到延迟队列。
消费者监听延迟队列,当订单到期时,消费者从队列中接收到该订单并执行关闭操作。
可以使用单个消费者或多个消费者来处理到期订单,实现并行处理和提高吞吐量。
RabbitMQ的延迟队列,使用过期时间+死信队列来实现
实现原理:
1)下单投放消息到 A交换机(过期时间30分钟),将 aa队列绑定到该死信交换机A, 不设置aa队列的消费者(故此消息一直未消费)
2)30分钟后,过期,消息投递到死信交换机,死信队列的消费者消费死信消息, 判断订单id是否支付,执行业务逻辑,支付->return 。未支付->关闭订单,返还库存。