MST

星途 面试题库

面试题:消息队列之RocketMQ事务消息应用场景与设计考量

在电商订单系统中使用RocketMQ事务消息,若订单创建成功但库存扣减失败,该如何通过事务消息机制保证数据一致性,请详细说明设计思路和涉及到的RocketMQ相关操作。
12.2万 热度难度
后端开发消息队列

知识考点

AI 面试

面试题答案

一键面试
  1. 设计思路
    • 订单创建阶段
      • 当用户下单时,系统首先发送一条半消息(Prepare消息)到RocketMQ,此消息不会被消费者立即消费,只是标记该事务的开始。
      • 订单服务创建订单记录到数据库,并将订单状态设为“创建中”。这一步是本地事务操作。
    • 库存扣减阶段
      • 如果订单创建成功,接着尝试扣减库存。库存服务执行本地扣减库存的事务操作。
      • 如果库存扣减成功,库存服务向RocketMQ发送事务提交消息,RocketMQ将该半消息标记为可消费状态,后续消费者可正常消费此消息进行后续业务处理,比如订单发货等。
      • 如果库存扣减失败,库存服务向RocketMQ发送事务回滚消息,RocketMQ删除之前发送的半消息,相当于撤销整个事务,订单状态可回滚为“未创建”或根据业务需求设为“库存不足”等相关状态。
    • 事务回查机制
      • RocketMQ会定时回查订单服务,询问半消息对应的本地事务执行状态。这是为了防止因网络等原因导致订单服务发送的事务提交或回滚消息丢失。
      • 订单服务接收到回查请求后,根据订单状态判断本地事务执行情况。如果订单状态为“创建中”,表示库存扣减操作可能未完成或失败,订单服务可再次尝试扣减库存或直接回滚订单;如果订单状态为“已创建(库存扣减成功)”,则向RocketMQ返回事务提交状态;如果订单状态为“已回滚”,则向RocketMQ返回事务回滚状态。
  2. 涉及到的RocketMQ相关操作
    • 发送半消息
      • 使用RocketMQ的事务消息发送接口,在发送消息时设置为事务消息模式。例如在Java中,使用如下代码(简化示例):
TransactionMQProducer producer = new TransactionMQProducer("groupName");
producer.setTransactionListener(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // 执行订单创建本地事务
        return LocalTransactionState.UNKNOW;
    }
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        // 处理事务回查
        return LocalTransactionState.COMMIT_MESSAGE;
    }
});
producer.start();
Message message = new Message("topic", "tags", "订单创建消息".getBytes());
SendResult sendResult = producer.sendMessageInTransaction(message, null);
  • 事务提交
    • 库存服务在成功扣减库存后,通过RocketMQ客户端向RocketMQ发送事务提交消息,通知RocketMQ该事务可以提交,示例代码如下(简化示例):
producer.endTransaction(sendResult.getLocalTransactionId(), LocalTransactionState.COMMIT_MESSAGE, null);
  • 事务回滚
    • 库存服务在库存扣减失败时,通过RocketMQ客户端向RocketMQ发送事务回滚消息,通知RocketMQ撤销该事务,示例代码如下(简化示例):
producer.endTransaction(sendResult.getLocalTransactionId(), LocalTransactionState.ROLLBACK_MESSAGE, null);
  • 事务回查处理
    • 在订单服务实现TransactionListener接口的checkLocalTransaction方法,在该方法中根据订单状态处理事务回查请求,返回正确的事务状态给RocketMQ。如上述代码中的checkLocalTransaction方法实现。