php中文网

跨多个服务的事务操作是一种疯狂的方法

php中文网

团队在微服务环境中必须处理的众多复杂问题之一就是事务。跨越多个微服务的事务。与单体应用程序不同,单体应用程序的事务通常使用单个数据库和 @transactional
进行管理 注解,在微服务中,每个服务往往都有自己的数据库,使得分布式事务变得更加复杂。这是有关如何在 spring boot 中有效处理这些分布式事务的指南。

首先,让我们先就什么是交易达成一致。

事务是计算或数据库环境中的一个工作单元,被视为单个不可分割的操作。它代表一系列必须一起成功或一起失败的操作或步骤,即使在发生意外事件(例如断电或网络故障)时也能确保数据的一致性和完整性。

在数据库上下文中,事务可能涉及多个查询,例如创建、更新或删除记录。交易通常遵循四个基本属性,称为 acid 属性:

a. 原子性 - 事务中的所有操作都被视为单个单元。要么所有操作成功,要么全部失败。

b. 一致性 - 事务将系统从一种有效状态转移到另一种有效状态,从而保持数据有效性。

c. 隔离 - 事务是隔离执行的,这意味着中间状态对其他事务不可见。

d. 持久性 - 事务一旦提交,其更改就是永久性的,并且在系统崩溃时也能幸存。


一个短篇故事

在一个繁忙的电子商务应用程序中,想象一下客户 alice 订购了一台新笔记本电脑、一个配件和快递。以下是她的订单如何在由 ordersagaorchestrator 管理的系统中流动的幕后故事。

在一个繁忙的电子商务应用程序中,想象一下客户 alice 订购了一台新笔记本电脑、一个配件和快递。以下是她的订单如何在由 ordersagaorchestrator 管理的系统中流动的幕后故事。

alice 输入付款和送货信息后点击“立即订购”。此操作启动了一个称为传奇的流程,这是一系列精心策划的交易,以确保她的订单从头到尾得到正确处理。

第 1 步:付款处理
saga 编排器首先检查 paymentservice,发起调用以从 alice 的帐户中扣除所需的金额。调用 paymentservice.processpayment() 方法,授权 alice 付款。

第2步:库存预订
一旦付款成功,协调器就会转移到 inventoryservice,在那里为 alice 保留特定的笔记本电脑型号和配件。此预订步骤至关重要,以便在订单仍在处理期间,库存不会售完或交给其他客户。

第 3 步:发货启动
成功预订库存后,saga 协调器将联系 shippingservice。在这里,shippingservice.initiateshipping() 启动物流,确保物品包装好并准备好运送到 alice 的地址。

处理失败:补偿逻辑

但是在分布式环境中,任何一步都可能出错。如果由于物流错误导致发货启动失败,或者由于库存差异而实际上无法履行库存怎么办?协调器已准备好补偿策略。

如果抛出异常,协调器将启动补偿事务以回滚整个过程,因此 alice 不会为她不会收到的物品付费:

3.1。取消发货 - 协调器调用shippingservice.cancelshipping(),停止发货。

3.2。释放库存 - 然后触发 inventoryservice.releaseinventory(),释放 alice 的保留物品,以便其他客户可以购买它们。

3.3。退款 - 最后,它调用 paymentservice.refund() 来退还 alice 的付款,确保她不会为订单付费。

最后,这个精心策划的传奇确保了 alice 的体验流畅且一致,如果出现任何问题,都会以维护系统完整性的方式解决。这就是微服务中分布式事务和补偿逻辑的魔力。


现在我们知道了什么是事务并了解了事务可能有用的现实场景,让我们深入研究如何在分布式环境中实现此功能。

团队可以使用一些关键方法来解决这个问题

1。 saga 模式: saga 模式是微服务架构中处理分布式事务最广泛使用的模式之一。传奇是每个服务独立执行的本地事务序列。 saga 中的每一步都会通过一个操作来补偿,如果 saga 失败,该操作会撤消该步骤。

saga 模式可以通过两种主要方式实现:

  1. a。 基于编排的 saga: 事务中涉及的每个服务都会侦听事件并执行其事务。完成后,它会发出一个事件来触发传奇中的下一步。如果某个步骤失败,则会触发补偿事件以撤消之前的步骤。

  2. b。 基于编排的 saga: 集中式服务(saga 编排器)协调 saga 的步骤。它确定操作顺序并管理发生故障时的补偿。

