【Temproal】快速了解Temproal的核心概念以及使用

背景

公司使用的任务调度框架是Temproal,最近我在业务中使用其作为定时任务,但我发现其不只是作为定时任务的调度,同时也是一个强大的工作流编排框架,所以就来研究一下。

什么是temproal?

Temporal 是一个开源的分布式工作流编排框架,用于构建可靠、可扩展的分布式应用。它源自 Uber 的 Cadence 项目,由原核心团队于 2019 年创建,已成为云原生工作流编排的事实标准。

📌github:github.com/temporalio/...

解决了什么问题?

传统的微服务的项目中,经常使用比如消息队列来做事件驱动的业务逻辑。但这样会有一个很明显的问题,就是业务过于分散,同一个流程的逻辑会在多个服务中进行业务处理,而这样又要引发诸如:

  1. 消息的时序问题,

  2. 重试幂等问题,

  3. 事件和消息链路追踪问题

  4. 失败补偿机制

...

那么是否存在一种方案,让所有的流程编排集中起来,我们只关心业务流程

什么是编排?

这里直接引用知乎大佬的文章,我认为非常容易理解

服务[编排模式]

在分布式系统中,服务之间的协调主要有两种模式:编排(Orchestration)和编舞(Choreography)。

编排(Orchestration)的特点:

  • 有一个中央控制器(orchestrator)负责协调和管理所有服务
  • 服务之间的交互流程由 orchestrator 明确定义和控制
  • 各个服务只需要执行 orchestrator 的指令,不需要了解整体流程
  • 适合需要强一致性和明确控制流程的场景,注重内聚性

编舞(Choreography)的特点:

  • 没有中央控制器,服务之间通过事件进行松散协作
  • 每个服务独立运行,响应其他服务发出的事件
  • 服务通过发布/订阅事件来实现协作
  • 更灵活和可扩展,但整体流程较难跟踪和调试,注重解耦

举例说明

  • 编排模式:类似于旅行预订系统,由中央服务协调航班、酒店和支付服务
  • **编舞模式**\]:类似于微服务系统,订单服务发出"订单创建"事件,库存和物流服务各自响应处理

从上述对比中可以看出temproal的主要功能就是作为,流程的编排以及协调。

Temporal 本质上 是一个分布式状态机的抽象层,它通过巧妙的确定性编程模型,让开发人员可以用直观的方式处理复杂的分布式业务流程,同时保证了系统的可靠性和可维护性。

这种实现机制使得 Temporal 特别适合处理:

  • 复杂的业务流程编排
  • 需要保证可靠性的关键业务流程
  • 跨服务的分布式事务
  • 长时运行的异步操作 pipeline

核心概念

要了解Temproal,我们必须要先了解其核心的一些概念

1. Workflow(工作流)

  • 是一个长时间运行的过程,可能持续数分钟、数小时甚至数月;
  • 必须是确定性的(deterministic) ------ 相同输入、相同执行顺序必须产生相同结果;
  • 工作流不能执行 IO 操作,所有的外部操作必须交给 Activity;
  • 工作流是可以被暂停、恢复、重放的;
  • 工作流中的所有状态、步骤都会被记录在执行历史中(用于溯源和重放)。

示例:

伪代码 复制代码
workflow {
    sendEmail();
    waitForUserReply();
    chargeCreditCard();
}

2. Activity(活动)

Activity 是工作流中调用的实际执行任务,比如发送邮件、调用 HTTP 接口、写数据库等。

  • 可以包含非确定性代码,如网络请求、数据库操作;
  • 运行失败会自动重试;
  • 可以配置超时、重试策略、幂等性保障;
  • Activity 的执行是由 Temporal Worker 执行的,可以分布式并行部署。
  • 长时间运行的 Activity 可以通过心跳机制报告其进度,这些信息可以在 Temporal UI 中查看。

📌 一个工作流通常会包含多个 Activity 调用。

3. Worker(工作节点)🧵

Worker 是真正运行工作流代码或 Activity 代码的程序进程。

  • 有两类 Worker:Workflow WorkerActivity Worker

  • 通常你用同一个程序注册 workflow + activity;

  • Worker 会从 Temporal Server 拉取任务并执行,然后汇报执行结果;

    • Work会启动多个线程池,用于:
      • Workflow执行
      • Activity执行
      • Poller(拉取任务)
  • 可以弹性扩展多个实例,提高并发执行能力。

4. Task Queue(任务队列)

