Skip to content

Go SDK - 高级特性 ​

本文档介绍 Temporal Go SDK 的高级特性,包括 Signal、Query、Interceptor 等。

12.1 Signal ​

Signal 用于从外部向运行中的 Workflow 发送消息。

定义 Signal 处理 ​

go
func OrderWorkflow(ctx workflow.Context, orderID string) error {
    // Signal 通道
    cancelCh := workflow.GetSignalChannel(ctx, "cancel-order")
    updateCh := workflow.GetSignalChannel(ctx, "update-order")
    
    // 状态
    var cancelled bool
    var updates []string
    
    // 处理循环
    for {
        selector := workflow.NewSelector(ctx)
        
        // 处理取消信号
        selector.AddReceive(cancelCh, func(c workflow.ReceiveChannel, more bool) {
            var reason string
            c.Receive(ctx, &reason)
            cancelled = true
            workflow.GetLogger(ctx).Info("订单取消", "reason", reason)
        })
        
        // 处理更新信号
        selector.AddReceive(updateCh, func(c workflow.ReceiveChannel, more bool) {
            var update string
            c.Receive(ctx, &update)
            updates = append(updates, update)
        })
        
        // 处理其他逻辑...
        selector.AddFuture(workflow.NewTimer(ctx, time.Second), func(f workflow.Future) {
            // 定时检查
        })
        
        selector.Select(ctx)
        
        if cancelled {
            return workflow.ExecuteActivity(ctx, CancelOrder, orderID).Get(ctx, nil)
        }
    }
}

发送 Signal ​

go
// 从客户端发送
err := c.SignalWorkflow(ctx, workflowID, "", "cancel-order", "用户取消")

// 在 Activity 中发送(不推荐)
func NotifyActivity(ctx context.Context, workflowID string) error {
    // 需要 Client
    c.SignalWorkflow(ctx, workflowID, "", "notification-sent", true)
    return nil
}

Signal 批量处理 ​

go
func MyWorkflow(ctx workflow.Context) error {
    signalCh := workflow.GetSignalChannel(ctx, "items")
    var items []string
    
    for {
        // 批量接收所有待处理的 Signal
        for signalCh.Len() > 0 {
            var item string
            signalCh.ReceiveAsync(&item)
            items = append(items, item)
        }
        
        // 处理批量
        if len(items) > 0 {
            workflow.ExecuteActivity(ctx, ProcessBatch, items).Get(ctx, nil)
            items = nil
        }
        
        workflow.Sleep(ctx, time.Minute)
    }
}

12.2 Query ​

Query 用于查询 Workflow 状态,不修改状态。

定义 Query Handler ​

go
func OrderWorkflow(ctx workflow.Context, orderID string) error {
    // 状态
    status := "pending"
    var items []string
    
    // 注册 Query Handler
    err := workflow.SetQueryHandler(ctx, "status", func() (string, error) {
        return status, nil
    })
    if err != nil {
        return err
    }
    
    err = workflow.SetQueryHandler(ctx, "items", func() ([]string, error) {
        return items, nil
    })
    if err != nil {
        return err
    }
    
    // Workflow 逻辑更新 status...
    status = "processing"
    
    return nil
}

执行 Query ​

go
// 查询状态
resp, err := c.QueryWorkflow(ctx, workflowID, "", "status")
var status string
resp.Get(&status)

// 查询 items
resp, err = c.QueryWorkflow(ctx, workflowID, "", "items")
var items []string
resp.Get(&items)

Query 最佳实践 ​

go
// 返回结构化状态
type OrderState struct {
    Status    string
    Items     []string
    CreatedAt time.Time
    UpdatedAt time.Time
}

err := workflow.SetQueryHandler(ctx, "state", func() (OrderState, error) {
    return OrderState{
        Status:    status,
        Items:     items,
        CreatedAt: createdAt,
        UpdatedAt: workflow.Now(ctx),
    }, nil
})

12.3 Update ​

Update 是较新的特性,允许修改 Workflow 状态并返回结果。

定义 Update Handler ​

go
func OrderWorkflow(ctx workflow.Context, orderID string) error {
    var items []string
    
    // 注册 Update Handler
    workflow.SetUpdateHandler(ctx, "add-item", func(ctx workflow.Context, item string) error {
        items = append(items, item)
        return nil
    })
    
    workflow.SetUpdateHandler(ctx, "remove-item", func(ctx workflow.Context, index int) error {
        if index >= 0 && index < len(items) {
            items = append(items[:index], items[index+1:]...)
        }
        return nil
    })
    
    // 等待完成信号
    workflow.GetSignalChannel(ctx, "complete").Receive(ctx, nil)
    return nil
}

