在 Encore 应用中集成 Temporal:工作流编排的完整实战指南
2026/9/15 15:32:21 网站建设 项目流程

在 Encore 应用中集成 Temporal:工作流编排的完整实战指南

【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore

导读

Temporal 是一套用于构建高可靠系统的工作流编排(Workflow Orchestration)平台,能够以持久化、可重放的方式编排分布式业务流程。Encore 作为面向云时代的后端开发平台,与 Temporal 可以无缝共存——本指南将带你从零搭建一套"Encore 业务 API + Temporal 工作流"的完整链路:包括本地与云端 Temporal 集群的选型配置、在 Encore 服务中启动 Worker、定义 Workflow 与 Activity、通过 Encore API 触发工作流,以及利用 Encore 的环境感知配置能力自动切换不同环境下的 Temporal 集群地址。读完本文,你将掌握在 Encore 应用中生产级接入 Temporal 的完整实操方案。

为什么在 Encore 中使用 Temporal

Temporal 的核心价值在于把"业务流程"抽象为可持久化、可重试、可恢复的工作流:即使进程崩溃、机器宕机,工作流的执行状态依然被保留,恢复后可以从断点继续执行。而 Encore 负责应用的后端基础设施(API 路由、数据库、配置、部署等),两者关注点正交、互不冲突,因此 Encore 官方文档明确说明"Encore works great with Temporal",二者可以非常自然地组合使用。

典型的组合方式是:

  • Encore:负责对外暴露 HTTP API、管理服务生命周期、处理环境感知配置、部署到云端;
  • Temporal:负责在 API 背后执行长时间运行、多步骤、需要可靠性保障的业务流程。

第一步:搭建 Temporal 集群

在接入之前,你需要准备至少两个 Temporal 集群:一个用于本地开发,一个用于云端环境。官方推荐的组合是:

场景推荐方案说明
本地开发Temporalite轻量级、单进程即可运行的 Temporal 开发版
云端环境Temporal CloudTemporal 官方托管服务,或自建自托管的 Temporal 集群

Temporalite 特别适合本地开发,因为它不需要依赖外部数据库(如 Cassandra/PostgreSQL),开箱即用。云端则优先考虑 Temporal Cloud,省去运维集群的成本;如果你的组织已经有自托管集群,同样可以在云端环境直接复用。

第二步:创建 Encore 服务并启动 Temporal Worker

2.1 新建 greeting 服务

首先在 Encore 应用中创建一个名为greeting的新服务。服务启动时会创建 Temporal client 与 worker,核心代码如下(greeting/greeting.go):

package greeting import ( "context" "fmt" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "encore.dev" ) // Use an environment-specific task queue so we can use the same // Temporal Cluster for all cloud environments. var ( envName = encore.Meta().Environment.Name greetingTaskQueue = envName + "-greeting" ) //encore:service type Service struct { client client.Client worker worker.Worker } func initService() (*Service, error) { c, err := client.Dial(client.Options{}) if err != nil { return nil, fmt.Errorf("create temporal client: %v", err) } w := worker.New(c, greetingTaskQueue, worker.Options{}) err = w.Start() if err != nil { c.Close() return nil, fmt.Errorf("start temporal worker: %v", err) } return &Service{client: c, worker: w}, nil } func (s *Service) Shutdown(force context.Context) { s.client.Close() s.worker.Stop() }

这段代码里有几个值得注意的设计点:

  • 环境感知的任务队列命名:通过encore.Meta().Environment.Name获取当前环境名(本地开发时值为"local",云端环境则是对应的环境名),拼接到任务队列名中。这样做的意义是:同一个 Temporal 集群可以服务于所有云环境(如 staging、prod),不同环境使用不同的任务队列,天然隔离、互不干扰。
  • 服务生命周期管理:利用 Encore 的//encore:service声明式服务语法,initService负责初始化(创建 client、启动 worker),Shutdown负责优雅关闭(停止 worker、关闭 client)。

2.2 encore.Meta() 的底层原理