工作流或活动的执行请求是放入某个 Task Queue 中,由 Worker 拉取任务执行。

  • 每个 Worker 监听一个或多个任务队列;
  • 支持将不同的 Workflow/Activity 绑定到不同的 Task Queue;
  • 支持路由、负载均衡。

5. Temporal Server(核心引擎)

Temporal 的后端服务,负责协调整个系统:

  • 负责调度工作流、持久化历史、发送任务给 Worker;
  • 是高可用的服务集群,包含 Frontend、History、Matching、Worker 等子模块;
  • 数据存储在数据库中(MySQL/Postgres/Cassandra 等);
  • 你只需写业务逻辑,Server 负责调度、重放、持久化等系统复杂性。

6. Signal(信号)

Signal 是一种机制,允许外部事件或系统向正在运行的工作流发送数据或通知。

  • 工作流可以 Workflow.await() 等待某个 Signal;
  • 实现了事件驱动型工作流;
  • 类似于"向正在执行的流程推送消息"。
  • 比如可以在审批过程中,让前端传递参数作为Singnal,而工作流会在收到Signal后才会继续向后执行

7. Query(查询)

允许从外部查询工作流的当前状态,不会影响其执行状态。

  • 是只读操作;
  • 常用于前端 UI 查询进度、状态。

8. Child Workflow(子工作流)

一个工作流可以启动另一个工作流作为"子工作流"。

  • 子工作流是独立的,有自己的执行历史;
  • 可并行运行多个子工作流;
  • 主工作流可以监听子工作流完成、失败等事件。

9. Execution History(执行历史)

Temporal 会将工作流的每一步操作记录为事件,形成完整的执行日志。

  • 用于恢复(crash-safe)、重放、Debug;
  • 工作流恢复时会从历史中"回放"之前的操作,重新构建当前状态;
  • 开发者无需手动管理。

基本概念的Q&A

🤔 Q: Activity执行失败了,如何保证异常恢复。

Activity 抛出异常,Temporal 会自动捕获、重试、记录失败日志、恢复状态

