背景

对于过期状态修改(如订单超时取消),有两种常见处理方式:轮询和 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); // 15 分钟
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 延时消息:按需触发,但瞬时高峰吞吐有限

没有银弹,所谓架构就是在各种不完美中找到当下最合适的妥协。