微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)

news2024/11/5 13:56:42

微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)

Compensating Transaction Pattern

定义

在云计算和分布式系统中,管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务,补偿事务模式Compensating Transaction Pattern)是一种允许在分布式系统中处理长时间运行的跨多个服务的事务一致性的方法。在执行主要事务步骤时,系统记录每个步骤的补偿操作(即回滚操作),以便在事务失败时可以撤销已执行的操作。简而言之,补偿事务通过逆向操作来确保系统达到一致性状态。

结构

补偿事务模式通常由以下几个部分组成:

  1. 主事务:主要事务逻辑,包含一系列需要执行的业务步骤。

  2. 补偿操作:用于撤销主事务中的某个步骤,如果该步骤失败则触发补偿操作。

  3. 事务管理器:负责协调事务步骤和补偿步骤的执行。

工作原理

补偿事务的工作原理如下:

  1. 执行主事务步骤:按照预定的业务逻辑,依次执行每个操作步骤。
  2. 记录补偿操作:在每个步骤成功后,记录对应的补偿操作,以备将来可能的回滚。
  3. 检测失败:在每个步骤执行期间,检测到失败时,立即执行已记录的补偿操作,撤销此前已完成的步骤。
  4. 成功完成:所有步骤成功后,事务完成;否则,执行完整的补偿逻辑,确保系统状态回滚至初始状态。

好处

  1. 高可用性:即使某些服务暂时不可用,补偿事务模式也能确保其他步骤的事务完成并且系统保持一致性。

  2. 灵活性:补偿操作提供了更多的控制和灵活性,可以根据业务逻辑定制补偿步骤。

  3. 低耦合性:通过分离主事务和补偿事务,可以降低服务之间的耦合性。

  4. 更好的性能:相比于两阶段提交,补偿事务模式更高效,不需要在所有资源上保持锁定,更适用于需要灵活性和容忍短暂不一致性的分布式系统,而两阶段提交则适用于需要强一致性和事务原子性的关键性场景。

应用场景

在微服务架构中,补偿事务模式广泛用于确保跨多个服务的长时间运行操作之间的一致性。以下是一些常见的应用场景:

  1. 订单处理系统:处理跨多个服务的订单,如支付、库存扣减和物流等步骤。
  2. 银行转账系统:处理跨多个银行账户的转账操作,如扣款、汇入和通知等步骤。
  3. 旅游预订系统:处理酒店预订、航班预订和租车预订等多个步骤。

订单处理系统为例,演示如何使用补偿事务模式。假设订单处理系统提供如下服务:

  • 服务1:支付服务
  • 服务2:库存扣减服务
  • 服务3:物流预订服务

每个服务分别执行其操作,并在失败时触发补偿操作,具体流程如下:

成功
成功
成功
失败
失败
失败
开始处理订单
执行支付操作
执行库存扣减
执行物流预订
订单处理成功
执行支付补偿
订单处理失败
执行库存补偿
执行物流补偿

示例代码

以下是一个简单的示例代码,在 Spring Boot 中实现补偿事务,并没有引入特别的事务补偿框架或者库。

项目结构

compensating-transaction/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   ├── com/
│   │   │   │   ├── example/
│   │   │   │   │   ├── controller/
│   │   │   │   │   ├── service/
│   │   │   │   │   ├── model/
│   │   │   │   │   ├── repository/
│   │   │   │   │   ├── CompensatingTransactionApplication.java
│   ├── resources/
│   │   ├── application.properties

配置文件

application.properties

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

主类

CompensatingTransactionApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CompensatingTransactionApplication {

    public static void main(String[] args) {
        SpringApplication.run(CompensatingTransactionApplication.class, args);
    }
}

模型类

model/Order.java

package com.example.model;

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

@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String status;
  
    // Getters and setters
}

仓储接口

repository/OrderRepository.java

package com.example.repository;

import com.example.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
}

服务类

service/OrderService.java

package com.example.service;

