如何保证分布式事务的最终一致性

目录

可靠消息最终一致性方案

TCC(Try - Confirm - Cancel)补偿机制

最大努力通知方案


可靠消息最终一致性方案

  • 原理:将本地事务和发送消息放在一个本地事务中,确保消息的发送与本地业务操作要么都成功,要么都失败。然后通过消息中间件将消息可靠地投递到其他服务,其他服务消费消息并执行相应的业务操作。
  • 示例代码:以 Java 中使用 Spring Cloud Stream 和 RabbitMQ 为例。
    • 首先定义消息发送接口:

import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;

public interface OrderMessageChannel {
    String OUTPUT = "order-output";

    @Output(OrderMessageChannel.OUTPUT)
    MessageChannel orderOutput();
}

  • 发送消息的服务类:

import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

@Service
public class OrderService {
    private final OrderMessageChannel orderMessageChannel;

    public OrderService(OrderMessageChannel orderMessageChannel) {
        this.orderMessageChannel = orderMessageChannel;
    }

    public void createOrder(Order order) {
        // 这里执行本地数据库插入订单操作

        // 将订单信息发送到消息队列
        orderMessageChannel.orderOutput().send(MessageBuilder.withPayload(order).build());
    }
}

  • 消息接收处理类:

import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.stereotype.Component;

@Component
public class OrderReceiver {

    @StreamListener("order-output")
    public void handleOrder(Order order) {
        // 这里执行订单相关的其他业务操作,如扣减库存等
    }
}

TCC(Try - Confirm - Cancel)补偿机制

  • 原理:将事务分为三个阶段,Try 阶段尝试预留资源,Confirm 阶段确认提交事务,Cancel 阶段在出现问题时回滚事务,释放预留资源。
  • 示例代码:以 Java 中使用 ByteTCC 框架为例。假设存在一个账户服务,用于转账操作。
    • 定义 TCC 接口:

import com.baomidou.bytekit.core.transaction.TransactionContext;
import com.baomidou.bytekit.spring.boot.autoconfigure.tcc.TccTransaction;

public interface AccountTcc {

    @TccTransaction(confirmMethod = "confirmTransfer", cancelMethod = "cancelTransfer")
    boolean tryTransfer(TransactionContext context, String fromAccount, String toAccount, double amount);

    boolean confirmTransfer(TransactionContext context, String fromAccount, String toAccount, double amount);

    boolean cancelTransfer(TransactionContext context, String fromAccount, String toAccount, double amount);
}

  • 实现 TCC 接口:

import org.springframework.stereotype.Service;

@Service
public class AccountTccImpl implements AccountTcc {

    @Override
    public boolean tryTransfer(TransactionContext context, String fromAccount, String toAccount, double amount) {
        // 这里执行预扣减操作,例如更新账户余额为冻结状态
        return true;
    }

    @Override
    public boolean confirmTransfer(TransactionContext context, String fromAccount, String toAccount, double amount) {
        // 这里执行实际的转账操作,将冻结金额扣除并添加到目标账户
        return true;
    }

    @Override
    public boolean cancelTransfer(TransactionContext context, String fromAccount, String toAccount, double amount) {
        // 这里执行回滚操作,将冻结金额解冻
        return true;
    }
}

最大努力通知方案

  • 原理:系统 A 在完成本地事务后,通过一定的机制(如定时任务、消息队列等)多次尝试向系统 B 发送通知,系统 B 接收到通知后执行相应操作,并返回执行结果。系统 A 根据系统 B 的反馈来决定是否继续通知。
  • 示例代码:以下是一个简单的 Java 示例,使用 Spring 定时任务来实现最大努力通知。
    • 定义通知记录实体类:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class NotificationRecord {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String targetSystem;
    private String message;
    private int retryCount;
    private boolean success;

    // 省略构造函数、getter和setter
}

  • 通知服务类:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    // 假设这里有对NotificationRecord的数据库操作方法

    @Scheduled(fixedDelay = 5000) // 每隔5秒尝试一次通知
    public void sendNotifications() {
        // 查询未成功且重试次数小于一定值的通知记录
        List<NotificationRecord> records = findUnsentNotifications();
        for (NotificationRecord record : records) {
            // 发送通知到目标系统
            boolean result = sendMessageToTargetSystem(record.getTargetSystem(), record.getMessage());
            if (result) {
                // 如果通知成功,更新记录为成功状态
                record.setSuccess(true);
            } else {
                // 通知失败,增加重试次数
                record.setRetryCount(record.getRetryCount() + 1);
            }
            // 更新通知记录到数据库
            updateNotificationRecord(record);
        }
    }

    private List<NotificationRecord> findUnsentNotifications() {
        // 这里实现从数据库查询未成功且重试次数小于一定值的通知记录逻辑
        return new ArrayList<>();
    }

    private boolean sendMessageToTargetSystem(String targetSystem, String message) {
        // 这里实现发送消息到目标系统的逻辑,返回是否发送成功
        return true;
    }

    private void updateNotificationRecord(NotificationRecord record) {
        // 这里实现更新通知记录到数据库的逻辑
    }
}

以上代码只是简单示例,实际应用中需要根据具体业务场景和技术框架进行更完善的实现和调整。同时,不同的分布式事务最终一致性方案有各自的优缺点和适用场景,需要根据实际情况进行选择和组合使用。