Skip to content

Go SDK - Workflow 开发 ​

本文档详细介绍 Temporal Go SDK 中 Workflow 的定义和开发。

10.1 Workflow 定义 ​

基本结构 ​

go
package app

import (
    "time"
    "go.temporal.io/sdk/workflow"
)

// MyWorkflow 是一个工作流定义
// 参数:ctx - 工作流上下文,input - 自定义输入
// 返回:自定义输出和错误
func MyWorkflow(ctx workflow.Context, input string) (string, error) {
    // 工作流逻辑
    return "result", nil
}

Workflow 签名规则 ​

规则说明
第一个参数必须是 workflow.Context
返回值(result, error) 格式
参数/返回值必须可序列化
命名导出函数(首字母大写)

10.2 Workflow Context ​

上下文功能 ​

go
func MyWorkflow(ctx workflow.Context, input string) error {
    // 获取 Logger
    logger := workflow.GetLogger(ctx)
    logger.Info("Workflow 开始")
    
    // 获取信息
    info := workflow.GetInfo(ctx)
    workflowID := info.WorkflowExecution.ID
    runID := info.WorkflowExecution.RunID
    namespace := info.Namespace
    
    return nil
}

设置 Activity 选项 ​

go
func MyWorkflow(ctx workflow.Context) error {
    ao := workflow.ActivityOptions{
        StartToCloseTimeout: 10 * time.Second,
        RetryPolicy: &temporal.RetryPolicy{
            InitialInterval:    time.Second,
            BackoffCoefficient: 2.0,
            MaximumInterval:    time.Minute,
            MaximumAttempts:    5,
        },
    }
    ctx = workflow.WithActivityOptions(ctx, ao)
    
    // 使用配置后的 ctx 执行 Activity
    return nil
}

设置 Local Activity 选项 ​

go
func MyWorkflow(ctx workflow.Context) error {
    lao := workflow.LocalActivityOptions{
        StartToCloseTimeout: 5 * time.Second,
    }
    ctx = workflow.WithLocalActivityOptions(ctx, lao)
    
    return nil
}

10.3 执行 Activity ​

同步执行 ​

go
func MyWorkflow(ctx workflow.Context, name string) (string, error) {
    var result string
    err := workflow.ExecuteActivity(ctx, MyActivity, name).Get(ctx, &result)
    if err != nil {
        return "", err
    }
    return result, nil
}

多个 Activity 串行执行 ​

go
func MyWorkflow(ctx workflow.Context) error {
    // 第一个 Activity
    var result1 string
    err := workflow.ExecuteActivity(ctx, Step1).Get(ctx, &result1)
    if err != nil {
        return err
    }
    
    // 第二个 Activity(依赖第一个的结果)
    var result2 string
    err = workflow.ExecuteActivity(ctx, Step2, result1).Get(ctx, &result2)
    if err != nil {
        return err
    }
    
    return nil
}

并行执行多个 Activity ​

go
func MyWorkflow(ctx workflow.Context) error {
    // 启动多个 Activity
    future1 := workflow.ExecuteActivity(ctx, Activity1)
    future2 := workflow.ExecuteActivity(ctx, Activity2)
    future3 := workflow.ExecuteActivity(ctx, Activity3)
    
    // 等待所有完成
    var result1, result2, result3 string
    if err := future1.Get(ctx, &result1); err != nil {
        return err
    }
    if err := future2.Get(ctx, &result2); err != nil {
        return err
    }
    if err := future3.Get(ctx, &result3); err != nil {
        return err
    }
    
    return nil
}

使用 Selector 等待任意完成 ​

go
func MyWorkflow(ctx workflow.Context) error {
    future1 := workflow.ExecuteActivity(ctx, FastActivity)
    future2 := workflow.ExecuteActivity(ctx, SlowActivity)
    
    selector := workflow.NewSelector(ctx)
    
    var result string
    selector.AddFuture(future1, func(f workflow.Future) {
        f.Get(ctx, &result)
    })
    selector.AddFuture(future2, func(f workflow.Future) {
        f.Get(ctx, &result)
    })
    
    selector.Select(ctx) // 等待第一个完成
    
    return nil
}

10.4 定时器和延迟 ​

简单延迟 ​

go
func MyWorkflow(ctx workflow.Context) error {
    // 等待 1 分钟
    workflow.Sleep(ctx, time.Minute)
    
    // 或使用 Timer
    timer := workflow.NewTimer(ctx, time.Minute)
    if err := timer.Get(ctx, nil); err != nil {
        return err
    }
    
    return nil
}

带超时的 Activity ​