import com.example.model.Order;
import com.example.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void processOrder() {
        // 执行支付操作
        try {
            performPayment();
        } catch (Exception e) {
            performPaymentCompensation();
            return;
        }

        // 执行库存扣减
        try {
            performInventory();
        } catch (Exception e) {
            performInventoryCompensation();
            performPaymentCompensation();
            return;
        }

        // 执行物流预订
        try {
            performShipping();
        } catch (Exception e) {
            performShippingCompensation();
            performInventoryCompensation();
            performPaymentCompensation();
            return;
        }
    }

    private void performPayment() throws Exception {
        // 模拟支付操作
        Order order = new Order();
        order.setStatus("PAYMENT_SUCCESS");
        orderRepository.save(order);
        // 如果支付失败,抛出异常
    }

    private void performPaymentCompensation() {
        // 模拟支付补偿操作
        System.out.println("Payment compensation executed.");
    }

    private void performInventory() throws Exception {
        // 模拟库存扣减
        Order order = new Order();
        order.setStatus("INVENTORY_SUCCESS");
        orderRepository.save(order);
        // 如果库存扣减失败,抛出异常
    }

    private void performInventoryCompensation() {
        // 模拟库存扣减补偿操作
        System.out.println("Inventory compensation executed.");
    }

    private void performShipping() throws Exception {
        // 模拟物流预订
        Order order = new Order();
        order.setStatus("SHIPPING_SUCCESS");
        orderRepository.save(order);
        // 如果物流预订失败,抛出异常
    }

    private void performShippingCompensation() {
        // 模拟物流预订补偿操作
        System.out.println("Shipping compensation executed.");
    }
}

控制器类

controller/OrderController.java

package com.example.controller;

import com.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/process")
    public String processOrder() {
        orderService.processOrder();
        return "Order processed.";
    }
}

问题和考虑

以上代码只是理想情况下的一种的简化,主要是借此来说明一下补偿机制的主要思想。尽管补偿事务模式提供了有效的方法保证分布式事务的一致性,但是在设计和实现过程中仍然需要考虑以下问题:

  1. 补偿逻辑的冗余:需要为每个操作设定相应的补偿操作,这可能增加代码的复杂性和维护成本。

  2. 不完全回滚:不是所有业务操作都能被完全回滚,设计补偿操作时需谨慎对待,补偿事务不一定总是成功,应该使补偿步骤具备幂等能力,这样即使补偿事务失败,也可以被安全地重复执行。

  3. 最终一致性:补偿事务模式强调的是最终一致性,而不是强一致性,需要根据具体业务需求权衡。

  4. 补偿逻辑的专一性:补偿逻辑难以通用化,因为它是特定于应用程序的。应用程序需要足够的信息,才能成功撤销失败操作的每一步。

在实际项目开发中,一般需要有效地结合补偿事务模式和重试模式,提高系统的可靠性并减少事务失败的影响。以下是一些具体的建议:

  1. 优先使用重试模式:

    • 识别瞬态故障:分辨出哪些故障是暂时性的(如网络波动、暂时的资源不可用)并优先对这些故障应用重试模式。
    • 设置重试策略:配置合适的重试策略,包括重试次数、重试间隔和指数退避等,以确保重试时不会对系统造成过大负载。
  2. 设置明确的重试限度:

    • 重试次数限制:为每个操作设置重试的最大次数。如果重试次数超过此限度,则不再尝试重试,转而启动补偿事务。
    • 超时机制:设置合理的超时机制以防止操作长时间挂起。一旦触发超时,系统应立即停止重试并启动补偿事务。
  3. 集成补偿事务模式:

    • 捕获所有重试失败:确保所有重试失败的情况都会被准确捕获并且能有效地启动补偿事务。
    • 确保持久性:记录每一步操作及其状态,以便在重试多次失败后能够在补偿事务中撤销这些操作。
  4. 补偿事务步骤的幂等性:

    • 确保幂等性:补偿事务的步骤必须是幂等的,即使被多次执行也不会对系统状态产生额外影响。这确保如果补偿事务执行过程中出现故障,可以安心地再次执行同样的补偿步骤。
  5. 设计良好的事务边界:

    • 明确的事务边界:清晰地定义事务的开始和结束,确保每个事务都是一个原子操作。尽量减少跨多个服务或数据存储的长事务,减少事务失败的复杂度。

    • 资源锁定和管理:按需锁定资源并在补偿事务中优先释放资源,以防止资源长时间被占用导致其他操作受阻。