默认的重试机制

  1. Temproal会自动记录异常
  2. 并且按照配置的策略进行 幂等重试( Temporal 自动重试失败的任务,但 不会重复执行已成功的步骤,并且会通过事件日志回放整个工作流状态。需要让 Activity 方法是幂等的

手动处理失败或者补偿:

1. 使用try-catch进行捕获异常
kotlin 复制代码
override fun placeOrder(userId: String): String {
    val orderId = activities.createOrder(userId)

    try {
        activities.sendNotification(orderId)
    } catch (e: Exception) {
        logger.warn("通知发送失败,将记录待补偿: $e")
        activities.logFailure(orderId)
    }

    return orderId
}
2. 调用补偿的Activity(Saga模式)

对当前或者当前以前的所有的Activity进行补偿

php 复制代码
try {
    activities.reserveInventory(orderId)
} catch (e: Exception) {
    activities.cancelOrder(orderId)
    throw e
}

🤔 Q:什么是Saga

举例说明

假设你有一个电商下单流程:

  1. 创建订单(订单服务)
  2. 扣库存(库存服务)
  3. 扣余额(用户服务)
  4. 发货(物流服务)

这四个步骤跨多个微服务,不能用传统的数据库事务。

Saga 如何处理?

步骤 执行操作 成功后 失败时
T1 创建订单 继续 T2 无需补偿
T2 扣库存 继续 T3 执行 T1 的补偿(取消订单)
T3 扣余额 继续 T4 补偿 T2(回滚库存),补偿 T1(取消订单)
T4 发货 完成 补偿 T3、T2、T1

为什么不用传统事务(2pc)?

问题 说明
2PC 会阻塞资源 所有服务都要锁定,直到提交或回滚
失败恢复难 Coordinator 崩溃可能造成数据不一致
扩展性差 适用于同库,微服务架构下基本不适用

Saga 就是为了解决这些问题而诞生的。

🤔 Q:为什么Workflow不直接执行Activity?

基于以下原因:

  1. 如果 Workflow 直接调用 Activity,可能会因外部依赖(如网络延迟、服务宕机)导致非确定性行为。 说明: 其实就是Workflow本身不支持事件的重放,导致Workflow每次执行Activity都会导致再次运行,而这样的结果并不是一致的(比如之前已经下过单了, 但是再执行又会下单)。但是当Activity交给Server,执行后,如果重放Workflow, Server会直接复用重放前的上次的结果,而不是直接调用Activity,而不会不一致。
  2. Activity 执行失败可能导致 Workflow 进程崩溃(如线程阻塞、资源泄漏)。

🤔 Q:Workflow中编排的Activity是怎么执行的?

Workflow中的Activity执行流程

  1. Workflow 代码调用 Activity
  • 你的 Workflow 代码里写调用 Activity 的代码(比如 Java 里通过 ActivityStub 调用)。
  • 这时,Workflow Worker 并不是立即执行 Activity 代码,而是把调用请求封装成一个任务(ActivityTask)。
  1. Workflow Worker 将 Activity 调度请求发送给 Temporal Server
  • Workflow Worker 通过 RPC 调用,将 Activity 执行请求(包括方法名、参数等)发送给 Temporal Server。
  • Temporal Server 把这个 Activity 任务写入事件历史和对应的 Task Queue。
  1. Activity Worker 监听 Task Queue 执行 Activity
  • 专门跑 Activity 代码的 Worker(Activity Worker)会监听这个 Task Queue。
  • 它接收到 Activity 任务后,调用对应的 Activity 实现代码,完成具体业务操作。
  1. Activity 执行结果返回给 Temporal Server
  • Activity 执行完成后,结果(成功或失败)通过 RPC 返回给 Temporal Server。
  • Temporal Server 更新事件历史,记录 Activity 执行状态和结果。
  1. Temporal Server 通知 Workflow Worker 继续执行
  • Temporal Server 通知 Workflow Worker Activity 执行完成。
  • Workflow Worker 继续重放 Workflow 代码,拿到 Activity 结果后,继续往下走。

使用举例

🎯 场景:用户下单支付流程


用户下单后:

  1. 创建订单
  2. 扣减库存
  3. 发起支付
  4. 等待支付结果(可能超时)
  5. 成功 → 发货;失败 → 取消订单、回滚库存

我们使用 Temporal 编排整个流程,并保证每一步具备可靠性、幂等性、可恢复性。

Temporal 优势

特性 应用
重试机制 网络异常、服务失败自动重试
超时控制 支付等待 30 分钟
Signal 用户主动取消
子工作流 支付流程可以作为子工作流
幂等 防止重复扣库存、重复发货

1. Workflow 接口定义

java 复制代码
@WorkflowInterface
public interface OrderWorkflow {
    @WorkflowMethod
    void placeOrder(String orderId);
}

2. Workflow 实现类

java 复制代码
public class OrderWorkflowImpl implements OrderWorkflow {

    private final OrderActivities activities = Workflow.newActivityStub(
        OrderActivities.class,
        ActivityOptions.newBuilder()
            .setStartToCloseTimeout(Duration.ofMinutes(5))
            .build()
    );

    @Override
    public void placeOrder(String orderId) {
        // 创建订单
        activities.createOrder(orderId);

        // 扣库存
        activities.reserveInventory(orderId);

        // 调用子工作流进行支付
        PaymentWorkflow paymentWorkflow = Workflow.newChildWorkflowStub(PaymentWorkflow.class);
        boolean paymentSuccess = paymentWorkflow.pay(orderId);

        if (paymentSuccess) {
            activities.shipOrder(orderId);
        } else {
            activities.cancelOrder(orderId);
            activities.releaseInventory(orderId);
        }
    }
}

3. Payment 子工作流接口

java 复制代码
@WorkflowInterface
public interface PaymentWorkflow {
    @WorkflowMethod
    boolean pay(String orderId);
}

4. 子工作流实现

java 复制代码
public class PaymentWorkflowImpl implements PaymentWorkflow {

    private final PaymentActivities activities = Workflow.newActivityStub(
        PaymentActivities.class,
        ActivityOptions.newBuilder()
            .setStartToCloseTimeout(Duration.ofMinutes(5))
            .build()
    );

    @Override
    public boolean pay(String orderId) {
        String paymentId = activities.initiatePayment(orderId);

        // 等待支付结果(30分钟)
        return Workflow.await(Duration.ofMinutes(30), () ->
            "SUCCESS".equals(activities.checkPaymentStatus(paymentId))
        );
    }
}

5. Activity 接口示例

java 复制代码
@ActivityInterface
public interface OrderActivities {
    void createOrder(String orderId);
    void reserveInventory(String orderId);
    void cancelOrder(String orderId);
    void releaseInventory(String orderId);
    void shipOrder(String orderId);
}

@ActivityInterface
public interface PaymentActivities {
    String initiatePayment(String orderId);
    String checkPaymentStatus(String paymentId);
}

🎯 场景:每天凌晨 3 点同步用户数据


1. 定义 Workflow 接口

java 复制代码
@WorkflowInterface
public interface DailySyncWorkflow {

    @WorkflowMethod
    void start(); // 启动工作流
}

2. Workflow 实现类

java 复制代码
public class DailySyncWorkflowImpl implements DailySyncWorkflow {

    private final SyncActivities activities = 
        Workflow.newActivityStub(SyncActivities.class,
            ActivityOptions.newBuilder()
                .setStartToCloseTimeout(Duration.ofMinutes(10))
                .setRetryOptions(RetryOptions.newBuilder()
                    .setMaximumAttempts(3)
                    .build())
                .build());

    @Override
    public void start() {
        while (true) {
            try {
                activities.syncUserData();
            } catch (Exception e) {
                Workflow.getLogger(DailySyncWorkflowImpl.class)
                        .error("同步失败: " + e.getMessage());
            }

            // 当前时间
            Instant now = Instant.ofEpochMilli(Workflow.currentTimeMillis());
            ZonedDateTime next3am = now.atZone(ZoneId.of("Asia/Shanghai"))
                .plusDays(1)
                .withHour(3).withMinute(0).withSecond(0).withNano(0);
            Duration sleepDuration = Duration.between(now, next3am.toInstant());

            Workflow.sleep(sleepDuration);
        }
    }
}

3. 定义 Activity 接口

java 复制代码
@ActivityInterface
public interface SyncActivities {

    @ActivityMethod
    void syncUserData();
}

4. 实现 Activity

java 复制代码
public class SyncActivitiesImpl implements SyncActivities {

    @Override
    public void syncUserData() {
        System.out.println("同步用户数据开始...");
        // 模拟失败
        if (new Random().nextInt(10) < 2) {
            throw new RuntimeException("同步失败,网络异常");
        }
        System.out.println("同步成功 ✅");
    }
}

5. 启动 Worker(注册工作流和 Activity)

java 复制代码
public class WorkerStarter {
    public static void main(String[] args) {
        WorkflowService service = WorkflowServiceStubs.newInstance();
        WorkflowClient client = WorkflowClient.newInstance(service);
        WorkerFactory factory = WorkerFactory.newInstance(client);
        Worker worker = factory.newWorker("DAILY_SYNC_TASK_QUEUE");

        worker.registerWorkflowImplementationTypes(DailySyncWorkflowImpl.class);
        worker.registerActivitiesImplementations(new SyncActivitiesImpl());

        factory.start();
    }
}

6. 启动工作流(一次性调用)

java 复制代码
public class WorkflowStarter {
    public static void main(String[] args) {
        WorkflowService service = WorkflowServiceStubs.newInstance();
        WorkflowClient client = WorkflowClient.newInstance(service);

        DailySyncWorkflow workflow = client.newWorkflowStub(
            DailySyncWorkflow.class,
            WorkflowOptions.newBuilder()
                .setTaskQueue("DAILY_SYNC_TASK_QUEUE")
                .setWorkflowId("daily-sync-workflow") // 保证幂等启动
                .build());

        WorkflowClient.start(workflow::start);
    }
}

参考

相关推荐
IMPYLH3 小时前
Python 的内置函数 reversed
笔记·python
程序员岳焱4 小时前
Java 与 MySQL 性能优化:Java 实现百万数据分批次插入的最佳实践
后端·mysql·性能优化
说私域4 小时前
基于开源AI智能名片链动2+1模式S2B2C商城小程序的超级文化符号构建路径研究
人工智能·小程序·开源
麦兜*4 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
大只鹅5 小时前
解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题
spring boot·后端·elasticsearch
ai小鬼头5 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
IT_10245 小时前
Spring Boot项目开发实战销售管理系统——数据库设计!
java·开发语言·数据库·spring boot·后端·oracle
猫头虎5 小时前
猫头虎 AI工具分享:一个网页抓取、结构化数据提取、网页爬取、浏览器自动化操作工具:Hyperbrowser MCP
运维·人工智能·gpt·开源·自动化·文心一言·ai编程
bobz9656 小时前
动态规划
后端
特立独行的猫a6 小时前
百度AI文心大模型4.5系列开源模型评测,从安装部署到应用体验
人工智能·百度·开源·文心一言·文心一言4.5