《Seata从入门到实战》第四章:Saga模式详解

本期内容为自己总结归档,欢迎评论区指正~

第一章:Seata核心概念与架构概览

第二章:AT 模式详解

第三章:TCC模式详解

第四章:Saga模式详解

第五章:XA模式详解

第六章:seata从部署到集成实战&避坑指南

第七章:seata总结

点赞关注哦~

第四章:Saga模式详解

4.1 Saga模式的核心思想与设计哲学

4.1.1 从长事务挑战到Saga的演进

在分布式系统中,长事务(Long-Running Transaction) 一直是一个难以解决的挑战。传统的ACID事务模型要求资源在整个事务期间保持锁定,这对于需要数分钟、数小时甚至数天才能完成的业务流程是完全不可行的。Saga模式正是为解决这一核心矛盾而诞生。

Saga模式的核心洞察在于:不是所有业务操作都需要强一致性,也不是所有操作都能快速完成 。它采用完全不同的思路------通过事件驱动补偿服务来实现最终一致性。

4.1.2 Saga模式的三大设计原则

1. 最终一致性原则

Saga明确放弃强一致性,接受中间状态的存在,但通过补偿机制保证最终结果正确。

2. 事件驱动原则

每个服务完成本地事务后,通过事件触发下一个服务执行,形成松耦合的协作关系。

3. 补偿驱动原则

为每个正向操作定义对应的补偿操作,在失败时按反向顺序执行补偿。

4.2 Saga模式的基本概念与两种实现方式

4.2.1 Saga的核心概念体系

1. Saga事务(Saga Transaction)

一个完整的业务逻辑流程,由一系列本地事务组成。

2. 本地事务(Local Transaction)

Saga中的每个步骤都是一个独立的本地ACID事务。

3. 补偿事务(Compensating Transaction)

为每个本地事务定义的反向操作,用于撤销该本地事务的影响。

4. Saga协调器(Saga Coordinator)

负责协调各个本地事务的执行顺序和状态管理。

4.2.2 两种实现方式对比

复制代码

1. 编排式(Orchestration)Saga

  • 核心:有一个集中的协调器控制整个流程

  • 优点:逻辑集中,易于理解和维护

  • 缺点:协调器可能成为单点故障

  • 适用:新系统开发,流程相对固定

2. 协同式(Choreography)Saga

  • 核心:每个服务通过事件触发下一个服务

  • 优点:完全解耦,无单点故障

  • 缺点:流程分散,难以理解和调试

  • 适用:遗留系统集成,服务高度自治

Seata Saga采用编排式 实现,提供了状态机引擎作为协调器,这是其最核心的创新。

4.3 Seata Saga状态机引擎深度解析

4.3.1 状态机引擎的架构设计

Seata Saga的状态机引擎是Saga模式的大脑和中枢,它通过定义状态机的形式来描述复杂的业务流程。

4.3.2 状态机定义语言详解

Seata Saga使用JSON格式定义状态机,这是一种声明式的业务流程描述方式。示例:

复制代码
{
  "Name": "placeOrderSaga",
  "Comment": "下单Saga流程",
  "Version": "1.0.0",
  "StartState": "CreateOrder",
  "States": {
    "CreateOrder": {
      "Type": "ServiceTask",
      "ServiceName": "orderService",
      "ServiceMethod": "createOrder",
      "CompensateState": "CancelOrder",
      "Next": "ProcessPayment",
      "Input": [
        "orderId = $input.orderId",
        "userId = $input.userId",
        "amount = $input.amount"
      ],
      "Output": {
        "orderNo": "$output.orderNo"
      }
    },
    "ProcessPayment": {
      "Type": "ServiceTask",
      "ServiceName": "paymentService",
      "ServiceMethod": "processPayment",
      "CompensateState": "RefundPayment",
      "Next": "ArrangeShipping",
      "Input": [
        "orderNo = $output[CreateOrder].orderNo",
        "amount = $input.amount"
      ],
      "Catch": [
        {
          "Exceptions": ["PaymentException"],
          "Next": "HandlePaymentFailure"
        }
      ]
    },
    "ArrangeShipping": {
      "Type": "ServiceTask",
      "ServiceName": "shippingService",
      "ServiceMethod": "arrangeShipping",
      "CompensateState": "CancelShipping",
      "Next": "Succeed",
      "Input": [
        "orderNo = $output[CreateOrder].orderNo"
      ],
      "Retry": [
        {
          "Exceptions": ["TimeoutException"],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 2.0
        }
      ]
    },
    "CancelOrder": {
      "Type": "CompensationTask",
      "ServiceName": "orderService",
      "ServiceMethod": "cancelOrder"
    },
    "HandlePaymentFailure": {
      "Type": "Choice",
      "Choices": [
        {
          "Expression": "$output[ProcessPayment].errorCode == 'INSUFFICIENT_BALANCE'",
          "Next": "NotifyUserToRecharge"
        },
        {
          "Expression": "true",
          "Next": "Fail"
        }
      ]
    },
    "Succeed": {
      "Type": "Succeed"
    },
    "Fail": {
      "Type": "Fail",
      "ErrorCode": "SAGA_FAILED",
      "Message": "Saga execution failed"
    }
  }
}
复制代码

4.3.3 状态机核心元素解析

1. 状态类型(State Type)

java 复制代码
public enum StateType {
    SERVICE_TASK,      // 服务调用
    COMPENSATION_TASK, // 补偿任务
    CHOICE,           // 条件分支
    SUCCEED,          // 成功结束
    FAIL,             // 失败结束
    COMPENSATION_SUCCEED, // 补偿成功
    COMPENSATION_FAIL    // 补偿失败
}

2. 服务任务(ServiceTask)

服务任务是Saga的核心执行单元,每个服务任务对应一个业务操作。

java 复制代码
public class ServiceTaskState extends AbstractTaskState {
    private String serviceName;     // 服务名
    private String serviceMethod;   // 方法名
    private String compensateState; // 对应的补偿状态
    private List<String> input;     // 输入参数映射
    private Map<String, Object> output; // 输出参数映射
    private List<Retry> retry;      // 重试配置
    private List<Catch> catchConfig; // 异常捕获配置
    
    // 执行逻辑
    public ExecutionResult execute(StateContext context) {
        // 1. 准备输入参数
        Map<String, Object> params = prepareInput(context);
        
        // 2. 调用目标服务
        Object result = serviceInvoker.invoke(serviceName, serviceMethod, params);
        
        // 3. 处理输出
        context.setOutput(buildOutput(result));
        
        // 4. 决定下一个状态
        return determineNextState(context);
    }
}

3. 补偿任务(CompensationTask)

补偿任务是Saga保证最终一致性的关键,每个服务任务都应有对应的补偿任务。

java 复制代码
public class CompensationTaskState extends AbstractTaskState {
    private String serviceName;
    private String serviceMethod;
    
    public ExecutionResult compensate(StateContext context) {
        // 1. 获取原始执行结果
        ServiceTaskState originalState = getOriginalState(context);
        Object originalOutput = context.getOutput(originalState.getName());
        
        // 2. 调用补偿服务
        Map<String, Object> params = buildCompensationParams(originalOutput);
        serviceInvoker.invoke(serviceName, serviceMethod, params);
        
        // 3. 返回补偿结果
        return ExecutionResultBuilder
            .newBuilder()
            .withNext(StateConstants.COMPENSATION_SUCCEED_STATE)
            .build();
    }
}

4. 选择状态(Choice)

选择状态允许根据条件执行不同的分支,实现复杂的流程控制。

java 复制代码
public class ChoiceState extends AbstractState {
    private List<Choice> choices;
    
    public ExecutionResult execute(StateContext context) {
        for (Choice choice : choices) {
            // 使用ExpressionEvaluator评估条件
            if (expressionEvaluator.eval(choice.getExpression(), context)) {
                return ExecutionResultBuilder
                    .newBuilder()
                    .withNext(choice.getNext())
                    .build();
            }
        }
        throw new SagaException("No choice matched");
    }
}