总结

cloud-native-definition-2

补偿事务模式是一种非常有效的方法,用于处理分布式系统中长时间运行事务的一致性问题。通过在主事务执行失败时执行补偿操作,系统能够恢复到一致性状态。尽管这一设计模式涉及较高的复杂性和代码冗余,但其在保证系统一致性和稳定性方面的优势是不可忽视的。在实际应用中,补偿事务模式广泛用于订单处理、银行转账等需要跨多个服务协调的业务场景。通过本文的示例,希望读者能够更好地理解和应用补偿事务模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2231137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

echart实现地图数据可视化

文章目录 [TOC](文章目录) 前言一、基本地图展示2.数据可视化 总结 前言 最近工作安排使用echarts来制作图形报表&#xff0c;记录一下我的步骤&#xff0c;需求呈现一个地图&#xff0c;地图显示标签&#xff0c;根据业务指标值给地图不同省市填充不同颜色&#xff0c;鼠标放…

华为自研仓颉编程语言官网上线 首个公测版本开放下载

仓颉编程语言官网正式公开上线&#xff0c;同时首个公测版本开放下载。本次仓颉编程语言官网上线了首页、在线体验、文档、学习、下载、动态以及三方库共六个模块&#xff0c;可供开发和学习和体验。 据悉&#xff0c;仓颉编程语言是在今年6月的华为开发者大会上正式公布&…

2024 网鼎杯 CTF --- Crypto wp

文章目录 青龙组Crypto1Crypto2 白虎组Crypto1Crypto2 朱雀组Crypto2Crypto3part1part2part3part4 青龙组 Crypto1 题目&#xff1a; from Crypto.Util.number import * from secret import flagp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,…

java并发编程-volatile的作用

文章目录 volatile的作用1.改变线程间的变量可见性2.禁止指令重排序 参考的学习视频 volatile的作用 1.改变线程间的变量可见性 每个线程都有一个专用的工作集内存&#xff0c;下图里面粉色的表示专用工作集内存&#xff0c;黄色的是共享内存工作区&#xff0c;如果加入了vol…

目前最新最好用 NET 混淆工具 .NET Reactor V6.9.8

目前最新最好用 NET 混淆工具 .NET Reactor V6.9.8 1、.NET Reactor V6.9.8 功能简介2、官方下载 1、.NET Reactor V6.9.8 功能简介 业界领先的源代码保护 .NET Reactor通过多种方法来防止反编译&#xff0c;这些方法会将 .NET 程序集转换为任何现有工具都无法反编译的进程。…

计算机性能分析的三个模型

计算机性能分析的三个模型【1】 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09;二、利特尔法则&#xff08;Littles Law&#xff09;【2】三、M/M/1 QueueReference 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09; 瓶颈分析可以帮我们更好地定位导致性能…

2025四川省考报名流程详细教程

2025年四川省考报名马上就要开始了&#xff0c;有想要参加四川省考的姐妹们&#xff0c;可以提前了解一下考试报名流程&#xff0c;提前准备好报名照片。 报名时间&#xff1a;2024年11月1日至7日上午8:00 审核时间&#xff1a;2024年11月1日至8日上午8:00 缴费时间&#xff1a…

Ts基础总结

文章目录 TS是什么&#xff1f;Ts编译Ts编译选项:如何在项目中使用Ts?为什么要使用 TS ? TypeScript 相对于 JavaScript 的优势是什么&#xff1f;ts 中有哪些类型&#xff1f;any 和 unknown的区别是&#xff1f;void 和 never 的区别是&#xff1f;TypeScript中的访问修饰符…

