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

ops/2024/11/1 7:10:22/

微服务设计模式 — 补偿事务模式(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 {@Autowiredprivate OrderRepository orderRepository;@Transactionalpublic 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 {@Autowiredprivate 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.ppmy.cn/ops/130066.html

相关文章

【ShuQiHere】数据科学与人工智能必备的 Python 包大全

【ShuQiHere】&#x1f4da;&#x1f4a1; 在数据科学与人工智能的世界里&#xff0c;拥有一套强大的 Python 工具包可以让您的研究和项目事半功倍。本文将详细介绍这些必备的包&#xff0c;每个包都附有用途、特点、安装方式&#xff0c;以及实际的代码示例和学习资源&#x…

Python酷库之旅-第三方库Pandas(181)

目录 一、用法精讲 836、pandas.api.types.is_file_like函数 836-1、语法 836-2、参数 836-3、功能 836-4、返回值 836-5、说明 836-6、用法 836-6-1、数据准备 836-6-2、代码示例 836-6-3、结果输出 837、pandas.api.types.is_list_like函数 837-1、语法 837-2、…

CentOS 9 Stream 上安装 Maven

CentOS 9 Stream 上安装 Maven 在 CentOS 9 Stream 上安装 Maven&#xff0c;可以按照以下步骤进行&#xff1a; 更新系统软件包&#xff1a; sudo dnf update安装 Maven&#xff1a; CentOS 9 Stream 默认的包管理器中已经包含 Maven&#xff0c;你可以直接安装&#xff1a; s…

Spring Boot 跨域解决方案

Spring Boot 跨域解决方案 引言 在 Web 应用中&#xff0c;跨域请求已经成为一个常见的问题。浏览器出于安全考虑&#xff0c;限制了不同源之间的请求&#xff0c;这种限制被称为同源策略。当我们的前端应用和后端 API 部署在不同的域名或端口下时&#xff0c;就会出现跨域问…

ELK实现加载多个配置日志文件

服务器准备3台133为ELS存储服务器&#xff0c;135为Kibana前台显示收集服务器&#xff0c;136为logstash客户端 打开136logstash配置pipelines.yml文件path.config:配置模块 打开136的logstash.yml配置文件 在136服务器上查看logstash配置文件 需要将mysql_log.conf和nginx_log…

学会定制化 Go 项目的 error,回溯错误的原因和发生位置

‍Go语言的Error处理一直被人吐槽&#xff0c;吐槽的点除了一个接一个的 if err &#xff01; nil 的判断外&#xff0c;还有人说Go的错误太原始不能像其他语言那样在抛出异常的时候的时候传一个Casue Exception 把导致异常的整个原因链串起来。 第一点确实是事实&#xff0c;但…

机器学习算法工程师笔试选择题(1)

1. 关于梯度下降的说法正确的是: A. 梯度下降法可以确保找到全局最优解。B. 随机梯度下降每次使用所有数据来更新参数。C. 批量梯度下降(Batch Gradient Descent)通常收敛更快。D. 学习率过大会导致梯度下降过程震荡。答案:D(学习率过大会导致不稳定,可能震荡或无法收敛)…

linux学习笔记 Ubuntu下的守护进程supervisor安装与多项目部署

我这里首先是在本地WSL上进行安装&#xff0c;WSL2的是ubuntu 24.04&#xff0c;之后又再正式环境的ubuntu 20.04上安装&#xff0c;再次记录一下。 1、首先安装supervisor apt install -y supervisor 2、创建配置文件 echo_supervisord_conf > /etc/supervisor/supervisor…