执行 Update ​

go
// 添加 item
err := c.UpdateWorkflow(ctx, workflowID, "", "add-item", "item-1")

// 删除 item
err = c.UpdateWorkflow(ctx, workflowID, "", "remove-item", 0)

12.4 Continue As New ​

长时间运行的 Workflow 可以使用 Continue As New 来重置历史:

go
func LongRunningWorkflow(ctx workflow.Context, state State) error {
    // 处理一批工作
    for i := 0; i < 1000; i++ {
        workflow.ExecuteActivity(ctx, ProcessItem, i).Get(ctx, nil)
    }
    
    // 更新状态
    state.Processed += 1000
    
    // 历史过长时,Continue As New
    info := workflow.GetInfo(ctx)
    if info.GetCurrentHistoryLength() > 40000 {
        return workflow.NewContinueAsNewError(ctx, LongRunningWorkflow, state)
    }
    
    // 继续处理
    return nil
}

12.5 Interceptor ​

Interceptor 用于拦截 Workflow 和 Activity 的执行。

实现 Interceptor ​

go
type MyInterceptor struct {
    interceptor.WorkflowInterceptorBase
}

func (i *MyInterceptor) InterceptWorkflow(
    ctx workflow.Context,
    in interceptor.WorkflowInput,
    next interceptor.WorkflowInboundInterceptor,
) (interceptor.WorkflowOutput, error) {
    // 前置处理
    workflow.GetLogger(ctx).Info("Workflow 开始", "type", in.WorkflowType)
    
    // 执行
    out, err := next.InterceptWorkflow(ctx, in)
    
    // 后置处理
    if err != nil {
        workflow.GetLogger(ctx).Error("Workflow 失败", "error", err)
    }
    
    return out, err
}

注册 Interceptor ​

go
w := worker.New(c, taskQueue, worker.Options{
    Interceptors: []interceptor.WorkflowInterceptor{
        &MyInterceptor{},
    },
})

12.6 Metrics ​

Temporal SDK 内置 Prometheus 指标支持。

配置 Metrics ​

go
import (
    "go.temporal.io/sdk/worker"
    "go.temporal.io/sdk/contrib/tally"
)

// 创建 Tally Scope
scope := tally.NewScope()

w := worker.New(c, taskQueue, worker.Options{
    MetricsHandler: tally.NewMetricsHandler(scope),
})

自定义指标 ​

go
func MyWorkflow(ctx workflow.Context) error {
    // 获取 Metrics Handler
    metrics := workflow.GetMetricsHandler(ctx)
    
    // 计数器
    metrics.Counter("workflow_executions").Inc(1)
    
    // 计时器
    timer := metrics.Timer("processing_time")
    timer.Start()
    defer timer.Stop()
    
    // Gauge
    metrics.Gauge("items_processed").Update(float64(itemsCount))
    
    return nil
}

12.7 数据转换 ​

自定义数据序列化。

实现 Codec ​

go
type JSONCodec struct{}

func (c *JSONCodec) Encode(value interface{}) ([]byte, error) {
    return json.Marshal(value)
}

func (c *JSONCodec) Decode(data []byte, valuePtr interface{}) error {
    return json.Unmarshal(data, valuePtr)
}

注册 Codec ​

go
c, err := client.Dial(client.Options{
    DataConverter: converter.NewCompositeDataConverter(
        &JSONCodec{},
    ),
})

12.8 生产配置建议 ​

Worker 配置 ​

go
w := worker.New(c, taskQueue, worker.Options{
    // 并发控制
    MaxConcurrentWorkflowTaskExecutionSize:     100,
    MaxConcurrentActivityExecutionSize:         1000,
    MaxConcurrentLocalActivityExecutionSize:    1000,
    
    // 心跳
    DisableWorkflowWorker:                      false,
    DisableActivityWorker:                      false,
    
    // 标识
    Identity: hostname + "-" + workerID,
    
    // 资源限制
    MaxConcurrentWorkflowTaskPollers:           5,
    MaxConcurrentActivityTaskPollers:           10,
})

客户端配置 ​

go
c, err := client.Dial(client.Options{
    HostPort:  "localhost:7233",
    Namespace: "production",
    
    ConnectionOptions: client.ConnectionOptions{
        DialTimeout:           10 * time.Second,
        MaxConcurrentStreams:  1000,
    },
    
    Logger:    zap.NewStdoutLogger(),
    MetricsHandler: metricsHandler,
})

参考 ​