0Pricing
Cloud & IT Cert Prep · 课时

使用 Durable Functions 编排有状态工作流

使用 Durable Functions 编排器模式(扇出/扇入、链接、监视)编排长时间运行的工作流,并了解状态如何创建检查点。

使用 Durable Functions 编排有状态工作流 是 CoddyKit 上的免费 Cloud & IT Cert Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Cloud & IT Cert Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Cloud & IT Cert Prep 课程共包含 4 节课。

为什么使用 Durable Functions

常规 Azure Functions 是无状态的——每次调用都独立运行,不会记住之前的调用。Durable Functions 扩展了 Azure Functions,使您能够使用普通的异步代码(async/await)编写有状态的长时间运行工作流。Durable Task Framework 会在每个步骤后自动将状态检查点保存到 Azure Storage,因此即使服务器重启、超时或进行计划内维护,工作流也能继续运行,并从中断的位置准确恢复。

三种函数类型

Durable Functions 引入了三种函数类型。协调器函数负责协调整个工作流——它调用活动函数并使用 yield 或 await 等待结果,但不会实际阻塞线程。活动函数执行一个独立的工作单元(例如调用 API 或写入数据库),副作用操作只能发生在这里。实体函数会在多次调用之间维护少量持久状态(例如计数器和标志)。

// Client function (HTTP trigger) — starts the orchestration
module.exports = async function (context, req) {
  const client = df.getClient(context);
  const orderId = req.body.orderId;
  const instanceId = await client.startNew('OrderOrchestrator', undefined, { orderId });
  return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};

链式模式

链式模式按顺序运行活动函数,将一个函数的输出传递给下一个函数作为输入。协调器依次等待每个活动完成。如果任一活动失败,工作流就会停止,并且可以从失败的步骤重新启动。这是最简单的 Durable Functions 模式,适用于每个步骤都依赖上一步结果的工作流,例如订单处理流水线。

// Orchestrator: chaining pattern
const df = require('durable-functions');
module.exports = df.orchestrator(function* (context) {
  const orderId = context.df.getInput().orderId;

  const validated = yield context.df.callActivity('ValidateOrder', orderId);
  const charged   = yield context.df.callActivity('ChargePayment', validated);
  const shipped   = yield context.df.callActivity('ShipOrder', charged);

  return { status: 'shipped', trackingId: shipped.trackingId };
});

扇出/扇入模式

扇出/扇入模式会并行启动多个活动函数,并等待它们全部完成后再继续。协调器使用 callActivity 同时启动所有任务而不等待它们完成,将任务对象收集到数组中,然后通过 Task.all() 等待所有任务。这对于处理多个文件、调用多个 API 或执行批量操作等相互独立的工作项而言,比顺序处理快得多。

// Orchestrator: fan-out / fan-in
module.exports = df.orchestrator(function* (context) {
  const items = context.df.getInput().items;

  // Fan-out: start all tasks in parallel
  const tasks = items.map(item => context.df.callActivity('ProcessItem', item));

  // Fan-in: wait for all tasks to complete
  const results = yield context.df.Task.all(tasks);

  return results;
});

监视器模式

监视器模式会按固定间隔轮询外部系统,直到满足某个条件——类似于轮询循环,但完全具备持久性。协调器调用活动函数检查状态,使用 createTimer 等待可配置的时间间隔,然后继续循环。由于每次轮询之间的状态都会保存到存储中,协调器在等待期间不会占用计算资源,因此相比基于休眠计时器的方法高效得多。

// Orchestrator: monitor pattern (poll until job completes)
module.exports = df.orchestrator(function* (context) {
  const jobId = context.df.getInput().jobId;
  const expiry = new Date(context.df.currentUtcDateTime);
  expiry.setHours(expiry.getHours() + 24); // 24-hour timeout

  while (context.df.currentUtcDateTime < expiry) {
    const status = yield context.df.callActivity('GetJobStatus', jobId);
    if (status === 'completed') return { jobId, status };
    if (status === 'failed') throw new Error('Job failed');
    // Wait 30 seconds before next poll
    const nextCheck = new Date(context.df.currentUtcDateTime);
    nextCheck.setSeconds(nextCheck.getSeconds() + 30);
    yield context.df.createTimer(nextCheck);
  }
  throw new Error('Workflow timed out');
});

人工交互模式

人工交互模式会暂停协调过程,等待外部事件,例如经理审批。协调器通过 waitForExternalEvent 等待事件,即使等待数天或数周也不会占用计算资源。外部系统(审批电子邮件链接、移动应用或 webhook)调用 Durable Functions HTTP API 来引发事件,从而解除协调过程的阻塞。如果没有收到响应,还可以结合计时器实现自动超时和升级处理。

// Orchestrator: wait for human approval with timeout
module.exports = df.orchestrator(function* (context) {
  const request = context.df.getInput();

  yield context.df.callActivity('SendApprovalEmail', request);

  const timeout = df.Task.createTimer(context, new Date(Date.now() + 48 * 3600 * 1000));
  const approval = context.df.waitForExternalEvent('ApprovalResponse');

  const winner = yield context.df.Task.any([approval, timeout]);

  if (winner === approval) {
    const approved = winner.result;
    return approved ? 'Approved' : 'Rejected';
  } else {
    return 'Timed out — escalated';
  }
});

协调器约束

协调器函数具有重要约束,因为它们可能会根据历史记录多次重放,以重新构建状态。它们必须是确定性的——绝不能使用 Date.now()、Math.random() 或直接执行 I/O 调用。请改用 context.df.currentUtcDateTime 获取时间戳,并通过活动函数执行所有 I/O 操作。在协调器函数体中进行日志记录会在重放期间产生重复的日志条目;请改用活动函数进行日志记录。

