分布式事务-使用队列实现最终一致性

分布式事务-扣减库存

一、最终一致性架构图

1、服务

左侧:创建订单服务Server1

右侧:扣减库存服务Server2

中间:独立消息服务Server3

2、中间件:

Kafka-MQ、MySQL-db


二、步骤

0、定义MQ,三个状态

  • prepare
  • confirm
  • ack

1、Server1

  • 生成单据号
  • 调用Server3#updateMQ接口,状态为prepare

2、Server1

  • 下单

3+5、Server1

  • 下单结束后,调用Server3#updateMQ接口,状态为confirm

4、Server3

  • 定时任务,捞取db中所有单据号(单据号id、消息id、业务类型、消息状态、消息体)

  • 遍历所有状态为prepare的单据号

    • 查询server1中#queryOrder接口

      • 如果没有用查询到订单信息,说明server1服务下单流程失败了,没有建单

        此时,直接删除db中此无用单据信息

      • 如果查询到了订单信息,说明server1服务的confirm阶段失败了

        此时,将db中此单据id对应的消息状态,更新为confirm、并sendMQ到消息队列

6、单据id对应的消息状态为confirm的MQ,被发送到MQ中了

7、server2

扣减库存服务,监听到MQ后,消费MQ

  • 扣减库存成功,则ACK

    • 调用server3#updateMQ接口,将单据id的状态改为ACK
  • 扣减库存成功,则Kafka有重试机制,也有死信机制,保障消费一定成功


三、代码实现

server3服务

1)updateMQ接口入参定义

java 复制代码
public class Message implements Serializable {
    private String orderId;
    /**
     * 业务类型:1-优选订单、2-买菜订单
     */
    private Integer businessType;

    /**
     * MQ状态:1-prepare、2-confirm、3-ack
     */
    private Integer status;
    
    private BizData bizData;
}

2)updateMQ方法内容

java 复制代码
    @Transactional
    public void updateMQ(Message mq) {
        Integer status = mq.getStatus();
        if (status == 1) {
            // saveDb
        } else if (status == 2) {
            // sendMQ2Kafka
            // updateDb(将单据状态改为2)
        } else if (status == 3) {
            // updateDb(将单据状态改为3)
        }
    }

3)定时任务

java 复制代码
    @Scheduled(cron = "0 * * * * *")
    public void craneTask() {
        // 1、拉去db
        // 2、获取状态为prepare=1的单据
        // 3.查看server1服务查询订单接口
            // 3.1 订单不存在,则删除db此条数据
            // 3.2 订单存在,  则更新db此条数据状态为confirm、然后sendMQ2Kafka
    }
server1
  • 提供查询订单接口服务
  • 在步骤1,创建订单之前时,调用server3#updateMQ接口,入参状态为prepare=1
  • 在步骤3和5,创建订单之后时,调用server3#updateMQ接口,入参状态为confirm=2
server2服务
  • 监听kafka的消息,消费,扣减库存即可
    erver3#updateMQ接口,入参状态为prepare=1
  • 在步骤3和5,创建订单之后时,调用server3#updateMQ接口,入参状态为confirm=2
server2服务
  • 监听kafka的消息,消费,扣减库存即可
    • 消费消息成功后,调用updateMQ,入参状态为ACK=3
相关推荐
码农飞哥4 天前
RocketMQ消费接口设计实战:为什么HTTP回调接口必须吞掉所有异常,始终返回成功?
网络协议·http·中间件·消息队列·rocketmq
小马爱打代码6 天前
事务处理最佳实践:单库 → 跨库 → 跨应用
分布式事务
byte轻骑兵7 天前
【LE Audio】CAP缩略词一本通:54个核心术语解码,协议入门快车道
人机交互·cap·le audio·蓝牙音频·低功耗音频
小小工匠7 天前
Redis - 缓存与数据库一致性:问题分析与解决方案
redis·缓存·性能优化·消息队列·并发
闪电悠米7 天前
黑马点评-Redis 消息队列-01_why_redis_mq
java·数据库·spring boot·redis·缓存·junit·消息队列
Fcy6488 天前
Linux下 进程间通信详解(二)System V IPC
linux·运维·消息队列·共享内存·信号量·system v
zz0723208 天前
深入理解Seata:微服务分布式事务解决方案
微服务·seata·分布式事务
Jabes.yang10 天前
Java电商订单系统面试全流程解析:接口设计、数据库、微服务与分布式事务实战
java·微服务·mybatis·分布式事务·电商·订单系统·接口设计
byte轻骑兵12 天前
【LE Audio】CAP精讲[13]: Central侧LE连接建立全流程解析
人工智能·音视频·cap·le audio·低功耗音频
Tenifs13 天前
深入对比分析 RabbitMQ、RocketMQ 和 Kafka
后端·kafka·消息队列·rabbitmq·rocketmq·爱编程的阿彬