当前位置: 首页 > news >正文

基于本地消息表实现分布式事务

假设我们有一个电商系统,包含订单服务和库存服务。当用户下单时,需要在订单服务中创建订单,同时在库存服务中扣减库存。这是一个典型的分布式事务场景,我们需要保证这两个操作要么都成功,要么都失败,以保证数据的最终一致性。

项目结构:

  1. 订单服务(Order Service)
  2. 库存服务(Inventory Service)
  3. 本地消息表(Local Message Table)
  4. 消息恢复系统(Message Recovery System)

核心思想:
使用本地消息表来实现分布式事务。在订单服务中,我们将创建订单和发送消息这两个操作放在一个本地事务中。如果本地事务成功,则订单创建成功,消息也被保存到本地消息表中。然后通过定时任务或消息队列来发送消息到库存服务,实现库存扣减。如果在这个过程中出现任何异常,我们可以通过重试机制来保证最终一致性。

下面是详细的代码实现:

订单服务(Order Service)

@Service  
@Transactional  
public class OrderService {  @Autowired  private OrderRepository orderRepository;  @Autowired  private LocalMessageRepository localMessageRepository;  @Autowired  private KafkaTemplate<String, String> kafkaTemplate;  public void createOrder(Order order) {  // 开启本地事务  TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());  try {  // 1. 保存订单  orderRepository.save(order);  // 2. 创建本地消息  LocalMessage message = new LocalMessage();  message.setMessageId(UUID.randomUUID().toString());  message.setMessage(JSON.toJSONString(order));  message.setStatus("NEW");  localMessageRepository.save(message);  // 3. 提交事务  transactionManager.commit(txStatus);  // 4. 发送消息到Kafka  kafkaTemplate.send("inventory-topic", message.getMessageId(), message.getMessage());  } catch (Exception e) {  // 回滚事务  transactionManager.rollback(txStatus);  throw new RuntimeException("Create order failed", e);  }  }  
}

库存服务(Inventory Service)

@Service  
public class InventoryService {  @Autowired  private InventoryRepository inventoryRepository;  @KafkaListener(topics = "inventory-topic")  public void handleOrderCreation(ConsumerRecord<String, String> record) {  String messageId = record.key();  Order order = JSON.parseObject(record.value(), Order.class);  try {  // 扣减库存  inventoryRepository.decreaseStock(order.getProductId(), order.getQuantity());  // 确认消息处理成功  kafkaTemplate.send("inventory-result-topic", messageId, "SUCCESS");  } catch (Exception e) {  // 消息处理失败,发送失败消息  kafkaTemplate.send("inventory-result-topic", messageId, "FAILED");  }  }  
}

本地消息表(Local Message Table)

@Entity  
@Table(name = "local_message")  
public class LocalMessage {  @Id  private String messageId;  private String message;  private String status; // NEW, SENT, CONFIRMED  private Date createTime;  private Date updateTime;  // Getters and setters  
}

消息恢复系统(Message Recovery System)

@Component  
public class MessageRecoverySystem {  @Autowired  private LocalMessageRepository localMessageRepository;  @Autowired  private KafkaTemplate<String, String> kafkaTemplate;  @Scheduled(fixedRate = 60000) // 每分钟执行一次  public void recoverFailedMessages() {  List<LocalMessage> failedMessages = localMessageRepository.findByStatusAndCreateTimeBefore("NEW", new Date(System.currentTimeMillis() - 300000)); // 5分钟前的消息  for (LocalMessage message : failedMessages) {  try {  kafkaTemplate.send("inventory-topic", message.getMessageId(), message.getMessage());  message.setStatus("SENT");  localMessageRepository.save(message);  } catch (Exception e) {  // 记录日志,等待下次重试  log.error("Failed to recover message: " + message.getMessageId(), e);  }  }  }  @KafkaListener(topics = "inventory-result-topic")  public void handleInventoryResult(ConsumerRecord<String, String> record) {  String messageId = record.key();  String result = record.value();  LocalMessage message = localMessageRepository.findById(messageId).orElse(null);  if (message != null) {  if ("SUCCESS".equals(result)) {  message.setStatus("CONFIRMED");  } else {  message.setStatus("FAILED");  }  localMessageRepository.save(message);  }  }  
}

代码说明:

