Temporal 深度拆解:当工作流把「断点续传」带进分布式系统——从事件溯源到 Workflow as Code 的工程全链路实战
背景介绍:从「服务崩溃」说起
想象这样一个场景:用户在你的电商系统里下了一笔订单,后端微服务链路上有 7 个步骤——库存校验、支付扣款、物流分配、积分发放、短信通知、发票生成、数据同步。当处理到第 5 步(积分发放)时,服务器突然断电,进程被强制杀死。
第二天你恢复服务,第 5 步到第 7 步的数据全丢了。用户付了钱但没收到通知,积分没到账,发票没生成。运维翻日志发现一半的数据孤立地存在不同的服务里——这不是 bug,这是分布式系统的原罪:状态丢失。
我们通常怎么解决这个问题?补偿事务(Saga 模式)、消息队列重试、手写幂等检查点、外部持久化状态机……每一种方案都是工程量巨大的土制轮子,而且彼此之间难以组合。
Temporal 做的事,就是把「断点续传」这件事,做成基础设施级别的 first-class 支持——你写业务代码,后端自动保证:哪怕服务器重启 100 次,每一步执行到哪、结果是什么、失败后怎么重试,全部由框架兜底。
这不是新瓶装旧酒。Temporal 背后是一套完整的分布式持久执行理论——事件溯源(Event Sourcing)+ 工作流即代码(Workflow as Code)+ 确定性重放(Deterministic Replay)——组合在一起,重新定义了什么叫「可靠的分布式业务逻辑」。
什么是 Temporal:一个精确的定义
Temporal 的官方定位是:持久执行平台(Durable Execution Platform)。
拆解这三个词:
- 持久(Durable):代码执行到哪、结果是什么,重启进程、重启机器、甚至 Temporal 服务端崩溃再恢复后,执行状态不丢失
- 执行(Execution):在这里不是"跑一个函数",而是"跑一个可以在任意时间暂停和恢复的业务流程"
- 平台(Platform):不是库,不是框架,而是一个独立运行的中间件服务(Temporal Server),业务代码通过 SDK 连接
官方一句话:"Temporal enables developers to build scalable applications without sacrificing productivity or reliability."
它的前身是 Uber 的内部项目 Cadence。Cadence 的创始团队离开 Uber 后,成立了 Temporal Technologies,于 2022 年将 Temporal 推向开源并持续活跃至今。
核心概念拆解:Workflow、Activity、Task Queue
Temporal 的模型里有三个核心概念,理解它们之间的边界是写好 Temporal 应用的第一步。
1. Workflow(工作流)
Workflow 是业务逻辑的核心载体——它定义了整个业务流程的编排逻辑,但你写的 Workflow 代码本身是纯同步、纯确定性的。
// 订单处理 Workflow 示例(Go SDK)
func OrderProcessingWorkflow(ctx workflow.Context, orderID string) (string, error) {
// 1. 查询订单详情
var order Order
err := workflow.ExecuteActivity(ctx, GetOrderActivity, orderID).Get(ctx, &order)
if err != nil {
return "", err
}
// 2. 扣减库存
err = workflow.ExecuteActivity(ctx, DeductInventoryActivity, order.Items).Get(ctx, nil)
if err != nil {
return "", err // 自动重试,由 Temporal 兜底
}
// 3. 处理支付(带超时控制)
ctxWithTimeout, cancel := workflow.WithTimeout(ctx, 24*time.Hour)
defer cancel()
paymentResult, err := workflow.ExecuteActivity(
ctxWithTimeout,
ProcessPaymentActivity,
order.Payment,
).Get(ctx, &PaymentResult{})
// 4. 通知下游
workflow.ExecuteActivity(ctx, NotifyActivity, orderID).Get(ctx, nil)
return "completed", nil
}
关键约束:Workflow 代码必须是确定性的(DETERMINISTIC)。这意味着:
- 不能直接调用
time.Now(),要用workflow.Now(ctx) - 不能直接调用
sleep,要用workflow.Sleep(ctx, duration) - 不能访问外部数据库或网络(这些全部在 Activity 里)
- 不能有随机数裸调用(要用 workflow 提供的 RNG)
为什么?因为 Temporal 需要重放(Replay) Workflow 的执行历史。同一个输入,同一段代码,必须产生完全相同的执行路径和结果。
2. Activity(活动)
Activity 是实际干活的单元——所有的 I/O 操作、外部 API 调用、数据库写入,全部放在 Activity 里。
// 扣减库存 Activity
func DeductInventoryActivity(ctx context.Context, items []OrderItem) error {
logger := activity.GetLogger(ctx)
logger.Info("扣减库存中...", "items", items)
// 这里是真实的业务逻辑:数据库操作、外部服务调用
err := db.InventoryStore.BatchDeduct(ctx, items)
if err != nil {
logger.Error("库存扣减失败", "error", err)
return err
}
logger.Info("库存扣减成功")
return nil
}
Activity 有几个重要特性:
- 可重试:Activity 执行失败后,Temporal 自动根据配置重试(指数退避、固定间隔等)
- 有超时:可以设置 Activity 的执行超时、重试超时
- 有心跳:长时运行的 Activity 可以定期发送心跳,宕机检测更精确
- 有版本:Activity 逻辑变更后,可以通过版本号控制新老逻辑共存
3. Task Queue(任务队列)
Task Queue 是连接 Workflow 和 Worker 的桥梁。Temporal Server 通过 Task Queue 分发任务给注册的 Worker 进程。
// Worker 注册
func main() {
c, _ := client.NewClient(client.Options{})
defer c.Close()
worker := client.NewWorker(c, "order-processing-queue")
worker.RegisterWorkflow(OrderProcessingWorkflow)
worker.RegisterActivity(DeductInventoryActivity)
worker.RegisterActivity(ProcessPaymentActivity)
if err := worker.Run(background.Background()); err != nil {
log.Fatal(err)
}
}
Task Queue 的设计允许:
- 多个 Worker 进程监听同一个队列,实现水平扩展
- 不同类型的 Workflow 走不同队列,互不干扰
- 分片隔离:高优先级订单走专属队列
架构深度解析:Temporal Server 是怎么做到的
事件溯源:Workflow 执行状态的全量记录
Temporal Server 存储的不是"当前状态",而是整个执行过程的事件序列(History)。
当 Workflow 执行时,每发生一件事(Activity 调度、Activity 完成、Timer 触发等),都会生成一条 History Event 写入持久化存储:
WorkflowExecutionStarted
→ ActivityTaskScheduled(调度扣库存 Activity)
→ ActivityTaskCompleted(扣库存完成)
→ ActivityTaskScheduled(调度支付 Activity)
→ ActivityTaskStarted(支付开始)
→ WorkflowExecutionCompleted
这些事件就是 Workflow 的「全量执行记录」。当 Workflow 进程崩溃后重启,它会从 Temporal Server 重新拉取这份 History,然后从头重放一遍——注意是重放,不是从头执行。重放时:
- Activity 调度指令再次发出,但 Temporal Server 识别到这些 Activity 已经执行过,直接返回缓存结果
- Workflow 代码看到已有的结果,就像什么都没发生过一样继续往下走
这就是 Workflow as Code 的核心:你用普通代码写业务流程,Temporal 自动给你加上断点续传能力。
持久化层:多层存储架构
Temporal Server 的数据分为三层:
┌─────────────────────────────────────────┐
│ Frontend Service │ ← gRPC 入口,接收请求
├─────────────────────────────────────────┤
│ History Service │ ← 管理 Workflow History
├─────────────────────────────────────────┤
│ Matching Service │ Worker Service │ ← 任务分发 / Worker 管理
├─────────────────────────────────────────┤
│ Persistence Layer │ ← 真正存数据的地方
│ (Cassandra / MySQL / PostgreSQL) │
└─────────────────────────────────────────┘
支持的持久化后端:
- Cassandra(官方推荐,大规模生产用)
- MySQL / PostgreSQL(方便自托管,中小规模)
- SQLite(仅限开发/测试)
这个架构的优势在于:历史数据无限增长时可以归档。旧的 Completed Workflow 的 History 可以定期归档到冷存储,主存储只保留活跃 Workflow 的数据。
命名空间隔离与多租户
Temporal Server 支持多 Namespace:
temporal operator namespace create -n production
temporal operator namespace create -n staging
temporal operator namespace create -n dev
每个 Namespace 下的 Workflow 完全隔离,适合多团队、多环境共存。Temporal Cloud 提供的 SaaS 服务就是基于多租户 Namespace 实现的。
Go SDK 深度实战:从零构建一个电商订单系统
环境准备
# 启动 Temporal Server(Docker Compose,最快方式)
git clone https://github.com/temporalio/docker-compose.git
cd docker-compose/other/base
docker-compose up
# 安装 Go SDK
go get go.temporal.io/sdk
完整示例:电商订单全流程
package main
import (
"context"
"fmt"
"log"
"time"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
)
// ============ 领域模型 ============
type Order struct {
ID string
UserID string
Items []OrderItem
Payment PaymentInfo
TotalAmt float64
}
type OrderItem struct {
SKU string
Quantity int
Price float64
}
type PaymentInfo struct {
Method string
AccountID string
Amount float64
}
type PaymentResult struct {
TxID string
Status string
Timestamp time.Time
}
// ============ Activity 实现 ============
// GetOrderActivity 模拟从数据库加载订单
func GetOrderActivity(ctx context.Context, orderID string) (*Order, error) {
// 模拟从 DB 读取
return &Order{
ID: orderID,
UserID: "user-123",
Items: []OrderItem{
{SKU: "SKU-001", Quantity: 2, Price: 99.90},
{SKU: "SKU-002", Quantity: 1, Price: 199.00},
},
Payment: PaymentInfo{
Method: "credit_card",
AccountID: "acc-456",
Amount: 398.80,
},
TotalAmt: 398.80,
}, nil
}
// DeductInventoryActivity 扣减库存
func DeductInventoryActivity(ctx context.Context, items []OrderItem) error {
// 真实场景:db.Inventory.BatchDeduct(ctx, items)
log.Printf("[DeductInventory] 扣减库存: %+v", items)
return nil
}
// ProcessPaymentActivity 处理支付(可能超时/失败)
func ProcessPaymentActivity(ctx context.Context, payment PaymentInfo) (*PaymentResult, error) {
log.Printf("[ProcessPayment] 开始处理支付: %+v", payment)
// 模拟支付网关调用,可能失败
// 真实场景:stripe.Charge.Create(ctx, ...)
return &PaymentResult{
TxID: fmt.Sprintf("tx-%d", time.Now().Unix()),
Status: "success",
Timestamp: time.Now(),
}, nil
}
// SendNotificationActivity 发送通知
func SendNotificationActivity(ctx context.Context, userID, message string) error {
log.Printf("[Notify] 通知用户 %s: %s", userID, message)
return nil
}
// UpdateOrderStatusActivity 更新订单状态
func UpdateOrderStatusActivity(ctx context.Context, orderID, status string) error {
log.Printf("[UpdateStatus] 订单 %s 状态更新为: %s", orderID, status)
return nil
}
// ============ Workflow 实现 ============
// OrderFulfillmentWorkflow 订单履约工作流
func OrderFulfillmentWorkflow(ctx workflow.Context, orderID string) (string, error) {
// === 阶段 1:加载订单 ===
ao := workflow.ActivityOptions{
StartToCloseTimeout: 10 * time.Second,
RetryPolicy: &workflow.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumAttempts: 3,
},
}
ctx = workflow.WithActivityOptions(ctx, ao)
var order Order
err := workflow.ExecuteActivity(ctx, GetOrderActivity, orderID).Get(ctx, &order)
if err != nil {
return "", fmt.Errorf("加载订单失败: %w", err)
}
// === 阶段 2:并发执行库存扣减 + 支付 ===
// 这两个操作可以并行,因为库存扣减和支付本身互不依赖
futures := []workflow.Future{
workflow.ExecuteActivity(ctx, DeductInventoryActivity, order.Items),
workflow.ExecuteActivity(ctx, ProcessPaymentActivity, order.Payment),
}
// 等待两个 Activity 都完成(任一失败则整个 Workflow 进入 Retry 状态)
for _, f := range futures {
if err := f.Get(ctx, nil); err != nil {
return "", fmt.Errorf("并发阶段失败: %w", err)
}
}
// === 阶段 3:支付成功后,发货前延迟 30 分钟(可取消的等待) ===
// 用户可以在 30 分钟内取消订单
cancelCtx, cancel := workflow.NewCancelExternalWorkflowContext(ctx, orderID)
defer cancel()
selector := workflow.NewSelector(ctx)
var cancelled bool
// 启动一个等待协程,监听取消信号
workflow.ExecuteActivity(ctx, func(ctx context.Context) error {
// 等待 30 分钟,或者收到取消信号
workflow.Sleep(ctx, 30*time.Minute)
return nil
})
// 同时监听外部取消请求
selector.AddFuture(futures[0], func(f workflow.Future) {})
// 这里简化了,实际用 Signal 机制实现取消
// 30 分钟超时等待
workflow.Sleep(ctx, 30*time.Minute)
// === 阶段 4:发货 & 通知 ===
err = workflow.ExecuteActivity(ctx, UpdateOrderStatusActivity, order.ID, "shipped").Get(ctx, nil)
if err != nil {
return "", fmt.Errorf("发货失败: %w", err)
}
notificationMsg := fmt.Sprintf("您的订单 %s 已发货,总金额 %.2f 元", orderID, order.TotalAmt)
err = workflow.ExecuteActivity(ctx, SendNotificationActivity, order.UserID, notificationMsg).Get(ctx, nil)
return fmt.Sprintf("订单 %s 履约完成", orderID), nil
}
// ============ Worker 启动 ============
func main() {
// 连接 Temporal Server
c, err := client.NewClient(client.Options{
HostPort: client.DefaultHostPort, // "localhost:7233"
Namespace: "default",
})
if err != nil {
log.Fatal("连接 Temporal Server 失败:", err)
}
defer c.Close()
// 创建 Worker
w := worker.New(c, "order-fulfillment", worker.Options{})
// 注册 Workflow 和 Activity
w.RegisterWorkflow(OrderFulfillmentWorkflow)
w.RegisterActivity(GetOrderActivity)
w.RegisterActivity(DeductInventoryActivity)
w.RegisterActivity(ProcessPaymentActivity)
w.RegisterActivity(UpdateOrderStatusActivity)
w.RegisterActivity(SendNotificationActivity)
log.Println("Worker 启动,监听队列: order-fulfillment")
if err := w.Run(worker.InterruptCh()); err != nil {
log.Fatal("Worker 运行失败:", err)
}
}
// ============ 客户端调用 ============
func StartOrderWorkflow() {
c, _ := client.NewClient(client.Options{})
defer c.Close()
workflowID := fmt.Sprintf("order-%d", time.Now().Unix())
we, err := c.ExecuteWorkflow(
context.Background(),
client.StartWorkflowOptions{
ID: workflowID,
TaskQueue: "order-fulfillment",
WorkflowRunTimeout: 24 * time.Hour,
WorkflowTaskTimeout: 10 * time.Second,
},
OrderFulfillmentWorkflow,
"ORD-20260818-001",
)
if err != nil {
log.Fatal("启动 Workflow 失败:", err)
}
log.Printf("Workflow 已启动,ID: %s,RunID: %s", we.GetID(), we.GetRunID())
// 同步等待结果(也可以异步查询状态)
var result string
err = we.Get(context.Background(), &result)
if err != nil {
log.Printf("Workflow 执行出错: %v", err)
} else {
log.Printf("Workflow 执行结果: %s", result)
}
}
长时等待与信号机制
Temporal 的一个杀手级特性:Workflow 可以随时等待外部信号(Signal)。
// 带人工审批的 Workflow
func ApprovalWorkflow(ctx workflow.Context, orderID string) (string, error) {
// 设置 7 天超时等待审批
approvalCh := workflow.NewChannel(ctx)
workflow.Subscribe(ctx, "approval_signal", approvalCh)
// 创建一个 7 天后的超时 Future
timeoutTimer := workflow.NewTimer(ctx, 7*24*time.Hour)
selector := workflow.NewSelector(ctx)
selector.AddReceive(approvalCh, func(c workflow.ReceiveChannel, more bool) {
var approval Approval
c.Receive(ctx, &approval)
if approval.Approved {
log.Printf("审批通过")
} else {
log.Printf("审批拒绝")
}
})
selector.AddFuture(timeoutTimer, func(f workflow.Future) {
log.Printf("审批超时")
})
selector.Select(ctx) // 阻塞等待,直到收到信号或超时
return "processed", nil
}
确定性重放:Workflow 代码热更新的工程秘密
为什么需要确定性
Temporal 的一个常见误解是:Activity 可以随便改,Workflow 代码改了就坏了。
实际上,恰恰相反:
- Activity 改逻辑 = 需要显式版本控制(
workflow.GetVersion) - Workflow 改编排逻辑 = 只要保持确定性,可以通过 Replayer 测试后直接上线
原因是:Temporal 的重放是基于 History 的。如果新的 Workflow 代码产生的事件序列和旧 History 匹配(骨架不变),那么重放后结果一致。
Workflow 代码热更新实战
func OrderWorkflowV1(ctx workflow.Context, orderID string) (string, error) {
// 原来的逻辑:串行执行
err := workflow.ExecuteActivity(ctx, Step1Activity).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, Step2Activity).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, Step3Activity).Get(ctx, nil)
return "done", nil
}
func OrderWorkflowV2(ctx workflow.Context, orderID string) (string, error) {
// 新的逻辑:Step1 和 Step2 并行执行
// 但旧 Workflow 的 History 是串行记录事件
// V2 代码重放时,必须兼容 V1 的事件骨架
// 方案:通过 workflow.GetVersion 检测版本并做兼容处理
version := workflow.GetVersion(ctx, "parallel-steps", workflow.DefaultVersion, 1)
if version == workflow.DefaultVersion {
// 走 V1 逻辑(串行):历史重放路径
err := workflow.ExecuteActivity(ctx, Step1Activity).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, Step2Activity).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, Step3Activity).Get(ctx, nil)
return "done", nil
} else {
// 走 V2 逻辑(并行):新代码路径
f1 := workflow.ExecuteActivity(ctx, Step1Activity)
f2 := workflow.ExecuteActivity(ctx, Step2Activity)
f1.Get(ctx, nil)
f2.Get(ctx, nil)
err := workflow.ExecuteActivity(ctx, Step3Activity).Get(ctx, nil)
return "done", err
}
}
Replayer:零风险上线测试
// 使用 Replayer 做代码验证
func TestWorkflowUpgrade() {
replayer := client.NewReplayer()
replayer.RegisterWorkflow(OrderWorkflowV2)
// 从生产环境导出历史数据(脱敏后)
history, _ := loadHistoryFromFile("order_workflow_history.json")
// 用 V2 代码重放历史,验证结果一致性
result, err := replayer.ReplayWorkflowExecution(
context.Background(),
history,
"order-123",
)
if err != nil {
fmt.Printf("重放失败,代码不兼容: %v\n", err)
} else {
fmt.Printf("重放成功,结果: %s\n", result)
}
}
故障容错:Temporal 的重试策略与死心处理
Activity 重试配置
Activity 的失败重试是最直观的容错机制:
ao := workflow.ActivityOptions{
StartToCloseTimeout: 5 * time.Minute,
RetryPolicy: &workflow.RetryPolicy{
InitialInterval: 1 * time.Second, // 首次重试等待 1 秒
BackoffCoefficient: 2.0, // 指数退避:1s → 2s → 4s → 8s...
MaximumInterval: 5 * time.Minute, // 最大间隔不超过 5 分钟
MaximumAttempts: 5, // 最多重试 5 次
NonRetryableErrorTypes: []string{ // 这些错误类型不重试
"InvalidPaymentMethod",
"InsufficientInventory",
},
},
}
Workflow 级别的失败策略
Workflow 执行失败有三种处理方式:
wo := client.StartWorkflowOptions{
// 方案 1:无限重试(默认)
WorkflowExecutionErrorWhenStillOpen: true,
// 方案 2:超时后标记失败但不终止(可手动处理)
WorkflowExecutionTimeout: 24 * time.Hour,
// 方案 3:使用 Idempotency Key 防止重复启动
IdempotencyKey: "order-" + orderID,
}
死心队列(Dead Letter Queue)
Activity 超过最大重试次数后,会进入 Suspended 状态。Temporal 提供了手动干预机制:
# 查看失败的任务
temporal --namespace default task-queue describe order-fulfillment
# 重试失败的任务
temporal --namespace default workflow reset bad-workflow-id --reason "手动重置"
性能优化:生产环境必须关注的几个数字
History 的体积管理
长期运行的 Workflow 会积累大量 History Event。几个优化手段:
1. 大型数据结构存 Object Store,不存 Temporal
// ❌ 不推荐:把大对象放 Activity 参数
workflow.ExecuteActivity(ctx, ProcessOrderActivity, largeOrderData) // 可能 MB 级别
// ✅ 推荐:传 ID,实际数据存 S3/MinIO
workflow.ExecuteActivity(ctx, ProcessOrderActivity, OrderRef{
ID: "order-123",
StoragePath: "s3://bucket/orders/order-123.json",
})
2. 定期归档(Archival)
Temporal 支持将已完成的 Workflow History 归档到冷存储:
# temporal-server 配置
archival:
enabled: true
history:
provider:
s3:
bucket: "temporal-archives"
region: "us-east-1"
3. Search Attributes:不用 History 也能查状态
// 给 Workflow 打标签(存索引,不存 History)
c.UpsertWorkflowSearchAttributes(ctx, we.GetID(), map[string]interface{}{
"CustomerID": "cust-789",
"OrderStatus": "pending",
"Priority": 1,
})
// 查询所有未完成的高优先级订单
query := `OrderStatus = "pending" AND Priority = 1`
results, _ := c.ListWorkflow(context.Background(), &client.ListWorkflowsOptions{
Query: query,
})
Worker 水平扩展
// 水平扩展 Worker:不增加队列,增加 Worker 数量
w := worker.New(c, "order-fulfillment", worker.Options{
ConcurrentActivityExecutionSize: 100, // 每个 Worker 最大并发 Activity 数
MaxConcurrentWorkflowTaskExecutionSize: 50, // 最大并发 Workflow 数
})
// 对于 CPU 密集型 Workflow,可以增加 Worker 数量
// Temporal 自动做负载均衡
单机性能基准(参考数据)
根据 Temporal 官方测试和社区反馈:
- 单个 Worker 进程可处理 ~1000 活跃 Workflow
- Activity 吞吐量:~500 次/秒(取决于业务逻辑复杂度)
- History 查询延迟:P99 < 100ms(使用 Cassandra)
Temporal vs 其他方案:为什么值得学
| 维度 | Temporal | Saga 模式 | 传统消息队列 |
|---|---|---|---|
| 状态持久化 | ✅ 原生支持 | ❌ 需自己实现 | ❌ 消息不保存状态 |
| 失败重试 | ✅ 可配置、自动 | ⚠️ 补偿事务、复杂 | ⚠️ 手动重推 |
| 流程可见性 | ✅ Web UI 全链路 | ❌ 分散在服务里 | ❌ 靠日志拼凑 |
| 代码模型 | ✅ 普通代码 | ⚠️ 需要 DSL 或框架 | ⚠️ 消息格式约定 |
| 长时间等待 | ✅ Timer/Signal 原生 | ❌ 很难优雅处理 | ❌ 消息有 TTL |
| 可测试性 | ✅ 本地测试模式 | ⚠️ 依赖集成测试 | ❌ 难以测试 |
Temporal 的本质价值:让你用写普通业务代码的方式,写出具备分布式事务能力的系统。不需要学新 DSL,不需要理解复杂的 BPMN,不需要手写补偿逻辑。
总结:Temporal 教会我们什么
1. 事件溯源是解决分布式状态丢失的银弹
把执行过程记录下来,而不是只记录最终状态。这是 Temporal 最核心的设计哲学,也是一个可以迁移到任何系统的思想——写时记录,而非读时重构。
2. 确定性是可靠分布式系统的门槛
Temporal 的 Workflow 约束(不能用 time.Now(),不能裸调用外部服务)看起来很严格,但正是这些约束,换来了"重放一定正确"的能力。在分布式系统中,不确定性是 bug 的温床。
3. 平台级抽象比库级抽象更适合核心业务逻辑
当"可靠执行"成为业务的核心需求时,把它做成基础设施(独立的 Temporal Server),比做成业务代码的一部分(每个服务自己写重试逻辑),要可靠得多。这也是微服务架构演进的必然方向:把横切关注点(cross-cutting concerns)平台化。
4. Workflow as Code 是下一代业务流程的表达方式
传统的 BPMN/Saga 模式需要学专门的建模语言,Temporal 让你用 Go/TypeScript/Java 直接写业务流程。这降低了门槛,也提高了可维护性——你不需要一个专门的业务流程工程师,普通的业务开发者就能写出可靠的分布式工作流。
附:学习路径推荐
- 入门:本地用 Docker Compose 启动 Temporal Server,跑通官方的 hello-world 示例
- 进阶:用 Temporal 改写一个现有的多步骤业务流程(订单、审批、注册等)
- 生产:研究 History 归档策略、Search Attributes 查询、Worker 扩缩容
- 深度:看 Temporal Server 源码(Go 语言质量极高,是学习分布式系统的好材料)
GitHub: https://github.com/temporalio/temporal
官方文档: https://docs.temporal.io/