ImportError: Install xlrd >= 1.0.0 for Excel support

文章目录 一、报错问题二、问题解释三、解决方法 一、报错问题 问题描述&#xff1a; python2.7使用pandas读取excel文件时报错ImportError: Install xlrd > 1.0.0 for Excel support。 问题代码&#xff1a; # codingutf-8import pandas as pddata pd.read_excel(D:\Wo…

算法学习(七)—— 分治

关于分治 分治&#xff0c;就是“分而治之”的意思&#xff0c;就是把一个大问题&#xff0c;转化为若干个相同或者相似的几个子问题&#xff0c;然后在子问题的基础上再进行划分&#xff0c;直到能够快速一个子问题时停止划分 我们的快速排序和归并排序就是典型的分治思想 …

2-141 怎么实现ROI-CS压缩感知核磁成像

怎么实现ROI-CS压缩感知核磁成像&#xff0c;这个案例告诉你。基于matlab的ROI-CS压缩感知核磁成像。ROI指在图像中预先定义的特定区域或区域集合&#xff0c;选择感兴趣的区域&#xff0c;通过减少信号重建所需的数据来缩短信号采样时间&#xff0c;减少计算量&#xff0c;并在…

C++ 实现俄罗斯方块游戏

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

VS+Qt解决提升控件后,包含头文件格式不对问题处理

一、前言 VSQt 提升控件后&#xff0c;在uic目录下会生成ui相关的初始化文件&#xff0c;对于提升的控件头文件包含的格式为#include<> 而非 #include “ ” 导致无法找到头文件。如果手动修改为 #include “ ”相当麻烦&#xff0c;甚至每次编译都要修改一遍&#xff0c…

02- 模块化编程-002 DS1302数码显示时间与日期

1、数码显示时间日期的电路 2、电路原理简介 电路组件与功能 单片机&#xff08; PIC16F887&#xff09;&#xff1a; 作为系统的主控芯片&#xff0c;处理所有输入输出&#xff0c;进行时间控制和显示信息更新。 DS1302&#xff08;实时时钟芯片&#xff09;&#xff1a; 用于…

java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; 资源获取方式再最下方 java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频) Java聊天室系统是一个基于Java语言开发的在线即时通讯平台&#xff0c;旨在为用户提供一个简单、易用的实时交流环境。该系统支持多用户同时在线交流&…

编译原理第一次实验报告

源代码及附件&#xff1a;编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计 前两部分指出了实验的宏观把控&#xff0c;为了具体实施实验&#xff0c;我们需要预先为实验做出如下设计&#xff1a; 本次实验我选取了C语言的一个子集进行设计词法分析器&…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…

C++_day01

目录 0. 课前须知 1. C发展历史&#xff08;了解&#xff09; 2. C特点&#xff08;熟悉&#xff09; 3. 面向对象核心术语&#xff08;熟悉&#xff09; 4. 开发环境 5. 新建项目 4. 开发环境 5. 新建项目 0. 课前须知 C的思维与C语言完全不同&#xff0c;不能生搬硬套。 C偏向…

安娜的档案(Anna’s Archive) 镜像网站/国内最新可访问入口(持续更新)

安娜的档案&#xff08;Anna’s Archive&#xff09;是一个颇受关注的资源库。它涵盖了广泛的内容&#xff0c;可能包括各类文献、资料等。其特色在于丰富的信息储备和一定的系统性。安娜的档案&#xff08;Anna’s Archive&#xff09;用户可以从中获取多样的知识和数据&#…

Linux 下执行定时任务之 Systemd Timers

不知道 ECS 因为什么缘故&#xff0c;上面安装的 MySQL 服务老是不定期挂掉&#xff0c;本来想通过 Linux 得 Cron 配置个半小时的定时检测任务&#xff0c;结果一直没有执行&#xff0c;因此又尝试使用了 Systemd Timers 进行了重新配置&#xff0c;简要做个记录。 Systemd Ti…