任务中心表示存储中应用程序的当前状态,包括所有挂起的工作。 当应用程序运行时,任务中心会持续存储业务流程、活动和实体函数的进度。 此方法可确保应用程序在暂时停止或中断后重新启动时可以恢复处理中断的位置。 任务中心还使应用程序能够动态缩放计算工作器。
从概念上讲,任务中心存储以下信息:
- 所有业务流程和实体实例的 实例状态 。
- 要处理的消息,包括:
- 任何表示活动等待运行的活动消息。
- 正在等待传递到实例的任何 实例消息 。
活动消息是无状态的,可在任意位置进行处理。 实例消息需要传递到由实例 ID 标识的特定有状态实例(业务流程或实体)。
在内部,每个存储提供程序可以使用不同的组织来表示实例状态和消息。 例如,Azure Storage提供程序将消息存储在Azure Storage队列中,但 MSSQL 提供程序将它们存储在关系表中。 这些差异对应用程序设计无关紧要,但其中一些差异可能会影响性能特征。 有关详细信息,请参阅 存储中的表示形式。
持久任务 SDK 使用 持久任务计划程序 作为任务中心的后端。 Durable Task Scheduler 是一项完全托管的服务,在内部处理存储。
工作项
任务中心的活动消息和实例消息表示应用程序需要处理的工作。 应用程序运行时,它会从任务中心持续提取 工作项 。 每个工作项正在处理一个或多个消息。 我们区分两种类型的工作项:
- 活动工作项:运行活动函数来处理活动消息。
- Orchestrator 工作项:运行业务流程协调程序或实体函数来处理一个或多个实例消息。
工作者可以同时处理多个工作项,但要受到配置的每个工作者的并发限制。
有关并发限制的详细信息,请参阅 性能和缩放。
工作者完成工作项后,会将其效果提交回任务枢纽。 这些效果因执行的函数类型而异:
- 已完成的活动函数创建一个包含结果的实例消息,该消息被发送到父协调器实例。
- 已完成的业务流程协调程序函数更新业务流程状态和历史记录,并可能创建新消息。
- 已完成的实体函数更新实体状态,还可以创建新的实例消息。
对于编排,每个工作项代表该编排执行过程中的一个阶段。 当有新消息需要协调程序处理时,一个新的过程就会开始。 此类消息可能指示编排应启动,或者它可能指示活动、实体调用、计时器或子编排已完成,或它可以表示外部事件。 该消息会触发一个工作项,该工作项允许业务流程协调程序处理结果并继续下一集。 当协调器完成或到达必须等待新消息的阶段时,该阶段将结束。
执行示例
请考虑一个扇出扇入业务流程,该业务流程并行启动两个活动,并等待两个活动完成:
[FunctionName("Example")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
Task t1 = context.CallActivityAsync<int>("MyActivity", 1);
Task t2 = context.CallActivityAsync<int>("MyActivity", 2);
await Task.WhenAll(t1, t2);
}
using Microsoft.DurableTask;
public class Example : TaskOrchestrator<object?, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
{
Task t1 = context.CallActivityAsync("MyActivity", 1);
Task t2 = context.CallActivityAsync("MyActivity", 2);
await Task.WhenAll(t1, t2);
return null;
}
}
客户端启动此业务流程后,应用程序会将其作为一系列工作项进行处理。 每个已完成的工作项在提交时更新任务中心状态。 下面是步骤:
- 客户请求启动实例 ID 为“123”的新工作流。 客户端完成此请求后,任务中心包含编排状态和实例消息的占位符:
标签 ExecutionStarted 是众多 历史事件类型之一,用于标识参与编排历史记录的各种消息和事件类型。
- 工作者执行 流程协调器工作项 来处理
ExecutionStarted消息。 它调用编排函数,开始执行编排代码。 此代码计划两个活动,然后在等待结果时停止执行。 工作者提交此工作项后,任务中心包含
运行时状态现在Running已添加两条新TaskScheduled消息,历史记录现在包含五个事件OrchestratorStarted,即ExecutionStarted,TaskScheduled和TaskScheduled。 OrchestratorCompleted 这些事件代表了该编排执行过程的第一个阶段。
- 工作人员执行 活动工作项 来处理一条
TaskScheduled消息。 它调用输入为“2”的活动函数。 活动函数完成后,会创建包含TaskCompleted结果的消息。 工作者提交此工作项后,任务中心包含
- 工作者执行 流程协调器工作项 来处理
TaskCompleted消息。 如果该编排仍缓存在内存中,则可以直接恢复执行。 否则,工作者首先 回放历史记录以恢复业务流程的当前状态。 然后,它会继续编排过程,交付活动的结果。 收到此结果后,编排仍在等待其他活动的结果,因此它再次停止执行。 工作者提交此工作项后,任务中心包含
业务流程历史记录现在包含另外三个事件:OrchestratorStarted、TaskCompleted 和 OrchestratorCompleted。 这些事件表示此编排执行的第二阶段。
- 一名工作者执行 活动工作项 来处理剩余消息。 它调用输入为“1”的活动函数。 工作者提交此工作项后,任务中心包含
- 工作者执行另一个 协调器工作项 来处理
TaskCompleted消息。 收到第二个结果后,编排将结束。 工作者提交此工作项后,任务中心包含
运行时状态现在是Completed,而编排历史记录现在又包含了四个事件OrchestratorStarted、TaskCompleted、ExecutionCompleted、OrchestratorCompleted。 这些事件表示此协调执行的第三阶段和最后阶段。
然后,此编排执行的最终历史记录包含 12 个事件 OrchestratorStarted、ExecutionStarted、TaskScheduled、TaskScheduled、OrchestratorCompleted、OrchestratorStarted、TaskCompleted、OrchestratorCompleted、OrchestratorStarted、TaskCompleted、ExecutionCompleted、OrchestratorCompleted。
注释
显示的计划并不是唯一一个计划:有许多可能的计划略有不同。 例如,如果第二个活动提前完成,则两 TaskCompleted 个实例消息都可以由单个工作项处理。 在这种情况下,执行历史记录稍短一些,因为只有两个阶段,并且它包含以下 10 个事件:OrchestratorStarted、ExecutionStarted、TaskScheduled、TaskScheduled、OrchestratorCompleted、OrchestratorStarted、TaskCompleted、TaskCompleted、ExecutionCompleted、OrchestratorCompleted。
持久任务调度器任务中心管理
本部分介绍如何在使用 Durable Task Scheduler 后端时创建和管理任务中心。 在应用程序使用计划程序和任务中心资源之前显式创建资源。
创建计划程序和任务中心
使用 Azure 门户、Azure CLI、Azure Resource Manager(ARM)或Bicep创建计划程序和任务中心。
在 Azure 门户中,搜索 Durable Task Scheduler 并从结果中选择它。
选择“ 创建 ”以打开计划程序创建窗格。
填写“ 基本信息 ”选项卡上的字段,包括资源组、计划程序名称、区域和 SKU。 选择“查看 + 创建”。
验证通过后,选择“ 创建”。 部署最多需要 15 分钟。
创建计划程序后,进入计划程序资源。 在 “概述 ”页上,创建新的任务中心。
重要
IP 0.0.0.0/0 允许列表允许从任何 IP 地址进行访问。 对于生产部署,请仅将此限制为所需的 IP 范围。
前面的示例使用专用 SKU。 持久任务计划程序还提供消耗 SKU。 有关管理持久任务计划程序资源的详细信息,请参阅 使用 Durable Task Scheduler 进行开发。
配置基于标识的身份验证
Durable Task Scheduler 仅支持托管标识身份验证。 它不支持包含存储密钥的连接字符串。 将适当的基于角色的访问控制(RBAC)角色分配给托管标识,并将应用配置为使用该标识。
以下角色可用:
| 角色 | 说明 |
|---|---|
| 持久任务数据参与者 | 完全数据访问。 所有其他角色的超集。 |
| 持久任务辅助角色 | 与处理编排、活动和实体的调度程序交互。 |
| 持久任务数据读取器 | 对业务流程和实体数据的只读访问权限。 |
注释
大多数应用都需要 持久任务数据参与者 角色。
尽可能使用用户分配的托管标识,因为它们未绑定到应用的生命周期,可以在删除应用后重复使用它们。
转到 Azure 门户中的调度程序或任务中心资源。
从左侧菜单中选择“访问控制 (IAM)”。
选择添加>添加角色分配。
搜索并选择“持久任务数据参与者”。 选择“下一步”。
在“将访问权限分配到”下,选择“托管标识”。 选择“+ 选择成员”。
选择 “用户分配的托管标识”,选择该标识,然后选择“ 选择”。
选择 “审阅 + 分配 ”以完成。
转到函数应用程序,然后选择 设置>身份验证。 选择 “用户分配 ”选项卡,然后添加标识。
分配标识后,将以下环境变量添加到应用:
| Variable | 价值 |
|---|---|
TASKHUB_NAME |
任务中心的名称。 |
DURABLE_TASK_SCHEDULER_CONNECTION_STRING |
Endpoint={scheduler endpoint};Authentication=ManagedIdentity;ClientID={client id} |
注释
如果使用系统分配的托管标识,请省略connection string中的 ClientID 段:Endpoint={scheduler endpoint};Authentication=ManagedIdentity。
有关完整的标识配置详细信息,请参阅 为 Durable Task Scheduler 配置托管标识。
多个应用程序
如果多个应用程序共享相同的计划程序,请使用单独的任务中心配置每个应用程序。 单个计划程序可以包含多个任务中心。 每个应用程序都应连接到其自己的任务中心,以避免冲突。 否则,应用程序争用消息,这可能会导致未定义的行为,包括业务流程意外停滞。
BYO 存储提供商任务中心管理
本部分涵盖任务中心的创建和删除、在运行多个函数应用时正确使用任务中心,以及检查任务中心内容。 它适用于自带 (BYO) 存储提供程序:Azure Storage、Netherite 和 MSSQL。
创建和删除
当函数应用首次启动时,会自动在存储中创建包含所有所需资源的空任务中心。
如果使用Azure Storage提供程序,则无需进行额外的配置。 否则,请按照 配置存储提供程序的说明,确保存储提供程序 能够正确设置和访问任务中心所需的存储资源。
注释
停止或删除函数应用时,不会自动删除任务中心。 若要删除该数据,请手动删除任务中心、其内容或包含的存储帐户。
小窍门
在开发场景中,您可能需要经常从干净状态重启。 若要快速执行此作,只需 更改配置的任务中心名称。 此更改强制在重启应用程序时创建一个新的空任务中心。 在这种情况下,不会删除旧数据。
多个函数应用程序
如果多个函数应用共享存储帐户,请使用单独的 任务中心名称配置每个函数应用。 此要求也适用于过渡槽:使用唯一的任务中心名称配置每个过渡槽。 单个存储帐户可以包含多个任务中心。 此限制通常也适用于其他存储提供程序。
重要
默认情况下,应用名称用作任务中心名称,这可确保不会意外共享任务中心。 如果在 host.json中显式配置应用的任务中心名称,请确保名称是唯一的。 否则,多个应用会争用消息,这可能导致未定义的行为,比如使编排意外地停滞在Pending或Running状态。 唯一的例外是在多个区域中部署同一应用的副本。 在这种情况下,对副本使用相同的任务中心。
下图演示了在共享和专用的 Azure 存储帐户中,每个 Function 应用配有一个任务中心。
注释
任务中心共享规则的例外情况是,如果要为区域灾难恢复配置应用。 有关详细信息,请参阅 灾难恢复和异地分发 文章。
内容检查
可通过几种常见方法来检查任务中心的内容:
- 在函数应用中,客户端对象提供查询实例存储的方法。 若要详细了解支持的查询类型,请参阅 实例管理 文章。
- 同样, HTTP API 提供 REST 请求来查询业务流程和实体的状态。 有关更多详细信息,请参阅 HTTP API 参考 。
- Durable Functions Monitor 工具可以检查任务中心并提供各种视觉显示选项。
对于某些存储提供程序,还可以直接访问基础存储以检查任务中心:
- 如果使用 Azure Storage 提供程序,则实例状态存储在 Instance Table 和 History Table 中,可以使用 Azure Storage Explorer 之类的工具对其进行检查。
- 如果使用 MSSQL 存储提供程序,请使用 SQL 查询和工具检查数据库中的任务中心内容。
存储中的表示形式
每个存储提供程序使用不同的内部组织来表示存储中的任务中心。 了解此组织(虽然不需要)在进行故障排除或尝试满足性能、可伸缩性或成本目标时会有所帮助。
持久任务 SDK 使用 持久任务计划程序 作为后端,后者在内部管理任务中心状态。
Durable 任务调度程序提供者
Durable Task Scheduler 是一个完全托管的后端提供程序,在内部存储所有任务中心状态。 与自带存储提供程序(BYO)不同,无需设置或管理任何底层存储基础结构。 每个计划程序资源(Microsoft.DurableTask/schedulers)都有专用的计算和内存资源,可以包含一个或多个任务中心(Microsoft.DurableTask/schedulers/taskHubs)。
由于 Durable Task Scheduler 在内部管理存储,因此无法直接检查基础数据。 请改用 Durable Task Scheduler 仪表板 来监视和查询编排实例。
有关 BYO 存储提供程序选项及其比较方式的详细信息,请参阅 Durable Functions 存储提供程序。
Azure存储提供商
Azure 存储提供程序使用以下组件表示存储中的任务中心:
- 两个Azure表存储实例状态。
- 一个 Azure 队列存储活动消息。
- 一个或多个 Azure 队列存储实例消息。 每个所谓的控制队列都表示一个分区,根据实例 ID 的哈希分配所有实例消息的子集。
- 一些用于租用 Blob 或大型消息的额外 Blob 容器。
例如,名为 xyz 的任务中心 (PartitionCount = 4) 包含以下队列和表:
以下部分更详细地介绍了这些组件及其角色。
有关任务中心如何由Azure Storage提供程序表示的详细信息,请参阅 Azure Storage 提供程序文档。
Netherite 存储提供程序 (停用路径)
Netherite 将所有任务中心状态分区为指定数量的分区。 在存储中,这些资源存储数据:
- 一个Azure Storage blob 容器,其中包含按分区分组的所有 blob。
- 一个包含有关分区已发布指标的 Azure 表。
- 用于在分区之间传递消息的Azure Event Hubs命名空间。
例如,在存储中表示一个名为“mytaskhubPartitionCount = 32”的任务中心,如下所示:
注释
所有任务中心状态都存储在 Blob 容器中 x-storage 。 表 DurableTaskPartitions 和事件中心命名空间包含冗余数据:如果内容丢失,则可以自动恢复它们。 因此,无需配置Azure Event Hubs命名空间来保留超过默认过期时间的消息。
Netherite 使用基于日志和检查点的事件溯源机制来表示分区的当前状态。 块 Blob 和页 Blob 都存储数据。 无法直接从存储中读取此格式,因此查询实例存储时,函数应用必须运行。
有关 Netherite 存储提供程序的任务中心的详细信息,请参阅 Netherite 存储提供程序的任务中心信息。
MSSQL 存储提供程序
所有任务中心数据存储在单个关系数据库中,使用以下表:
- 和
dt.Instancesdt.History表存储实例状态。 - 该
dt.NewEvents表存储实例消息。 - 该
dt.NewTasks表存储活动消息。
若要使多个任务中心能够独立地在同一数据库中共存,每个表在其主键中包含一个 TaskHub 列。 与其他两个提供程序不同,MSSQL 提供程序没有分区。
有关 MSSQL 存储提供程序的任务中心的详细信息,请参阅 Microsoft SQL (MSSQL) 存储提供程序的任务中心信息。
任务中心名称
任务中心由符合以下规则的名称标识:
- 仅包含字母数字字符
- 以字母开头
- 最小长度为 3 个字符,最大长度为 45 个字符
在 host.json 文件中声明任务中心名称,如以下示例所示:
host.json (Functions 2.0)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
}
}
host.json (Functions 1.x)
{
"durableTask": {
"hubName": "MyTaskHub"
}
}
还可以使用应用设置设置任务中心,如以下示例 host.json 文件中所示:
host.json (Functions 1.x)
{
"durableTask": {
"hubName": "%MyTaskHub%"
}
}
host.json (Functions 2.0)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%MyTaskHub%"
}
}
}
任务中心名称设置为 MyTaskHub 应用设置的值。 以下 local.settings.json 文件显示如何将 MyTaskHub 设置定义为 samplehubname:
{
"IsEncrypted": false,
"Values": {
"MyTaskHub" : "samplehubname"
}
}
注释
使用部署槽位时,最佳做法是使用应用设置设置任务中心名称。 如果要确保特定槽始终使用特定任务中心,请使用“槽粘滞”应用设置。
除了 host.json,还可以在业务流程客户端绑定元数据中设置任务中心名称。 当需要访问位于单独函数应用中的业务流程或实体时,此设置非常有用。 以下代码演示如何编写一个函数,该函数使用 协调客户端绑定 来处理在应用设置中配置的任务中心:
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[DurableClient(TaskHub = "%MyTaskHub%")] IDurableOrchestrationClient starter,
string functionName,
ILogger log)
{
// Function input comes from the request content.
object eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync(functionName, eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
注释
前面的示例适用于 Durable Functions 2.x。 对于 Durable Functions 1.x,请使用 DurableOrchestrationContext 而不是 IDurableOrchestrationContext。 有关版本之间差异的详细信息,请参阅 Durable Functions 版本一文。
注释
仅在使用一个函数应用访问另一个函数应用中的业务流程和实体时,才需要在客户端绑定元数据中设置任务中心名称。 如果在业务流程和实体相同的函数应用中定义客户端函数,请避免在绑定元数据中指定任务中心名称。 默认情况下,所有客户端绑定从 host.json 设置中获取其任务中心元数据。
任务中心名称以字母开头,仅包含字母和数字。 如果未指定,则使用默认任务中心名称,如下表所示:
| 持久扩展版本 | 默认任务中心名称 |
|---|---|
| 2.x | 在 Azure 中部署时,任务中心名称派生自 功能应用的名称。 在Azure外部运行时,默认任务中心名称为 TestHubName。 |
| 1.x | 所有环境的默认任务中心名称为 DurableFunctionsHub。 |
有关扩展版本之间的差异的详细信息,请参阅 Durable Functions 版本一文。
注释
当共享存储帐户中有多个任务中心时,该名称区分一个任务中心与另一个任务中心。 如果有多个函数应用共享共享存储帐户,请为 host.json 文件中的每个任务中心显式设置不同的名称。 否则多个函数应用会相互竞争消息,这可能会导致未定义的行为,例如业务流程意外“卡”在 Pending 或 Running 状态。