go使用 seata 示例

在 Go 语言中使用 Seata 实现 Saga 模式,核心在于使用 seata-go 框架。

与 Java 版本通常使用 JSON/XML 状态机配置不同,Go 版本的 Saga 更倾向于代码编排(Code-based Orchestration),利用 Go 语言的结构体和接口特性来定义事务流程。

以下是基于 seata-go 实现 Saga 模式的完整指南。


🛠️ 第一步:环境准备

首先,你需要引入 seata-go 的依赖:

go get github.com/apache/seata-go

同时,确保你已经部署了 Seata Server (TC),并且在 Go 项目的配置文件中(通常是 conf/seatago.yml 或代码配置)正确连接到了 TC 地址。


💻 第二步:定义业务服务(参与者)

在 Saga 模式中,每个步骤都是一个“参与者”。你需要定义 Go 结构体来实现具体的业务逻辑(正向操作)和补偿逻辑。

假设我们有一个 订单创建库存扣减 的流程。

package service

import (
	"fmt"
	"github.com/apache/seata-go/pkg/saga/engine"
)

// 1. 定义订单服务参与者
type OrderService struct {}

// 正向操作:创建订单
func (o *OrderService) CreateOrder(orderID string) error {
	fmt.Printf("🛒 [Order] 创建订单: %s\n", orderID)
	// 模拟业务逻辑
	return nil
}

// 补偿操作:取消订单
func (o *OrderService) CancelOrder(orderID string) error {
	fmt.Printf("🚫 [Order] 取消订单: %s\n", orderID)
	// 模拟回滚逻辑
	return nil
}

// 2. 定义库存服务参与者
type InventoryService struct {}

// 正向操作:扣减库存
func (i *InventoryService) DeductStock(productID string, count int) error {
	fmt.Printf("📦 [Inventory] 扣减库存: %s, 数量: %d\n", productID, count)
	// 模拟扣减
	if productID == "BAD_PRODUCT" {
		return fmt.Errorf("库存不足") // 模拟异常
	}
	return nil
}

// 补偿操作:恢复库存
func (i *InventoryService) RestoreStock(productID string, count int) error {
	fmt.Printf("↩️ [Inventory] 恢复库存: %s, 数量: %d\n", productID, count)
	// 模拟恢复
	return nil
}

🧩 第三步:编排 Saga 流程

这是 Seata Go 版本的核心。你需要使用 engine 包来构建状态机,定义步骤的顺序、参数传递以及补偿关系。

package main

import (
	"context"
	"log"
	"your_project/service" // 引入上面的服务包

	"github.com/apache/seata-go/pkg/saga/engine"
	"github.com/apache/seata-go/pkg/saga/statemachine"
)

func main() {
	// 初始化服务实例
	orderSvc := &service.OrderService{}
	inventorySvc := &service.InventoryService{}

	// 1. 创建状态机定义
	// 在实际生产中,建议将状态机定义持久化到数据库,这里为了演示直接在代码中构建
	stateMachine := &statemachine.StateMachine{
		Name: "CreateOrderSaga",
		TenantId: "default",
	}

	// 2. 定义步骤 1: 创建订单
	// 使用 AddServiceTask 添加服务任务
	stateMachine.AddServiceTask("createOrder", 
		orderSvc.CreateOrder,       // 正向方法
		orderSvc.CancelOrder,       // 补偿方法
	)

	// 3. 定义步骤 2: 扣减库存
	stateMachine.AddServiceTask("deductStock", 
		inventorySvc.DeductStock,   // 正向方法
		inventorySvc.RestoreStock,  // 补偿方法
	)

	// 4. 定义执行顺序
	// 先执行 createOrder,成功后执行 deductStock
	stateMachine.AddTransition("createOrder", "deductStock")
	// 定义结束状态
	stateMachine.AddEndState("deductStock")

	// 5. 启动引擎并执行
	// 注意:这里需要 Seata-go 的引擎初始化配置,此处省略配置加载代码
	// engine.Init() 
	
	// 准备业务参数
	params := map[string]interface{}{
		"orderID":   "ORD_123456",
		"productID": "PROD_888",
		"count":     10,
	}

	// 启动 Saga
	// context 用于传递全局事务 ID (XID) 等信息
	ctx := context.Background()
	
	log.Println("🚀 开始执行 Saga 事务...")
	
	// 调用启动
	// 注意:实际 API 可能会随版本更新,请以最新版 seata-go 文档为准
	// 这里演示逻辑流程
	err := engine.Execute(ctx, stateMachine, params)
	
	if err != nil {
		log.Printf("❌ Saga 执行失败: %v", err)
		// Seata-go 会自动根据状态机定义,反向调用 CancelOrder 或 RestoreStock
	} else {
		log.Println("✅ Saga 事务执行成功!")
	}
}

⚙️ 关键配置与机制

1. 状态机持久化

在生产环境中,为了防止服务宕机导致事务状态丢失,Seata 的 Saga 状态机定义(JSON 或 对象结构)和执行状态通常存储在数据库中。

  • Go 实现seata-go 提供了 StateLogStore 接口,你可以实现该接口将状态存入 MySQL 或 Redis。

2. 参数传递

Saga 的精髓在于上下文传递。

  • 上一步(CreateOrder)的返回值(如生成的 orderID),需要作为参数传递给下一步(DeductStock)。
  • seata-go 中,这通常通过 Context 或者 Map 在步骤间共享。

3. 异常处理与补偿

  • 自动触发:当 DeductStock 返回 error 时,引擎会捕获该异常。
  • 逆向执行:引擎会根据状态机定义的依赖关系,自动逆序调用已执行步骤的补偿方法(先调用 RestoreStock,再调用 CancelOrder)。

⚖️ Seata-go Saga vs DTM Saga

既然你在 Go 生态中,不得不提 DTM。虽然 Seata 是阿里出品,但在 Go 语言中,DTM 对 Saga 模式的支持往往被认为更“Go 风格”。

特性 Seata-go Saga DTM (Go)
编排方式 偏向结构化配置/状态机定义 纯代码流式编排 (saga.Add(...))
学习曲线 较高(需理解 Seata 的状态机概念) 低(API 非常直观)
生态背景 适合 Java/Go 混合架构,统一使用 Seata 适合纯 Go 架构,或微服务架构较新的团队
代码风格 定义 StateMachine 对象 链式调用,类似 saga.Add(url, compensateUrl, data)

如果你发现 Seata-go 的配置过于繁琐,或者文档不够直观,强烈建议尝试 DTM 的 Saga 模式,它可能是目前 Go 语言中体验最好的分布式事务实现。

📌 总结

在 Go 中使用 Seata Saga:

  1. 引入 seata-go 包。
  2. 编写业务 Service,包含正向和补偿方法。
  3. 使用 statemachine 包构建状态机,定义步骤和顺序。
  4. 调用 engine.Execute 启动事务。
  5. 框架会自动处理异常捕获和补偿回滚。
posted @ 2026-04-21 13:29  干炸小黄鱼  阅读(19)  评论(0)    收藏  举报