encore.Meta()由 Encore 运行时提供,在 runtimes/go/pkgfn.go 中导出,实际实现位于 runtimes/go/meta.go。其中EnvironmentMeta结构体包含三个字段(见 runtimes/go/meta.go):

  • Name:当前环境名,本地开发时为"local"
  • Type:环境类型,包括productiondevelopmentephemeraltest等(runtimes/go/meta.go);
  • Cloud:当前运行的云提供商,本地运行时为CloudLocal(runtimes/go/meta.go)。

这些元数据由运行时的配置注入(mgr.runtime.EnvNamemgr.runtime.EnvCloud),因此你可以在任何环境下安全地读取它们来定制业务逻辑——这正是"环境特定任务队列"能够成立的基础。

第三步:定义 Workflow 与 Activity

Workflow 和 Activity 需要与服务放在同一个模块下,以便注册。这里在greeting服务内部新建一个workflow子包,将两者分文件组织:

3.1 定义 Workflow(greeting/workflow/workflow.go

package workflow import ( "time" "go.temporal.io/sdk/workflow" ) func Greeting(ctx workflow.Context, name string) (string, error) { options := workflow.ActivityOptions{ StartToCloseTimeout: time.Second * 5, } ctx = workflow.WithActivityOptions(ctx, options) var result string err := workflow.ExecuteActivity(ctx, ComposeGreeting, name).Get(ctx, &result) return result, err }

这里为 Activity 设置了StartToCloseTimeout: 5s超时,这是 Temporal 可靠性的关键之一:一旦 Activity 执行超过超时时间,Temporal 会按照配置的重试策略处理,避免工作流无限挂起。

3.2 定义 Activity(greeting/workflow/activity.go

package workflow import ( "context" "fmt" ) func ComposeGreeting(ctx context.Context, name string) (string, error) { greeting := fmt.Sprintf("Hello %s!", name) return greeting, nil }

Activity 是真正执行业务动作的函数(调用外部服务、读写数据库等),而 Workflow 负责编排这些 Activity 的执行顺序与补偿逻辑。Temporal 对二者有明确的边界要求:Workflow 代码必须是确定性的(deterministic),因此 IO 操作都应放在 Activity 中完成。

3.3 注册 Workflow 与 Activity

回到greeting服务,在initService中把二者注册到 worker:

-- greeting/greeting.go -- // Import the package at the top: import "encore.app/greeting/workflow" // Add these lines to `initService`, below the call to `worker.New`: w.RegisterWorkflow(workflow.Greeting) w.RegisterActivity(workflow.ComposeGreeting)

注册完成后,Worker 就知道该监听greetingTaskQueue队列中的Greeting工作流及其关联的ComposeGreeting活动。

第四步:通过 Encore API 触发工作流

新建greeting/greet.go,定义一个公共 API 来触发工作流并返回结果:

package greeting import ( "context" "encore.app/greeting/workflow" "encore.dev/rlog" "go.temporal.io/sdk/client" ) type GreetResponse struct { Greeting string } //encore:api public path=/greet/:name func (s *Service) Greet(ctx context.Context, name string) (*GreetResponse, error) { options := client.StartWorkflowOptions{ ID: "greeting-workflow", TaskQueue: greetingTaskQueue, } we, err := s.client.ExecuteWorkflow(ctx, options, workflow.Greeting, name) if err != nil { return nil, err } rlog.Info("started workflow", "id", we.GetID(), "run_id", we.GetRunID()) // Get the results var greeting string err = we.Get(ctx, &greeting) if err != nil { return nil, err } return &GreetResponse{Greeting: greeting}, nil }

要点说明:

  • //encore:api public path=/greet/:name声明了一个公共 API 端点,路径参数:name会自动绑定到name参数,无需手工解析 URL;
  • StartWorkflowOptions中指定了工作流 ID(ID)与任务队列(TaskQueue),后者使用了之前的环境感知变量greetingTaskQueue
  • ExecuteWorkflow提交工作流后,通过we.GetID()/we.GetRunID()拿到工作流标识,并用rlog.Info记录日志(Encore 的 rlog 会自动集成分布式追踪);
  • we.Get(ctx, &greeting)会阻塞等待工作流执行完成并把结果反序列化到greeting

第五步:本地运行与验证

一切就绪后,在两个独立终端分别启动 Temporalite 和 Encore 应用:

$ temporalite start --namespace default $ encore run

然后通过 cURL 调用 API 进行验证:

$ curl 'http://localhost:4000/greeting/Temporal' {"Greeting": "Hello Temporal!"}

如果看到上述输出,说明整条链路(Encore API → Temporal Client → Worker 队列 → Workflow → Activity → 返回结果)已经跑通。你也可以通过 Encore 的 Local Development Dashboard 查看请求的追踪信息,观察工作流的调用细节。

第六步:云端运行与环境感知配置

本地验证通过后,部署到云端时需要使用 Temporal Cloud 或自托管集群。最优雅的做法是利用Encore 的配置(Config)功能,让不同环境自动使用正确的集群地址。

6.1 定义配置结构(greeting/config.go

package greeting import "encore.dev/config" type Config struct { TemporalServer string } var cfg = config.Load[*Config]()

6.2 编写环境感知的 CUE 配置(greeting/config.cue

package greeting TemporalServer: [ // These act as individual case statements if #Meta.Environment.Cloud == "local" { "localhost:7233" }, // TODO: configure this to match your own cluster address "my.cluster.address:7233", ][0] // Return the first value which matches the condition

这里展示了 Encore 配置系统最强大的能力之一:基于元数据#Meta的条件配置。列表中的每一项相当于一个 case 分支,[0]取出第一个匹配条件成立的值。在本例中:

  • 当环境云提供商为local(即encore run本地运行时)→ 使用localhost:7233
  • 其他任何环境(staging、prod 等云端环境)→ 回退到"my.cluster.address:7233",你只需把该地址替换为 Temporal Cloud 提供的命名空间地址或自托管集群地址。

#Meta.Environment.Cloud与代码中encore.Meta().Environment.Cloud一脉相承,都来自运行时元数据(对应 runtimes/go/meta.go 中CloudProvider,本地为CloudLocal)。因此配置与代码共享同一套环境事实,不会出现"配置判断的环境"与"代码运行的环境"不一致的问题。

6.3 让 Client 使用配置的地址

最后,回到greeting/greeting.go,把client.Dial改为使用配置值:

-- greeting/greeting.go -- client.Dial(client.Options{HostPort: cfg.TemporalServer})

config.Load[*Config]()的实现位于 runtimes/go/config/pkgfn.go:Encore 在编译期从各服务目录下的 CUE 文件计算配置并做校验,运行时直接反序列化为类型化的配置结构体。换言之,配置错误会在编译/启动阶段被尽早暴露,而不是等到运行时才发现。

完成以上改造后,同一份代码在本地与云端会自动连接到各自对应的 Temporal 集群,无需任何手工切换。

完整文件结构总览

至此,greeting服务完整的目录结构如下:

greeting/ ├── greeting.go # 服务定义:client、worker、注册 workflow/activity ├── greet.go # 公共 API:触发工作流 ├── config.go # 配置结构体定义 ├── config.cue # 环境感知配置(本地/云端集群地址) └── workflow/ ├── workflow.go # Workflow 定义 └── activity.go # Activity 定义

实战要点小结

  1. 环境隔离靠任务队列:通过encore.Meta().Environment.Name生成{env}-greeting任务队列名,让单个 Temporal 集群服务所有环境,各环境工作流互不干扰;
  2. 生命周期交给 Encore//encore:service+initService/Shutdown让 Temporal client 与 worker 的创建、启动、优雅关闭与 Encore 服务的生命周期完全对齐;
  3. 超时与重试是可靠性的灵魂:务必为 Activity 设置StartToCloseTimeout等超时选项,Temporal 才能依据超时执行重试/补偿策略;
  4. 配置环境感知化:善用 Encore 的 CUE 配置与#Meta.Environment.Cloud条件,一处配置、全环境生效;
  5. 尽早验证:本地用 Temporalite 跑通端到端链路后,再部署云端,减少云端调试成本。

现在,你的 Encore 应用已经具备了与 Temporal 深度集成的能力——既拥有 Encore 带来的极简基础设施体验,也拥有 Temporal 提供的高可靠工作流编排能力。

【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询