开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情
定时任务
启动cron定时任务扫表,将超时订单状态设置为失败
基于消息队列的延迟队列
用户下单,向mq投放消息到交换机,设置过期时间为30min,消息到a队列,a队列绑定私信交换机,不设置aa队列的消费者。超过30min后,过期消息投递到死信交换机,死信队列,由死信消费者消费,订单未支付则取消。
使用rabbitmq的死信交换机(exchange)和消息的存活时间(ttl)
死信交换机就是普通的交换机,存放过期信息:
- 消息被consumer拒收,且设置不会再次放入队列中被其他消费者使用
- 消息队列的消息数量超过最大队列长度
- 消息在队列的存活时间超过ttl
缺点:
- 第一条消息成为死信前,后面的消息即使过期也不会投递到死信
时间轮算法
环形队列,环上每个slot是一个任务列表。根据过期时间,计算订单所在时间轮的cycle和slot。环的index不停移动,移动到新的slot,删除对应的task
redis
基于notify-keyspace-events事件通知
notify-keyspace-events,配置消息监听,
用户下单生成令牌有效期30min,存入redis。redis.set(orderToken,orderID),并将id存储入库。redis客户端监听,过期后获取orderID,去数据库查询订单,没有支付则关闭订单。
缺点
- 键空间通知采用发送即忘(fire and foget),订阅事件的客户端会丢失所有在断线期间分发给它的事件
- 定时任务离线扫描并删除部分过期键,访问键时惰性检查是否过期并删除过期键。不保证在设定的过期时间内立即删除并发送过期通知。
基于redis的延迟队列
使用zset,订单超时时间戳和订单号分别设置为score和member,通过score扫描最小元素判断是否超时。
高并发条件下多消费者可能取到同一个订单号,需要加入分布式锁实现幂等性,降低性能。