// WRONG — non-deterministic, will cause replay bugs
module.exports = df.orchestrator(function* (context) {
  const now = new Date();          // Don't use Date()
  const rand = Math.random();       // Don't use Math.random()
  const data = await fetch('/api'); // Don't make HTTP calls directly
});

// CORRECT
module.exports = df.orchestrator(function* (context) {
  const now = context.df.currentUtcDateTime; // OK
  const data = yield context.df.callActivity('FetchData', null); // OK
});

管理实例:状态和终止

每次协调运行都有唯一的实例 ID,您可以使用它查询状态、发送事件或终止运行。Durable Functions HTTP 管理 API 提供了用于检查状态(GET /instances/{id})、发送事件(POST /instances/{id}/raiseEvent/{name})和终止运行(POST /instances/{id}/terminate)的端点。在函数中使用 Durable 客户端绑定,即可通过编程方式访问这些操作。

// Client function: check orchestration status
module.exports = async function (context, req) {
  const client = df.getClient(context);
  const instanceId = req.params.instanceId;

  const status = await client.getStatus(instanceId, true, true, true);
  return {
    status: 200,
    body: {
      instanceId,
      runtimeStatus: status.runtimeStatus,
      customStatus: status.customStatus,
      output: status.output
    }
  };
};

存储后端和性能

Durable Functions 会将协调历史记录、实例状态以及函数间消息队列存储在Azure Storage 帐户中(如果需要更高吞吐量,也可以使用 Azure SQL 或 Netherite 后端)。每个检查点都会写入 Azure Table Storage 和 Azure Queue Storage。对于高吞吐量场景(数千个并发协调过程),Netherite 存储后端使用 Azure Event Hubs,可显著提升性能。请监视协调队列深度,以便发现瓶颈。

// host.json: configure the Durable Task storage provider
{
  'version': '2.0',
  'extensions': {
    'durableTask': {
      'hubName': 'MyTaskHub',
      'storageProvider': {
        'type': 'azure',
        'connectionStringName': 'AzureWebJobsStorage',
        'controlQueueBatchSize': 32,
        'maxQueuePollingInterval': '00:00:02'
      }
    }
  }
}

错误处理和重试

活动函数可能会引发异常,这些异常会以 TaskFailedException 的形式传播回协调器。请在协调器中使用 try-catch 代码块,以优雅地处理失败。对于暂时性错误,请通过 callActivityWithRetry 配置带退避的自动重试,并指定最大尝试次数、首次重试间隔和退避系数。对于调用外部 API 或数据库的活动,这是推荐的模式。

// Orchestrator: retry an activity with exponential backoff
module.exports = df.orchestrator(function* (context) {
  const retryOptions = new df.RetryOptions(
    5000,  // firstRetryIntervalInMilliseconds
    3      // maxNumberOfAttempts
  );
  retryOptions.backoffCoefficient = 2; // 5s, 10s, 20s

  try {
    const result = yield context.df.callActivityWithRetry(
      'CallExternalAPI',
      retryOptions,
      context.df.getInput()
    );
    return result;
  } catch (e) {
    yield context.df.callActivity('SendFailureAlert', e.message);
    throw e;
  }
});

Durable 实体

Durable Entities(实体函数)实现了可通过标识访问的少量持久状态,类似于虚拟参与者。实体具有 ID 和一个会在多次调用之间持久保存的状态。您可以从协调器或客户端调用实体上的操作,而实体会一次处理一个操作(串行处理)。常见用途包括计数器、审批状态机、速率限制器和购物车——任何需要持久且可更新状态、但不需要数据库的场景。

// Counter entity function
const df = require('durable-functions');
module.exports = df.entity(function (context) {
  let count = context.df.getState(() => 0);
  const operation = context.df.operationName;

  if (operation === 'add') count += context.df.getInput();
  if (operation === 'reset') count = 0;
  if (operation === 'get') context.df.return(count);

  context.df.setState(count);
});

// From orchestrator, increment counter entity
// const entityId = new df.EntityId('Counter', 'myCounter');
// yield context.df.callEntity(entityId, 'add', 1);

快速检查

测试您对本课中 Microsoft Azure 基础知识(AZ-900)概念的理解。

课程回顾

本课中您学习了:Durable Functions 通过将协调器状态保存到 Azure Storage 的检查点来支持有状态的长时间运行工作流;关键模式包括链式、扇出/扇入、监视器和人工交互;并且协调器必须是确定性的——所有 I/O 和非确定性调用都必须通过活动函数执行。接下来我们将探索 Azure Logic Apps。

常见问题解答

「使用 Durable Functions 编排有状态工作流」课时是免费的吗?

是的 — 「使用 Durable Functions 编排有状态工作流」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Cloud & IT Cert Prep 课程的其余内容,请升级到 CoddyKit PRO。 Cloud & IT Cert Prep 课程共包含 4 节课。

「使用 Durable Functions 编排有状态工作流」这节课中我会学到什么?

使用 Durable Functions 编排器模式(扇出/扇入、链接、监视)编排长时间运行的工作流,并了解状态如何创建检查点。 你通过在浏览器中直接运行的动手代码来练习 Cloud & IT Cert Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Cloud & IT Cert Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Cloud & IT Cert Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「使用 Durable Functions 编排有状态工作流」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Cloud & IT Cert Prep 课中编写并运行代码吗?

能。每节 Cloud & IT Cert Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Azure Functions 触发器和绑定
  2. 使用 Durable Functions 编排有状态工作流
  3. Azure Logic Apps
  4. 事件网格和事件驱动架构
← 返回 Cloud & IT Cert Prep