2. Temporal 平台基础:Server、Worker、Client 架构与核心概念

Temporal 平台由三个核心组件构成:Temporal Server、Worker 和 Client。三者通过 gRPC 协议通信,共同支撑起可靠的工作流执行引擎。理解这三者的职责边界以及 Workflow 与 Activity 的语义差异,是掌握 Temporal 架构的第一步。

2.1 三大组件架构总览

下图展示了 Temporal 平台的高层组件关系:Client 提交任务,Server 负责状态持久化与调度,Worker 执行实际业务逻辑。

Temporal 三大组件架构图 Temporal 平台三大组件架构 Client SDK (Go/Java/Python/TS) 启动 Workflow / 发送 Signal Temporal Server 历史存储 (History) 任务队列 (Task Queue) 可见性 (Visibility) Worker 执行 Workflow & Activity 轮询 Task Queue 通信机制: Client → Server:通过 gRPC 提交 Workflow 执行请求或发送 Signal。 Worker → Server:轮询 Task Queue 获取任务,执行后上报结果。 所有状态变更由 Server 持久化到 History,保证故障恢复 Worker 无状态,Server 有状态

2.2 组件职责详解

组件 职责 状态 典型部署
Temporal Server 管理 Workflow 生命周期、持久化事件历史、维护任务队列、提供可见性查询 有状态(依赖数据库) 集群部署(3-5 节点)
Worker 轮询 Task Queue,执行 Workflow 与 Activity 代码,上报执行结果 无状态 水平扩展,按需启动
Client 通过 SDK 启动 Workflow、发送 Signal、查询状态 无状态 嵌入应用进程
关键设计原则:Worker 不直接持久化任何状态,所有执行进度由 Server 通过 Event History 记录。这使得 Worker 可以随时崩溃、重启或扩缩容,而不会丢失工作流进度。

2.3 Workflow 与 Activity 的核心概念

Workflow 和 Activity 是 Temporal 应用模型的两大基本执行单元,它们在职责、约束和容错方式上有本质区别。

2.3.1 Workflow(工作流)

  • 定义:工作流是一个编排流程的协调者,它定义了一系列步骤的执行顺序、重试策略和补偿逻辑。
  • 确定性约束:Workflow 代码必须是确定性的——相同的输入和事件历史必须产生完全相同的执行路径。因此,Workflow 内禁止直接调用外部 API、生成随机数、读取系统时间等非确定性操作。
  • 执行模型:Workflow 执行被切分为多个决策任务(Decision Task),每次决策都会追加到 Event History 中。Worker 通过重放历史来恢复状态。
  • 生命周期:Workflow 可以运行数毫秒到数年,支持挂起、恢复、定时器、信号和查询。

2.3.2 Activity(活动)

  • 定义:Activity 是工作流中的实际业务操作,例如调用支付接口、写入数据库、发送邮件等。
  • 无约束:Activity 代码没有确定性要求,可以执行任何 I/O 操作、调用第三方服务、生成随机数等。
  • 执行模型:Activity 由 Worker 在独立线程或进程中执行,执行结果(成功/失败/超时)通过 Task Token 上报给 Server。
  • 重试与超时:Activity 支持独立的重试策略(Retry Policy)和超时配置(StartToCloseTimeout、ScheduleToCloseTimeout 等)。
对比维度 Workflow Activity
主要职责 编排与协调 执行具体业务逻辑
确定性要求 必须确定性 无要求
可执行 I/O 禁止直接 I/O 允许任意 I/O
重试机制 通过 Activity 重试间接实现 内置独立重试策略
执行时长 可极长(年级) 通常较短(秒/分钟级)
状态恢复方式 事件历史重放 心跳 + 结果上报

2.4 Workflow 与 Activity 的交互流程

下图展示了一个典型工作流中,Client、Server、Worker 三者如何协作完成一次 Workflow + Activity 的执行。

