使用 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 反馈 — 无需本地设置。
此课程中的所有课时
- Azure Functions 触发器和绑定
- 使用 Durable Functions 编排有状态工作流
- Azure Logic Apps
- 事件网格和事件驱动架构