首先带入我们的业务场景:我们买火车票或者叫外卖的时候,下完单之后会跳转到支付页面,页面会有一个计时器,要求在指定时间内完成支付,否则订单自动取消。这是延时任务的一个典型场景,分析这个场景,就是如何在订单超时的时候立即触发取消这个动作。
那么如何实现这种业务场景呢?
- 定时任务轮询db
用户下单之后,数据库会存在一条订单记录,包括订单创建时间,订单状态,订单详情。。。。
我们可以启动一个定时任务,每个固定时间轮询DB,
select * from order where create_time<now()-超时时间
这种方式的弊端也很明显,超时时间通常是秒级的,如果任务每秒执行一次,那就等于每秒对订单表做一次扫描,相当好db 资源的。因此定时任务一般不会设置秒级。但是如果设置了分钟级又会牺牲即时性。延长了订单的取消时间。
- DelayQueue
jdk 的DelayQueue (延迟队列)是无界阻塞队列,只有延迟期满时才从中获取元素,每生成一个订单的同时,把订单信息记录入db 同时 订单的id 保存到队列中,启动一个线程不断的从队列中获取元素来做取消订单的动作。
这种方式最大的问题就是 没有将延迟信息持久化,一旦服务重启,队列中的延迟信息不复存在。
- redis的zset在redis中创建一个key是”delayOrders”的zset,每个member就是订单ID,member的score就是该订单的超时时间戳。我们每次从zset中取出score最小也就是最先超时的元素,判断其是否超时,如果超时就将其从zset中删除并取消订单,如果未超时就继续执行下一次循环。
- RabbitMQ的TTL+DLX
RabbitMQ可设置消息过期时间,当消息过期后,可以将该消息投递到队列上设置的死信队列中,重新消费。
四种方式对比:
你觉得 生产应该 使用哪一种?