  1. 订单服务:
    • 在一个本地事务中完成订单创建和本地消息保存。
    • 事务成功后,立即发送消息到Kafka。
  2. 库存服务:
    • 监听Kafka消息,处理库存扣减。
    • 处理结果(成功或失败)通过Kafka反馈给订单服务。
  3. 本地消息表:
    • 存储待发送的消息,包括消息ID、内容、状态等信息。
  4. 消息恢复系统:
    • 定期检查本地消息表,重新发送失败的消息。
    • 监听库存服务的处理结果,更新本地消息状态。

项目亮点:

  1. 高可用性: 即使在网络故障或服务宕机的情况下,也能保证消息最终被成功处理。
  2. 数据一致性: 通过本地事务保证订单创建和消息发送的原子性,再通过消息重试机制保证最终一致性。
  3. 解耦性: 订单服务和库存服务通过消息进行异步通信,降低了系统耦合度。
  4. 可靠性: 使用本地消息表作为消息队列的可靠存储,避免了消息丢失的风险。
  5. 扩展性: 该方案易于扩展,可以方便地增加新的微服务而不影响现有服务。
  6. 性能: 采用异步处理方式,提高了系统的整体吞吐量。

通过这种方式,我们实现了在分布式系统中保证数据最终一致性的目标,同时保持了系统的高可用性和可扩展性。这种方案特别适用于对实时性要求不是特别高,但对数据一致性有较高要求的业务场景。


系列文章

  1. IT Governance Framework:IT治理框架
  2. 12306亿级流量架构分析(史上最全)
  3. 京东内部Redis性能优化最佳实践
  4. 金融级多数据中心灾备互联
  5. TOGAF业务架构-CSDN博客
  6. 如何建设金融数据中心-CSDN博客

资料下载和预览地址:

  • 链接: https://pan.baidu.com/s/1LFyFlsIHCv46DBQRfMGP9A 提取码: kx6b 

相关文章:

基于本地消息表实现分布式事务

假设我们有一个电商系统,包含订单服务和库存服务。当用户下单时,需要在订单服务中创建订单,同时在库存服务中扣减库存。这是一个典型的分布式事务场景,我们需要保证这两个操作要么都成功,要么都失败,以保证数据的最终一致性。 项目结构: 订单服务(Order Service)库存服务(Inv…...

Web3与加密技术的结合:增强个人隐私保护的未来趋势

随着互联网的快速发展&#xff0c;个人隐私和数据安全问题越来越受到关注。Web3作为新一代互联网架构&#xff0c;凭借其去中心化的特性&#xff0c;为个人隐私保护提供了全新的解决方案。而加密技术则是Web3的重要组成部分&#xff0c;进一步增强了隐私保护的能力。本文将探讨…...

广播网络实验

1 实验内容 1、构建星性拓扑下的广播网络,实现hub各端口的数据广播,验证网络的连通性并测试网络效率 2、构建环形拓扑网络,验证该拓扑下结点广播会产生数据包环路 2 实验流程与结果分析 2.1 实验环境 ubuntu、mininet、xterm、wireshark、iperf 2.2 实验方案与结果分析…...

Vscode——SSH连接不上的一种解决办法

一、完整报错: > @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ > IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! > Someone could be eavesdropping on you right now (man-in-the...

ChatGPT大模型极简应用开发-目录

引言 要理解 ChatGPT&#xff0c;了解其背后的 Transformer 架构和 GPT 技术一路的演进则变得非常必要。 ChatGPT 背后的 LLM 技术使普通人能够通过自然语言完成过去只能由程序员通过编程语言实现的任务&#xff0c;这是一场巨大的变革。然而&#xff0c;人类通常容易高估技术…...

EI Scopus双检索 | 2025年第四届信息与通信工程国际会议(JCICE 2025)

会议简介 Brief Introduction 2025年第四届信息与通信工程国际会议(JCICE 2025) 会议时间&#xff1a;2025年7月25日-27日 召开地点&#xff1a;中国哈尔滨 大会官网&#xff1a;www.jcice.org 由黑龙江大学和成都信息工程大学主办&#xff0c;江苏科技大学协办的2025年第四届信…...

重学SpringBoot3-Spring Retry实践

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞??收藏评论 重学SpringBoot3-Spring Retry实践 1. 简介2. 环境准备3. 使用方式 3.1 注解方式 基础使用自定义重试策略失败恢复机制重试和失败恢复效果注意事项 3.2 编程式使用3.3 监听重试过程 监…...

TiDB 和 MySQL 的关系:这两者到底有什么不同和联系?

TiDB 和 MySQL 的关系&#xff1a;这两者到底有什么不同和联系&#xff1f; 在了解 TiDB 和 MySQL 之间的关系时&#xff0c;很多人可能会有疑问&#xff1a;这两个数据库到底有什么区别和联系&#xff1f;是不是 TiDB 就是 MySQL 的升级版&#xff1f;或者 TiDB 是一种“替代…...

【Java】JDK17的下载安装(与JDK1.8相互切换)

本文以参考以下链接为主&#xff1a;JDK17 如果上述操作不生效&#xff0c;请看以下操作&#xff1a; 添加以下变量并移动到最上面即可...

CSS3 3D 转换介绍

CSS3 中的 3D 转换提供了一种在二维屏幕上呈现三维效果的方式&#xff0c;主要包括translate3d、rotate3d、scale3d等转换函数&#xff0c;下面来详细介绍&#xff1a; 1. 3D 转换的基本概念 坐标系 在 CSS3 的 3D 空间中&#xff0c;使用的是右手坐标系。X 轴是水平方向&…...

Vue3 Element-Plus el-tree 右键菜单组件

参考代码&#xff1a;实现Vue3Element-Plus(tree、table)右键菜单组件 这篇文章的代码确实能用&#xff0c;但是存在错误&#xff0c;修正后的代码&#xff1a; <template><div style"text-align: right"><el-icon size"12" color"#…...

鸿蒙学习构建视图的基本语法(二)

一、层叠布局 // 图片 本地图片和在线图片 Image(https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/080662.png) Entry Component//自适应伸缩 设置layoutWeight属性的子元素与兄弟元素 会按照权重进行分配主轴的空间// Position s…...

python-leetcode-存在重复元素 II

219. 存在重复元素 II - 力扣&#xff08;LeetCode&#xff09; class Solution:def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:seen set()for i, num in enumerate(nums):if num in seen:return Trueseen.add(num)if len(seen) > k:seen.remove…...

P6周:VGG-16算法-Pytorch实现人脸识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 我的环境 语言环境&#xff1a;Python 3.8.12 编译器&#xff1a;jupyter notebook 深度学习环境&#xff1a;torch 1.12.0cu113 一、前期准备 1.设置GPU im…...

BeanFactory 是什么?它与 ApplicationContext 有什么区别?

谈到Spring&#xff0c;那势必要讲讲容器 BeanFactory 和 ApplicationContext。 BeanFactory是什么&#xff1f; BeanFactory&#xff0c;其实就是 Spring 容器&#xff0c;用于管理和操作 Spring 容器中的 Bean。可能此时又有初学的小伙伴会问&#xff1a;Bean 是什么&#x…...

虚幻基础-1:cpu挑选(14600kf)

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 ue非常吃cpu拉满主频打开项目编写蓝图运行原因 时间长 关于压力测试 本文以14600kf为例&#xff0c;双12购入&#xff0c;7月份产。 ue非常吃cpu 经本人测试&#xff0c;ue是非常吃cpu的。 拉满主频 无论任何时间…...

多种vue前端框架介绍

学如逆水行舟&#xff0c;不进则退。 在现今的软件开发领域&#xff0c;Vue.js凭借其高效、灵活和易于上手的特性&#xff0c;成为了前端开发的热门选择。对于需要快速搭建企业级后台管理系统的开发者而言&#xff0c;使用现成的Vue后台管理系统模板无疑是一个明智之举。 本文…...

jenkins-node节点配置

一.简述&#xff1a; Jenkins有一个很强大的功能&#xff1a; 即&#xff1a;支持分布式构建(jenkins配置中叫节点(node),也被称为slave)。分布式构建通常是用来吸收额外的负载。通过动态添加额外的机器应对构建作业中的高峰期&#xff0c;或在特定操作系统或环境运行特定的构建…...

计算机网络 (50)两类密码体制

前言 计算机网络中的两类密码体制主要包括对称密钥密码体制&#xff08;也称为私钥密码体制、对称密码体制&#xff09;和公钥密码体制&#xff08;也称为非对称密码体制、公开密钥加密技术&#xff09;。 一、对称密钥密码体制 定义&#xff1a; 对称密钥密码体制是一种传…...

基于SpringBoot+Vue旅游管理系统的设计和实现(源码+文档+部署讲解)

个人名片 &#x1f525; 源码获取 | 毕设定制| 商务合作&#xff1a;《个人名片》 ⛺️心若有所向往,何惧道阻且长 文章目录 个人名片环境需要技术栈功能介绍功能说明 环境需要 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...