go
func MyWorkflow(ctx workflow.Context) error {
    ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
        StartToCloseTimeout: 10 * time.Second,
    })
    
    // 带超时控制
    future := workflow.ExecuteActivity(ctx, MyActivity)
    
    selector := workflow.NewSelector(ctx)
    selector.AddFuture(future, func(f workflow.Future) {
        var result string
        f.Get(ctx, &result)
    })
    selector.AddFuture(workflow.NewTimer(ctx, 5*time.Second), func(f workflow.Future) {
        // 超时处理
    })
    
    selector.Select(ctx)
    return nil
}

10.5 条件等待 ​

等待多个条件 ​

go
func MyWorkflow(ctx workflow.Context) error {
    signalCh := workflow.GetSignalChannel(ctx, "my-signal")
    timerCh := workflow.NewTimer(ctx, time.Hour)
    
    var received bool
    selector := workflow.NewSelector(ctx)
    
    selector.AddReceive(signalCh, func(c workflow.ReceiveChannel, more bool) {
        var signal string
        c.Receive(ctx, &signal)
        received = true
    })
    
    selector.AddFuture(timerCh, func(f workflow.Future) {
        // 超时,未收到信号
        received = false
    })
    
    selector.Select(ctx)
    
    if !received {
        return fmt.Errorf("等待超时")
    }
    
    return nil
}

10.6 Child Workflow ​

启动子工作流 ​

go
func ParentWorkflow(ctx workflow.Context) error {
    childOpts := workflow.ChildWorkflowOptions{
        WorkflowID: "child-" + workflow.GetInfo(ctx).WorkflowExecution.ID,
    }
    ctx = workflow.WithChildOptions(ctx, childOpts)
    
    var result string
    err := workflow.ExecuteChildWorkflow(ctx, ChildWorkflow, "input").Get(ctx, &result)
    return err
}

等待子工作流完成 ​

go
func ParentWorkflow(ctx workflow.Context) error {
    future := workflow.ExecuteChildWorkflow(ctx, ChildWorkflow, "input")
    
    // 可以先做其他事情...
    
    var result string
    if err := future.Get(ctx, &result); err != nil {
        return err
    }
    
    return nil
}

10.7 确定性约束 ​

必须遵守的规则 ​

go
// ❌ 禁止:使用 time.Now()
t := time.Now() // 非确定性!

// ✅ 正确:使用 workflow.Now()
t := workflow.Now(ctx)

// ❌ 禁止:使用 time.Sleep()
time.Sleep(time.Second) // 非确定性!

// ✅ 正确:使用 workflow.Sleep()
workflow.Sleep(ctx, time.Second)

// ❌ 禁止:使用随机数
rand.Intn(100) // 非确定性!

// ✅ 正确:使用确定性值或从输入获取

// ❌ 禁止:使用全局变量
var counter int // 不安全!

// ✅ 正确:使用 Workflow 状态
counter := 0

可用的确定性 API ​

API用途
workflow.Now()获取当前时间
workflow.Sleep()延迟
workflow.GetLogger()日志
workflow.GetInfo()工作流信息
workflow.SideEffect()执行非确定性操作

10.8 SideEffect ​

用于执行非确定性操作但保存结果:

go
func MyWorkflow(ctx workflow.Context) error {
    // 生成唯一 ID(非确定性操作)
    var uniqueID string
    workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
        return uuid.New().String()
    }).Get(&uniqueID)
    
    // uniqueID 在重放时保持相同值
    workflow.GetLogger(ctx).Info("Unique ID", "id", uniqueID)
    
    return nil
}

10.9 最佳实践 ​

1. 保持 Workflow 简洁 ​

go
// ✅ 好的做法:Workflow 只编排逻辑
func OrderWorkflow(ctx workflow.Context, orderID string) error {
    // 验证
    if err := workflow.ExecuteActivity(ctx, ValidateOrder, orderID).Get(ctx, nil); err != nil {
        return err
    }
    
    // 处理
    if err := workflow.ExecuteActivity(ctx, ProcessOrder, orderID).Get(ctx, nil); err != nil {
        return err
    }
    
    // 通知
    return workflow.ExecuteActivity(ctx, SendNotification, orderID).Get(ctx, nil)
}

2. 使用版本控制 ​

go
func MyWorkflow(ctx workflow.Context) error {
    v := workflow.GetVersion(ctx, "add-step", workflow.DefaultVersion, 1)
    if v == 1 {
        // 新增的步骤
        workflow.ExecuteActivity(ctx, NewStep).Get(ctx, nil)
    }
    return nil
}

3. 正确处理错误 ​

go
func MyWorkflow(ctx workflow.Context) error {
    var result string
    err := workflow.ExecuteActivity(ctx, MyActivity).Get(ctx, &result)
    if err != nil {
        var appErr *temporal.ApplicationError
        if errors.As(err, &appErr) {
            // 应用错误,可以重试或跳过
            return nil
        }
        return err
    }
    return nil
}

下一步 ​