4.4 Saga补偿机制的深度实现

4.4.1 补偿触发与执行流程

Saga的补偿机制是其最终一致性的保证,补偿流程需要精心设计。

4.4.2 补偿策略与实现

1. 立即补偿策略

当某个服务执行失败时,立即开始反向补偿。

java 复制代码
@Component
public class ImmediateCompensationStrategy implements CompensationStrategy {
    
    @Override
    public void compensate(String instanceId, String failedStateName) {
        // 1. 获取Saga实例
        SagaInstance instance = repository.get(instanceId);
        
        // 2. 获取已成功执行的状态(按反向顺序)
        List<StateInstance> completedStates = instance.getCompletedStates()
            .stream()
            .sorted(Comparator.comparing(StateInstance::getEndTime).reversed())
            .collect(Collectors.toList());
        
        // 3. 逐个执行补偿
        for (StateInstance state : completedStates) {
            if (state.getCompensateState() != null) {
                try {
                    // 调用补偿服务
                    compensationExecutor.execute(
                        state.getCompensateState(), 
                        state.getOutput()
                    );
                    state.setCompensationStatus(CompensationStatus.COMPENSATED);
                } catch (Exception e) {
                    state.setCompensationStatus(CompensationStatus.FAILED);
                    throw new CompensationException("补偿失败", e);
                }
            }
        }
    }
}

2. 延迟补偿策略

在某些场景下,可能需要延迟执行补偿,等待特定条件满足。

java 复制代码
@Component
public class DelayedCompensationStrategy implements CompensationStrategy {
    
    @Autowired
    private ScheduledExecutorService scheduler;
    
    @Override
    public void compensate(String instanceId, String failedStateName) {
        // 1. 记录补偿计划
        CompensationPlan plan = new CompensationPlan();
        plan.setInstanceId(instanceId);
        plan.setScheduledTime(calculateScheduleTime());
        plan.setMaxRetries(3);
        repository.savePlan(plan);
        
        // 2. 调度补偿任务
        scheduler.schedule(() -> {
            executeCompensationWithRetry(plan);
        }, plan.getDelay(), TimeUnit.SECONDS);
    }
    
    private void executeCompensationWithRetry(CompensationPlan plan) {
        int retryCount = 0;
        while (retryCount < plan.getMaxRetries()) {
            try {
                doCompensate(plan.getInstanceId());
                plan.setStatus(CompensationStatus.COMPENSATED);
                break;
            } catch (Exception e) {
                retryCount++;
                if (retryCount >= plan.getMaxRetries()) {
                    plan.setStatus(CompensationStatus.FAILED);
                    // 发送告警,需要人工干预
                    alertService.sendCompensationFailedAlert(plan);
                } else {
                    // 指数退避重试
                    long delay = (long) (Math.pow(2, retryCount) * 1000);
                    scheduler.schedule(() -> 
                        executeCompensationWithRetry(plan), 
                        delay, TimeUnit.MILLISECONDS
                    );
                }
            }
        }
    }
}

4.4.3 补偿幂等性与事务性

补偿操作必须是幂等事务性的,这是Saga正确性的关键。

幂等性实现

java 复制代码
@Service
public class OrderCompensationService {
    
    @Autowired
    private OrderCompensationRecordDao recordDao;
    
    @Transactional
    public void compensateOrder(Long orderId, String sagaInstanceId) {
        // 1. 幂等性检查
        OrderCompensationRecord existing = recordDao
            .findBySagaInstanceId(sagaInstanceId);
        if (existing != null) {
            log.info("补偿已执行过,直接返回,sagaInstanceId={}", sagaInstanceId);
            return;
        }
        
        // 2. 创建补偿记录(在事务开始时)
        OrderCompensationRecord record = new OrderCompensationRecord();
        record.setSagaInstanceId(sagaInstanceId);
        record.setOrderId(orderId);
        record.setStatus(CompensationStatus.PROCESSING);
        record.setCreateTime(new Date());
        recordDao.insert(record);
        
        try {
            // 3. 执行业务补偿逻辑
            orderDao.cancelOrder(orderId);
            inventoryDao.restoreStock(orderId);
            
            // 4. 更新补偿状态
            record.setStatus(CompensationStatus.COMPLETED);
            record.setUpdateTime(new Date());
            recordDao.update(record);
            
        } catch (Exception e) {
            // 5. 补偿失败处理
            record.setStatus(CompensationStatus.FAILED);
            record.setErrorMessage(e.getMessage());
            recordDao.update(record);
            throw e;
        }
    }
}

