【轻松上手 RocketMQ 专栏】事务消息与幂等性

作者:微信小助手

发布时间:2021-10-07T04:05:54

这一讲,我们结合业务场景来讲讲事务消息以及消息的幂等性。

1、业务场景

在电商场景中,一般付款成功后,会给用户发放优惠券,一来给用户优惠,二来激励用户继续消费。

上面的场景:在电商系统中,会出现付款成功后、准备发优惠券的时候,服务器宕机了。这个时候会造成用户成功付款,却没收到优惠券的情况。这种情况下,我们很容易想到用事务来保证付款和发优惠券的原子性即可:要么付款和发优惠券同时成功,要么同时失败,是不允许其他一个成功,另一个失败的。

但上面,存在一种情况:付款和发优惠券高度耦合,这样子容易出现:发优惠券一直失败,会导致付款一直失败的场景。

对于这种场景的解决方案:引入消息中间件MQ来解耦。

  • 1、支付订单
  • 2、发送支付消息
  • 3、消费支付消息
  • 4、发放优惠券

上面这个是发放优惠券的流程图。

但是上述流程中,存在MQ不可用、消息重复的异常情况,进而导致:

  • 产生付款成功,发优惠券失败
  • 优惠券重复发放

怎样才能确保付款成功后进行优惠券发放并且不会重复呢?这需要引入事务消息和幂等性处理。

2、事务消息

首先我们来了解下事务消息。分布式事务是一种抽象的概念。

那具体的实现呢?

是有很多种实现的。

在这里,主要介绍:RocketMQ的事务消息。

事务消息的流程图

流程步骤:
  • 1、生产者发送half消息
  • 2、MQ回复ACK确认消息
  • 3、执行本地事务:订单付款。如果订单付款成功,那么就给MQ发送commit消息。如果订单付款失败,就发送rollback消息
  • 4、如果步骤3发送消息失败,这个时候MQ的定时器会检查half消息。MQ回调方法,去检查本地事务的执行情况。如果执行成功,就返回commit消息。如果执行失败,就返回rollback消息。
  • 5、如果MQ收到的是commit消息,此时会把half消息复制到真正的topic中
  • 6、消费者对消息进行消费,下发优惠券

3、如何使用事务消息

上面,大概知道了事务消息的流程。

接下来,要知道如何使用。

还是以付款下发优惠券为例。

3.1 发送half消息-MQ回复ACK确认消息


 @Override
    public void finishedOrder(String orderNo, String phoneNumber) {
        
        try {
          // 退房事务消息,topic:完成订单
        Message msg = new Message(orderFinishedTopic, JSON.toJSONString(orderInfo).getBytes(StandardCharsets.UTF_8));
        
            // 发送half消息
            TransactionSendResult transactionSendResult = orderFinishedTransactionMqProducer.sendMessageInTransaction(msg, null);
            
        } catch (MQClientException e) {
           
        }

    }

3.2 执行本地事务:付款

@Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
      
        try {