1)使用jdk原生DealyQueue
2)使用RocketMQ定时消息
3)使用redis的过期监听
4)使用定时任务
下面我们就来详细介绍以下这些方式的具体实践步骤
1)使用jdk原生DealyQueue
- 当客户新增超时订单的时候将订单信息添加到数据库和延迟阻塞队列中
- dealyQueue使用过期时间做升序排序
- 使用线程不停的轮询队列的首节点,当到达过期时间时就修改数据库订单状态
- 服务重启的时候需要将数据库中所有未支付的超时订单加入到dealyqueue中
优点:简单不需要借助其他第三方组件,成本低
缺点:所有的超时订单都需要加入到队列中,占用内存大,
没办法做到分布式,并且数据量过大容易照成内存耗尽。
适用于单体应用并发量和数据量不是很高的场景。
2)使用MQ定时消息
- 当用户新增超时订单时向Mq服务发送一个定时消息
- mq内置的定时任务在到达定时时间后将信息分发到消息队列
- 监听消息队列修改数据库订单状态
优点:使用简单,支持分布式,精度高,任意时刻都可处理
缺点:订单时长最高只能24h,并且需要额外的成本去维护
每个订单都需要新增一个定时消息,并且不会被立刻消费,增加了存储成本
同一时刻大量消息会导致消息延时:定时消息的处理逻辑是先存储到达定时时间才会投递给消费者,因此大量消息在同一时刻被处理发放会照成系统压力过大,分发延迟。
3)使用redis过期监听
新增订单时设置过期时间存储到redis
实现RedisKeyExpirationListerner接口实现对redis中的订单过期监听,修改 数据库中的订单状态。
优点:实现简单
缺点:不可靠,Redis过期通知时,服务重启,可能宕机会导致消息丢失,可以通过定时任务做补偿机制。
如果有大量订单存储会降低redis性能
4)使用定时任务
定时任务频繁访问数据库会影响性能,可以单独抽取出一个超时库
优点:稳定性和效率非常高
数据可以批量处理,降低了数据库压力
可运维性更高,成本低
总结:对于超时精度比较第高并且在24小时内,且不会有峰值压力的可以使用rocketMq的定时消息处理.
对应于,订单多在24小时以上超时并且精度不敏感,并且海量数据批处理的场景可以使用定时任务。