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:
- 引入
seata-go包。 - 编写业务 Service,包含正向和补偿方法。
- 使用
statemachine包构建状态机,定义步骤和顺序。 - 调用
engine.Execute启动事务。 - 框架会自动处理异常捕获和补偿回滚。

浙公网安备 33010602011771号