2。两阶段提交(2pc): 虽然两阶段提交协议通常用于单体系统,但它可以通过 atomikos 或 bitronix 等分布式事务管理器跨分布式系统使用。但是,我不推荐这种方法,因为它在微服务上下文中存在一些限制,因为它会导致高延迟并且容错能力较差。如果我是你,我通常会避免这种方法,而选择 saga 模式。

3。事件驱动架构: 使用事件驱动方法,服务通过事件进行通信,特别适合处理分布式事务。这种方法与 saga 模式非常吻合。每个服务独立执行其事务,然后发出一个事件以通知其他服务有关结果。这些事件可以使用 apache kafka、rabbitmq 或其他消息代理来处理。

现在,让我们看看它在代码中是如何工作的。

saga 模式有多种风格,但在本文中,我将尝试在 spring boot 中实现基于编排的 saga 模式:

第 1 步: 定义 saga orchestrator:
在这里,我将创建一个简单的服务来充当协调器,负责协调事务。

该服务将定义传奇的流程,以正确的顺序调用每个服务,并在需要时处理补偿事务。

@service
public class ordersagaorchestrator {

    @autowired
    private paymentservice paymentservice;

    @autowired
    private inventoryservice inventoryservice;

    @autowired
    private shippingservice shippingservice;

    public void createordersaga(order order) {
        try {
            paymentservice.processpayment(order.getpaymentdetails());
            inventoryservice.reserveinventory(order.getitems());
            shippingservice.initiateshipping(order.getshippingdetails());
        } catch (exception e) {
            // compensation logic
            shippingservice.cancelshipping(order.getshippingid());
            inventoryservice.releaseinventory(order.getitems());
            paymentservice.refund(order.getpaymentid());
        }
    }
}

第 2 步: 在每个服务中创建本地事务和补偿方法:

每个服务都应该有自己的事务来完成其在传奇中的步骤,并在需要时使用另一个事务来补偿它。这是大概的结构。

@service
public class paymentservice {

    @transactional
    public void processpayment(paymentdetails details) {
        // perform payment logic
    }

    @transactional
    public void refund(string paymentid) {
        // perform refund logic
    }
}

第 3 步: 基于事件的通信(可选,用于编排):每个服务都可以发出事件来通知其他人交易的结果。

public class paymentservice {

    private final applicationeventpublisher eventpublisher;

    public paymentservice(applicationeventpublisher eventpublisher) {
        this.eventpublisher = eventpublisher;
    }

    public void processpayment(paymentdetails details) {
        // process payment
        eventpublisher.publishevent(new paymentprocessedevent(this, details));
    }
}

第 4 步: 采取措施保证数据一致性:使用幂等性检查来确保 saga 中的每个步骤仅执行一次。这在分布式系统中很重要,因为网络故障或重试可能会导致重复请求。

第 5 步: 使用消息代理来提高可靠性:如果您使用事件来管理传奇,可以使用像 rabbitmq 的 kafka 这样的消息代理持久性,并且可以在服务暂时不可用时缓冲事件。

第 6 步: 错误处理和重试: 将错误处理和重试逻辑合并到您的协调器和各个服务中以处理临时故障。 spring retry 在这里很有用,因为它可以在可配置策略中自动重试失败的操作。

@Retryable(value = {RemoteServiceException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000))
public void reserveInventory(List<Item> items) {
    // Attempt to reserve inventory
}

结论

微服务中的分布式事务具有挑战性,但通过使用 saga(尤其是编排)和事件驱动通信等模式,您可以实现可靠且可扩展的解决方案。

spring boot 通过提供对事务管理、事件发布以及与消息代理集成的支持,使这一切变得更容易。

最后,这个精心策划的传奇确保了 alice 的体验流畅且一致,如果出现任何问题,都会以维护系统完整性的方式解决。这就是微服务中分布式事务和补偿逻辑的魔力。

以上就是跨多个服务的事务操作是一种疯狂的方法的详细内容,更多请关注php中文网其它相关文章!