补偿事务管理

java 复制代码
@Component
public class CompensationTransactionManager {
    
    // Saga补偿需要新的事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void executeCompensation(Runnable compensationTask) {
        try {
            compensationTask.run();
        } catch (Exception e) {
            // 补偿失败不能回滚原始事务
            // 记录日志并抛出,由上层处理
            log.error("补偿事务执行失败", e);
            throw new CompensationException("补偿失败", e);
        }
    }
    
    // 嵌套补偿事务(复杂场景)
    @Transactional(propagation = Propagation.NESTED)
    public void executeNestedCompensation(String compensationId, 
                                         List<Runnable> tasks) {
        CompensationRecord record = new CompensationRecord();
        record.setCompensationId(compensationId);
        record.setStartTime(new Date());
        
        try {
            for (Runnable task : tasks) {
                task.run();
            }
            record.setStatus("SUCCESS");
        } catch (Exception e) {
            // 嵌套事务会回滚到保存点
            record.setStatus("FAILED");
            throw e;
        } finally {
            record.setEndTime(new Date());
            compensationRecordDao.save(record);
        }
    }
}

4.5 异常处理与状态恢复机制

4.5.1 异常分类与处理策略

Saga执行过程中可能遇到各种异常,需要不同的处理策略:

java 复制代码
public class SagaExceptionHandler {
    
    // 异常分类
    public enum ExceptionCategory {
        BUSINESS_EXCEPTION,     // 业务异常(如库存不足)
        INFRASTRUCTURE_EXCEPTION, // 基础设施异常(如网络超时)
        SYSTEM_EXCEPTION,       // 系统异常(如数据库连接失败)
        COMPENSATION_EXCEPTION  // 补偿异常
    }
    
    // 异常处理策略
    public interface ExceptionHandler {
        ExecutionResult handle(Exception e, StateContext context);
    }
    
    // 业务异常处理:触发补偿
    @Component
    public class BusinessExceptionHandler implements ExceptionHandler {
        @Override
        public ExecutionResult handle(Exception e, StateContext context) {
            // 记录业务异常
            context.setException(e);
            
            // 触发补偿流程
            return ExecutionResultBuilder
                .newBuilder()
                .withNext(StateConstants.COMPENSATION_TRIGGER_STATE)
                .build();
        }
    }
    
    // 基础设施异常处理:重试
    @Component
    public class InfrastructureExceptionHandler implements ExceptionHandler {
        @Override
        public ExecutionResult handle(Exception e, StateContext context) {
            StateInstance state = context.getStateInstance();
            
            // 检查重试次数
            if (state.getRetryCount() < state.getMaxRetries()) {
                // 等待后重试
                long delay = calculateRetryDelay(state.getRetryCount());
                return ExecutionResultBuilder
                    .newBuilder()
                    .withRetry(true)
                    .withRetryDelay(delay)
                    .build();
            } else {
                // 重试耗尽,触发补偿
                return ExecutionResultBuilder
                    .newBuilder()
                    .withNext(StateConstants.COMPENSATION_TRIGGER_STATE)
                    .build();
            }
        }
        
        private long calculateRetryDelay(int retryCount) {
            // 指数退避算法
            return (long) (Math.pow(2, retryCount) * 1000);
        }
    }
}

4.5.2 Saga状态恢复机制

由于Saga可能长时间运行,需要完善的状态恢复机制来应对各种故障。

状态恢复实现

java 复制代码
@Component
public class SagaRecoveryManager {
    
    @Autowired
    private SagaInstanceRepository instanceRepository;
    
    @Autowired
    private StateMachineEngine engine;
    
