4. 第一个Temporal工作流:编写HelloWorld工作流、注册活动、启动工作流执行
本章将带领你从零搭建第一个Temporal工作流——HelloWorld。通过这个最小化示例,你将掌握Temporal工作流开发的核心三要素:工作流定义、活动实现以及客户端启动。这是后续所有复杂调度与OpenAPI扩展的基础。
4.1 工作流与活动的角色划分
在Temporal中,工作流(Workflow)负责编排业务逻辑的执行顺序,而活动(Activity)负责执行具体的业务操作。两者通过任务队列(Task Queue)进行通信。下表列出了它们的关键区别:
| 维度 | 工作流(Workflow) | 活动(Activity) |
|---|---|---|
| 职责 | 编排与协调,决定“做什么” | 执行具体任务,决定“怎么做” |
| 执行时长 | 可长期运行(天/月/年) | 通常较短(秒/分钟) |
| 状态持久化 | 自动持久化,支持重放 | 不自动持久化,需自行处理 |
| 重试策略 | 默认不重试 | 默认支持重试 |
| 代码限制 | 必须确定性(无随机/IO) | 无限制(可调用外部API) |
设计原则:将所有可能失败的操作(如网络请求、文件读写)放入活动,工作流只负责调用活动并处理其结果。这能最大化利用Temporal的自动重试与故障恢复能力。
4.2 HelloWorld工作流架构
下图展示了HelloWorld工作流的完整执行流程,包含Worker、Temporal Server以及客户端三方的交互关系:
4.3 编写HelloWorld工作流与活动
我们使用Java SDK作为示例,其他语言(Go、Python、TypeScript)的概念完全一致。整个开发过程分为三个步骤:
4.3.1 定义活动接口
活动接口使用 @ActivityInterface 注解标记,Temporal会自动扫描并注册:
import io.temporal.activity.ActivityInterface;
import io.temporal.activity.ActivityMethod;
@ActivityInterface
public interface HelloActivities {
@ActivityMethod
String sayHello(String name);
}
4.3.2 实现活动逻辑
活动实现类可以包含任意业务代码,例如调用外部API、读写数据库等:
public class HelloActivitiesImpl implements HelloActivities {
@Override
public String sayHello(String name) {
// 模拟业务处理,实际场景可替换为REST调用或数据库查询
String greeting = "Hello, " + name + "! 当前时间: " + System.currentTimeMillis();
System.out.println("活动执行: " + greeting);
return greeting;
}
}
4.3.3 定义工作流接口与实现
工作流接口使用 @WorkflowInterface 注解,实现类通过 Workflow.newActivityStub 创建活动代理:
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;
import io.temporal.workflow.Workflow;
import io.temporal.activity.ActivityOptions;
import java.time.Duration;
@WorkflowInterface
public interface HelloWorkflow {
@WorkflowMethod
String greet(String name);
}
public class HelloWorkflowImpl implements HelloWorkflow {
private final HelloActivities activities;
public HelloWorkflowImpl() {
// 配置活动选项:设置任务队列与超时
ActivityOptions options = ActivityOptions.newBuilder()
.setTaskQueue("HELLO_TASK_QUEUE")
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build();
this.activities = Workflow.newActivityStub(HelloActivities.class, options);
}
@Override
public String greet(String name) {
// 工作流编排:调用活动并返回结果
String result = activities.sayHello(name);
// 可在此处添加更多编排逻辑,例如条件判断、循环等
return result;
}
}
重要约束:工作流实现代码必须保持确定性(Deterministic)。禁止使用
System.currentTimeMillis()、Random、Thread.sleep() 等非确定性操作。如果需要获取当前时间,应使用 Workflow.currentTimeMillis()。
4.4 注册活动与启动Worker
Worker是连接Temporal Server与业务代码的桥梁。它负责注册工作流与活动实现,并持续轮询任务队列:
import io.temporal.client.WorkflowClient;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
public class HelloWorker {
public static void main(String[] args) {
// 1. 连接Temporal Server(默认地址:127.0.0.1:7233)
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
// 2. 创建Worker工厂,绑定任务队列
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker("HELLO_TASK_QUEUE");
// 3. 注册工作流与活动实现
worker.registerWorkflowImplementationTypes(HelloWorkflowImpl.class);
worker.registerActivitiesImplementations(new HelloActivitiesImpl());
// 4. 启动Worker,开始轮询
factory.start();
System.out.println("Worker 已启动,等待任务...");
}
}
4.5 启动工作流执行
客户端通过 WorkflowClient 创建工作流Stub,然后调用 @WorkflowMethod 启动执行:
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
public class HelloClient {
public static void main(String[] args) {
// 1. 连接Temporal Server
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
// 2. 配置工作流选项(指定任务队列与工作流ID)
WorkflowOptions options = WorkflowOptions.newBuilder()
.setTaskQueue("HELLO_TASK_QUEUE")
.setWorkflowId("hello-workflow-" + System.currentTimeMillis())
.build();
// 3. 创建工作流Stub
HelloWorkflow workflow = client.newWorkflowStub(HelloWorkflow.class, options);
// 4. 同步启动工作流(阻塞等待结果)
String result = workflow.greet("Temporal");
System.out.println("工作流执行结果: " + result);
}
}
4.6 完整执行流程总结
下图将上述步骤整合为一条完整的执行链路,帮助你理解从客户端提交到Worker执行再到结果返回的全过程:
4.7 常见问题与避坑指南
- 任务队列不匹配:Worker注册的任务队列名称必须与客户端
WorkflowOptions中设置的完全一致,否则工作流无法被Worker拾取。 - 活动未注册:Worker必须显式调用
registerActivitiesImplementations(),否则Temporal会抛出ActivityNotRegisteredException。 - 工作流超时:如果活动执行时间超过
setStartToCloseTimeout设置的值,Temporal会标记活动失败并触发重试。 - 非确定性错误:工作流代码中使用了
new Random()或System.currentTimeMillis(),导致重放时结果不一致,Temporal会抛出NonDeterministicWorkflowError。
验证方法:启动Worker后,运行客户端程序。如果控制台输出
Hello, Temporal! 当前时间: ...,则说明第一个Temporal工作流已成功运行。你可以通过Temporal Web UI(默认端口8233)查看工作流执行历史。