背景
对于过期状态修改(如订单超时取消),有两种常见处理方式:轮询和 MQ 延时消息。
方案一:轮询
直接暴力扫表,对不合法状态进行修改。
缺点:
- 即使没有订单也会去扫表,浪费资源
- 数据量大时不够友好
方案二:MQ 延时消息
只在有订单时才触发查询和修改:
1 2 3 4 5 6 7 8 9 10 11 12
| @Autowired private RabbitTemplate rabbitTemplate;
public void sendDelayedOrderMessage(Long orderId) { MessageProperties messageProperties = new MessageProperties(); messageProperties.setHeader("x-delay", 900000); Message message = MessageBuilder.withBody(String.valueOf(orderId).getBytes()) .andProperties(messageProperties) .build(); rabbitTemplate.convertAndSend("order.delay.queue", message); log.info("发送延时消息:{}", orderId); }
|
消费者处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Autowired private OrderMapper orderMapper;
@RabbitListener(queues = "order.delay.queue") public void handleDelayedOrderMessage(Message message) { String orderIdStr = new String(message.getBody()); Long orderId = Long.parseLong(orderIdStr); Orders order = orderMapper.getById(orderId); if (order != null && order.getStatus() == Orders.PENDING_PAYMENT) { order.setStatus(Orders.CANCELLED); order.setCancelReason("订单超时,自动取消"); order.setCancelTime(LocalDateTime.now()); orderMapper.update(order); log.info("处理订单超时:{}", orderId); } }
|
优点:只在有订单时查询,每次处理的数据量可控。
缺点:同一时刻数据量非常大时,部分数据可能在限定时间内得不到处理(可以通过增加消费者、使用死信队列等方式缓解,但也会引入消息幂等性等新问题)。
小结
两种方案各有利弊:
- 轮询:实现简单,高峰时处理能力强,但空闲时浪费资源
- MQ 延时消息:按需触发,但瞬时高峰吞吐有限
没有银弹,所谓架构就是在各种不完美中找到当下最合适的妥协。