    @Scheduled(fixedDelay = 60000) // 每分钟执行一次
    public void recoverUnfinishedInstances() {
        // 1. 查询需要恢复的实例
        List<SagaInstance> instances = instanceRepository
            .findByStatusIn(Arrays.asList(
                InstanceStatus.RUNNING,
                InstanceStatus.COMPENSATING,
                InstanceStatus.SUSPENDED
            ));
        
        for (SagaInstance instance : instances) {
            try {
                // 2. 检查实例健康状况
                InstanceHealth health = checkInstanceHealth(instance);
                
                // 3. 根据健康状况决定恢复策略
                switch (health) {
                    case HEALTHY:
                        // 正常恢复执行
                        resumeExecution(instance);
                        break;
                    case STUCK:
                        // 卡住的处理
                        handleStuckInstance(instance);
                        break;
                    case TIMEOUT:
                        // 超时处理
                        handleTimeoutInstance(instance);
                        break;
                    case UNRECOVERABLE:
                        // 不可恢复,标记为失败
                        markAsFailed(instance);
                        break;
                }
            } catch (Exception e) {
                log.error("恢复Saga实例失败: {}", instance.getId(), e);
                // 记录失败,下次重试
                instance.setRecoveryFailureCount(
                    instance.getRecoveryFailureCount() + 1);
                instanceRepository.update(instance);
            }
        }
    }
    
    private InstanceHealth checkInstanceHealth(SagaInstance instance) {
        // 检查最后更新时间
        Date lastUpdate = instance.getLastUpdateTime();
        long inactiveDuration = System.currentTimeMillis() - lastUpdate.getTime();
        
        if (inactiveDuration > 24 * 3600 * 1000) {
            return InstanceHealth.UNRECOVERABLE;
        } else if (inactiveDuration > 10 * 60 * 1000) {
            return InstanceHealth.TIMEOUT;
        } else if (instance.getCurrentStateDuration() > 5 * 60 * 1000) {
            return InstanceHealth.STUCK;
        } else {
            return InstanceHealth.HEALTHY;
        }
    }
    
    private void resumeExecution(SagaInstance instance) {
        // 获取当前状态
        StateInstance currentState = instance.getCurrentState();
        
        if (currentState == null) {
            // 从头开始
            engine.start(instance.getStateMachineName(), 
                        instance.getInput());
        } else {
            // 从当前状态继续
            engine.trigger(instance.getId(), 
                          currentState.getName(), 
                          currentState.getOutput());
        }
    }
}

4.6 本章总结

Saga模式代表了分布式事务的另一条路径------放弃强一致性,拥抱最终一致性,通过事件驱动和补偿机制来解决长事务问题。

  1. 核心思想:Saga通过将长事务拆分为多个可补偿的本地事务,解决了传统事务模型无法处理长时间运行业务流程的问题。

  2. 状态机引擎:Seata Saga的核心创新,通过声明式的JSON状态机定义,实现了复杂的业务流程编排。状态机引擎提供了完整的生命周期管理、状态持久化和恢复能力。

  3. 补偿机制:这是Saga保证最终一致性的关键。我们深入分析了补偿的触发机制、执行策略、幂等性保证和事务管理。

  4. 异常处理与恢复:Saga模式需要完善的异常处理机制和状态恢复能力,以应对分布式环境中的各种故障场景。

  5. 实践指南:从状态机设计、服务接口规范到性能优化和故障处理,提供了完整的Saga实施指南。

Saga模式的适用场景:

  • 长流程业务:如订单处理、供应链管理、工作流审批

  • 遗留系统集成:需要与无法改造的旧系统协同工作

  • 最终一致性可接受:业务上可以接受短暂的中间状态

相关推荐
青云计划13 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿13 小时前
Jsoniter(java版本)使用介绍
java·开发语言
Victor35613 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor35613 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
探路者继续奋斗13 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-194314 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
yeyeye11114 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
A懿轩A14 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
Tony Bai15 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
乐观勇敢坚强的老彭15 小时前
c++寒假营day03
java·开发语言·c++