1. 兜底方案

尽管通过多种机制提高了消息的可靠性,仍无法保证MQ消息100%的成功传递。因此,交易服务需要有一个兜底方案来确保订单的支付状态一致性,即使MQ通知失败。

1.1. 方案思路

由于MQ通知可能会失败,交易服务可以主动查询支付状态,确保即便MQ通知丢失,支付状态仍能得到正确更新。

1.2. 方案流程

  1. 支付服务发送MQ消息:支付服务在用户支付成功后,通过MQ通知交易服务更新订单状态。
  2. MQ消息可靠性:为了确保MQ消息的可靠性,采用生产者确认、消费者确认、以及消费者失败重试等策略。
  3. 交易服务主动查询:当MQ通知失败时,交易服务主动定期查询支付状态。
  4. 定时任务:由于无法确定用户支付的确切时间,采用定时任务(例如每隔20秒查询一次)来确认订单是否已支付,并更新订单状态。

1.3. 关键点


2. 延迟消息

在电商支付业务中,尤其是库存有限的商品,需要避免用户长时间占用库存资源。如果用户下单后长时间未付款,可能导致其他用户无法购买该商品。因此,需要在用户下单后的规定时间内检查支付状态,并取消未支付订单,释放库存资源。

2.1. 延迟任务需求

为确保在一定时间后检查支付状态,通常使用延迟任务。最简单的方式是通过MQ的延迟消息

2.2. 延迟消息实现方式

在RabbitMQ中,可以通过以下两种方式实现延迟消息:

  1. 死信交换机(DLX) + TTL
  2. 延迟消息插件

2.3. 死信交换机与TTL

  1. 死信交换机
    • 死信是指满足以下条件的消息:
      • 消费者拒绝消息,且requeue=false
      • 消息超时,未被消费。
      • 队列满,无法投递消息。
    • 死信消息会投递到指定的死信交换机(Dead Letter Exchange),然后根据绑定的路由规则,转发到另一个队列。
  2. TTL(消息有效期)
    • 通过设置消息TTL(生存时间),当消息的TTL到期时,消息变为死信,投递到死信交换机,再根据路由规则转发到目标队列,最终由消费者处理,从而实现延迟消费。

2.4. 延迟消息的实现

  1. 发送消息到ttl.fanout交换机,并设置TTL(如5000毫秒)。
  2. 消息进入ttl.queue队列,由于没有消费者,消息超时后变为死信。
  3. 死信投递到死信交换机hmall.direct,并根据之前的RoutingKey(例如blue)路由到direct.queue1
  4. 如果此时有消费者与direct.queue1队列绑定,则该消费者可以消费该延迟消息。

2.5. 死信交换机与延迟消息的总结