Workflow 与 Activity 交互时序图 Workflow + Activity 执行时序 Client Server Worker 外部服务 StartWorkflow 创建 Task 执行 Workflow ScheduleActivity Activity Task 执行 Activity 返回结果 上报结果 Workflow 完成
执行要点:
  • Workflow 代码中通过 workflow.ExecuteActivity() 调用 Activity,该调用会被序列化为一个命令(Command)追加到 Event History。
  • Server 收到命令后,在对应的 Task Queue 中创建一个 Activity Task,等待 Worker 轮询。
  • Worker 执行 Activity 时,可以发送心跳(Heartbeat)来报告进度,Server 据此判断 Activity 是否存活。
  • Activity 执行完成后,Worker 将结果通过 RespondActivityTaskCompleted 上报给 Server,Server 再调度下一个决策任务。

2.5 代码示例:最小 Workflow + Activity

以下是一个使用 Temporal Go SDK 的完整示例,展示 Workflow 如何调用 Activity。

// activity.go
package myapp

import (
    "context"
    "fmt"
    "go.temporal.io/sdk/activity"
)

// GreetActivity 是一个简单的 Activity,返回问候语
func GreetActivity(ctx context.Context, name string) (string, error) {
    logger := activity.GetLogger(ctx)
    logger.Info("GreetActivity started", "name", name)
    return fmt.Sprintf("Hello, %s!", name), nil
}
// workflow.go
package myapp

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

// GreetWorkflow 是一个工作流,调用 GreetActivity
func GreetWorkflow(ctx workflow.Context, name string) (string, error) {
    // 设置 Activity 执行选项:超时 5 秒
    ao := workflow.ActivityOptions{
        StartToCloseTimeout: 5 * time.Second,
    }
    ctx = workflow.WithActivityOptions(ctx, ao)

    var result string
    // 调用 Activity,等待结果
    err := workflow.ExecuteActivity(ctx, GreetActivity, name).Get(ctx, &result)
    if err != nil {
        return "", err
    }
    return result, nil
}
// worker.go
package main

import (
    "log"
    "go.temporal.io/sdk/client"
    "go.temporal.io/sdk/worker"
    "myapp"
)

func main() {
    c, err := client.Dial(client.Options{})
    if err != nil {
        log.Fatalln("Unable to create client", err)
    }
    defer c.Close()

    w := worker.New(c, "greeting-task-queue", worker.Options{})
    w.RegisterWorkflow(myapp.GreetWorkflow)
    w.RegisterActivity(myapp.GreetActivity)

    err = w.Run(worker.InterruptCh())
    if err != nil {
        log.Fatalln("Unable to start worker", err)
    }
}
// client.go
package main

import (
    "context"
    "fmt"
    "log"
    "go.temporal.io/sdk/client"
    "myapp"
)

func main() {
    c, err := client.Dial(client.Options{})
    if err != nil {
        log.Fatalln("Unable to create client", err)
    }
    defer c.Close()

    // 启动 Workflow
    we, err := c.ExecuteWorkflow(context.Background(),
        client.StartWorkflowOptions{
            TaskQueue: "greeting-task-queue",
        },
        myapp.GreetWorkflow, "Temporal")
    if err != nil {
        log.Fatalln("Unable to execute workflow", err)
    }

    var result string
    err = we.Get(context.Background(), &result)
    if err != nil {
        log.Fatalln("Workflow failed", err)
    }
    fmt.Printf("Workflow result: %s\n", result)
}
常见误区:
  • 不要在 Workflow 中直接调用 Activity 函数——必须通过 workflow.ExecuteActivity() 调用,否则无法保证确定性。
  • 不要将 Activity 的结果直接赋值给 Workflow 的局部变量而不使用 .Get()——这会导致异步调用未完成就继续执行。
  • Worker 必须同时注册 Workflow 和 Activity 类型,否则 Server 无法找到对应的执行代码。

2.6 小结