Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
在使用持久化编排系统时,将更改部署到编排程序逻辑是一个关键考虑因素。 如果业务流程中断,以后恢复(例如,在主机更新期间),运行时将重播业务流程的事件,确保在执行下一步之前所有前面的步骤都成功执行。 如果业务流程代码在部署之间发生更改,则执行的步骤可能不再相同。 在这种情况下,系统会引发非确定性错误,而不是允许业务流程继续。
业务流程版本控制 可防止与不确定相关的问题,使你能够与新的(或旧)业务流程无缝协作,同时维护持久业务流程所需的确定性执行模型。
此内置功能通过最少的配置提供自动版本隔离。 它与后端无关,因此任何使用 Durable Functions storage 提供程序的应用(包括 Durable Task Scheduler)都可以使用它。
持久任务 SDK 支持两种版本控制样式,可以单独使用或结合使用:
- 基于客户端/上下文的条件版本控制 - 在业务流程协调程序中的客户端和分支逻辑上设置版本。
- 基于工作者的版本控制 - 让工作者决定它可以处理的编排版本。
术语
本文使用两个相关但不同的术语:
- Orchestrator 函数 (或只是“orchestrator”):定义工作流逻辑的函数代码,即工作流执行方式的模板或蓝图。
- 业务流程实例 (或只是“业务流程”):业务流程协调程序函数的特定运行执行,其自己的状态、实例 ID 和输入。 多个协调程序实例可以从同一协调程序函数并发运行。
了解此区别对于编排版本控制至关重要。 业务流程协调程序函数代码包含版本感知逻辑,而业务流程实例在创建时永久关联到特定版本。
工作原理
编排版本控制基于以下核心原则进行:
- 版本关联:创建业务流程实例时,它会永久获取与其关联的版本。
- 版本感知执行:业务流程协调程序代码会相应地检查与当前业务流程实例和分支执行关联的版本值。
- 向后兼容性:运行较新的协调程序版本的工作者继续运行旧版本创建的协调任务。
- 向前保护:运行时阻止运行旧业务流程协调程序版本的工作进程执行由较新版本启动的业务流程。
先决条件
在使用业务流程版本控制之前,请确保具有编程语言所需的包版本。
如果使用非.NET语言(JavaScript、Python、PowerShell 或 Java)和 extension 捆绑包,则函数应用必须引用 Extension Bundle 版本 4.30.0 或更高版本。 在 extensionBundle 中配置 host.json 范围,使最低版本至少为 4.30.0。 例如:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.30.0, 5.0.0)"
}
}
有关选择和更新捆绑包版本的详细信息,请参阅 扩展捆绑包配置文档。
除了非.NET语言的扩展捆绑包要求外,还需要使用下面列出的特定于语言的 SDK 包的最低版本。 业务流程版本控制需要扩展捆绑包和 SDK 包才能正常工作。
使用 Microsoft.Azure.Functions.Worker.Extensions.DurableTask 版本 1.14.0 或更高版本。
设置默认版本
若要使用业务流程版本控制,请先为新的业务流程实例配置默认版本。
在Azure Functions项目中的 defaultVersion 文件中添加或更新 host.json 设置:
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>"
}
}
}
版本字符串可以遵循适合版本控制策略的任何格式:
- 多部分版本控制:
"1.0.0"、"2.1.0" - 简单编号:
"1""2" - 基于日期:
"2025-01-01" - 自定义格式:
"v1.0-release"
设置 defaultVersion后,所有新的编排实例将永久关联到该版本。
配置应用程序时,在客户端生成器中设置默认版本。
注释
自 v1.9.0 起,.NET SDK(Microsoft.DurableTask.Client.AzureManaged)中可用。
builder.Services.AddDurableTaskClient(builder =>
{
builder.UseDurableTaskScheduler(connectionString);
builder.UseDefaultVersion("1.0.0");
});
版本是一个简单的字符串,接受任何值。 SDK 尝试将其转换为 .NET 的 System.Version。 如果成功,则使用该库进行比较。 否则,将使用简单的字符串比较。
在客户端上设置默认版本后,此客户端启动的任何业务流程将永久关联到该版本。 该版本在编排上下文中也可用,允许在条件语句中使用。
版本比较规则
当Strict或CurrentOrOlder策略被选择时(请参阅版本匹配),运行时使用以下规则将业务流程实例的版本与defaultVersion工作节点的值进行比较:
- 空或 null 版本被视为相等。
- 空版本或 null 版本被视为早于任何定义的版本。
- 如果两个版本都是数字(例如
"1.0",和"2.0"),则它们被比较为版本号,因此"2.0"比版本号更新"1.0"。 - 否则,将执行不区分大小写的字符串比较。
以下示例说明了版本比较的工作原理:
| 版本 A | 版本 B | 结果 |
|---|---|---|
"1.0" |
"2.0" |
A 较旧 |
null |
"1.0" |
A 较旧 |
null |
null |
Equal |
"v1-release" |
"v2-release" |
A 较旧(字母顺序) |
当选择Strict或CurrentOrOlder匹配策略时(请参阅版本匹配),版本比较依据所使用的语言。
-
.NET:SDK 尝试将版本分析为
System.Version。 如果两者都成功解析,则使用CompareTo进行比较。 否则,SDK 使用字符串比较。 -
Python:SDK 使用
packaging.version进行语义版本控制比较。 - Java:SDK 将版本与简单字符串进行比较。
版本感知协调程序逻辑
若要实现版本感知逻辑,请使用上下文参数访问当前业务流程实例的版本和分支执行。
重要
在实现版本感知逻辑时,至关重要的是为旧版本保留确切的编排逻辑。 对现有版本的活动调用顺序、次序或标识所做的任何更改都可能会中断确定性重放,并导致在途的编排流程失败或产生不正确的结果。 部署后,保留旧版本代码路径不变。
[Function("MyOrchestrator")]
public static async Task<string> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
if (context.Version == "1.0")
{
// Original logic for version 1.0
...
}
else if (context.Version == "2.0")
{
// New logic for version 2.0
...
}
...
}
[DurableTask]
class HelloCities : TaskOrchestrator<string, List<string>>
{
private readonly string[] Cities = ["Seattle", "Amsterdam", "Hyderabad"];
public override async Task<List<string>> RunAsync(
TaskOrchestrationContext context, string input)
{
List<string> results = [];
foreach (var city in Cities)
{
results.Add(await context.CallSayHelloAsync($"{city} v{context.Version}"));
if (context.CompareVersionTo("2.0.0") >= 0)
{
results.Add(await context.CallSayGoodbyeAsync($"{city} v{context.Version}"));
}
}
return results;
}
}
注释
context.Version 属性是只读的,反映创建时与业务流程实例永久关联的版本。 编排执行期间无法修改此值。
小窍门
如果在指定默认版本之前已创建正在进行的编排,context.Version 将为这些实例返回 null,或返回语言相关的等效项。 构建业务流程协调程序逻辑,以处理旧版(null 版本)和新版本的业务流程。
部署行为
预计在使用新版逻辑部署更新的编排器功能时会发生以下情况:
- 工作者共存:含有新协调器函数代码的工作者开始,而具有旧代码的一些工作者可能仍然处于活动状态。
-
新实例的版本分配:由新工作进程创建的所有新业务流程和子业务流程都从
defaultVersion中获取分配的版本。 - 新的辅助角色兼容性:新辅助角色可以同时处理新创建的业务流程和以前存在的业务流程,因为版本感知分支逻辑可确保向后兼容性。
-
旧辅助角色限制:旧辅助角色只能处理版本等于或低于自己
defaultVersion中指定的host.json版本的业务流程,因为它们不应具有与较新版本兼容的业务流程协调程序代码。
注释
编排版本控制不影响工作进程生命周期。 Azure Functions 平台根据托管模型的常规规则管理工作进程设置和停用。
示例:替换序列中的活动
此示例演示如何使用编排版本控制替换序列中间的活动。
版本 1.0
host.json 配置:
{
"extensions": {
"durableTask": {
"defaultVersion": "1.0"
}
}
}
业务流程协调程序函数:
[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var orderId = context.GetInput<string>();
await context.CallActivityAsync("ValidateOrder", orderId);
await context.CallActivityAsync("ProcessPayment", orderId);
await context.CallActivityAsync("ShipOrder", orderId);
return "Order processed successfully";
}
版本 2.0 的折扣处理功能
host.json 配置:
{
"extensions": {
"durableTask": {
"defaultVersion": "2.0"
}
}
}
业务流程协调程序函数:
[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var orderId = context.GetInput<string>();
await context.CallActivityAsync("ValidateOrder", orderId);
if (TaskOrchestrationVersioningUtils.CompareVersions(context.Version, "1.0") <= 0)
{
// Preserve original logic for existing instances
await context.CallActivityAsync("ProcessPayment", orderId);
}
else
{
// New logic with discount processing
await context.CallActivityAsync("ApplyDiscount", orderId);
await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
}
await context.CallActivityAsync("ShipOrder", orderId);
return "Order processed successfully";
}
版本匹配
版本匹配策略根据版本兼容性确定哪个工作器处理哪些编排实例。
下表描述了可用的策略:
| 策略 | 说明 |
|---|---|
| 没有 | 处理工作时,不会考虑版本。 无论版本如何,都会处理所有工作。 |
| 严格 | 编排版本和工作器版本必须完全匹配。 |
| CurrentOrOlder | 编排版本必须等于或小于工作者版本。 这是默认策略。 |
配置
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>",
"versionMatchStrategy": "CurrentOrOlder"
}
}
}
-
None(不建议):禁用版本检查。 任何工作进程可以处理任意编排实例。 -
Strict:仅处理与defaultVersion版本完全相同的业务流程中的任务。 需要仔细的部署协调以避免孤立的业务流程。 -
CurrentOrOlder(默认值):处理协调程序中版本小于或等于defaultVersion的任务。 启用向后兼容性,同时防止较旧的工作器处理较新的流程编排。
通过工作线程生成器配置匹配策略。
注释
自 v1.9.0 起,在 .NET SDK(Microsoft.DurableTask.Worker.AzureManaged)中可用。
builder.Services.AddDurableTaskWorker(builder =>
{
builder.AddTasks(r => r.AddAllGeneratedTasks());
builder.UseDurableTaskScheduler(connectionString);
builder.UseVersioning(new DurableTaskWorkerOptions.VersioningOptions
{
Version = "1.0.0",
DefaultVersion = "1.0.0",
MatchStrategy = DurableTaskWorkerOptions.VersionMatchStrategy.Strict,
FailureStrategy = DurableTaskWorkerOptions.VersionFailureStrategy.Reject,
});
});
版本不匹配处理
版本不匹配处理策略确定当协调流程实例版本与工作器版本不匹配时会发生什么情况。
下表描述了可用的策略:
| 策略 | 说明 |
|---|---|
| Reject | 编排被拒绝并返回到工作队列。 另一个工人以后可以尝试此任务。 此策略是默认值。 |
| 失败 | 编排失败,并从工作队列中删除。 |
配置
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>",
"versionFailureStrategy": "Reject"
}
}
}
-
Reject(默认值):业务流程实例保持其当前状态,当兼容的辅助角色可用时,可以稍后重试。 此策略是最安全的选项,因为它保留业务流程状态。 -
Fail:立即终止具有失败状态的业务流程实例。 当版本不匹配表示严重部署问题时,此选项可能适用。
何时使用每个策略
拒绝:如果希望编排稍后或在不同的工作者上重试,请使用此策略。 发生Reject故障时:
- 编排被拒绝并返回到工作队列。
- 另一个工作人员取消编排的排队。
- 出队的编排可能被分配给不同的工作线程,也可能再次分配给同一工作线程。
此过程将重复,直到有可以胜任编排任务的工作节点可用为止。 此策略可无缝处理逐步更新辅助角色的滚动部署。
失败:当预期没有其他版本的工作进程处理业务流程时使用此策略。 编排失败并进入终止状态。
使用特定版本启动编排
默认情况下,所有新的协调实例都使用在defaultVersion配置中指定的当前host.json。 但是,您可能会遇到需要使用与当前默认版本不同的特定版本来创建编排的情况。
何时使用特定版本
- 逐步迁移:即使在部署较新版本之后,仍使用较旧版本继续创建业务流程。
- 测试方案:在生产环境中测试特定的版本行为。
- 回滚情况:暂时恢复到使用以前版本创建实例。
- 特定于版本的工作流:不同的业务流程需要不同的编排版本。
[Function("HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
var options = new StartOrchestrationOptions
{
Version = "1.0"
};
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"ProcessOrderOrchestrator", orderId, options);
// ...
}
还可以从业务流程协调程序函数中使用特定版本启动子业务流程:
[Function("MainOrchestrator")]
public static async Task<string> RunMainOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var subOptions = new SubOrchestratorOptions
{
Version = "1.0"
};
var result = await context.CallSubOrchestratorAsync<string>(
"ProcessPaymentOrchestrator", orderId, subOptions);
// ...
}
删除旧代码路径
随着时间的推移,你可能想要从业务流程协调程序函数中删除旧代码路径,以简化维护和减少技术债务。 请仔细删除代码以避免中断现有业务流程实例。
如果删除旧代码是安全的
- 使用旧版本的所有编排实例已完成(成功、失败或结束)。
- 不会使用旧版本创建新的编排实例。
- 通过监控或查询,检查是否没有实例在旧版本上运行。
- 自旧版本最后一次部署以来,已经过了足够的时间。
警告
在编排实例仍在运行这些版本时删除旧代码路径可能会导致确定性重播失败。 在删除代码之前,请始终检查没有实例使用旧版本。
最佳做法
版本管理
-
使用多部分版本控制:采用一致的版本控制方案,例如
major.minor.patch。 - 记录重大变更:清楚地记录哪些更改需要新版本。
- 计划版本生命周期:定义何时删除旧代码路径。
代码组织
- 独立的版本逻辑:对不同版本使用清晰分支或独立的方法。
- 保留确定性:部署后不要修改现有版本逻辑。 如果更改是绝对必要的,比如关键的错误修复,请确保它们保持确定性行为,并且不会更改操作顺序。
- 全面测试:测试所有版本路径,尤其是在转换期间。
监视和可观测性
- 日志版本信息:在日志记录中包含版本,以便更轻松地进行调试。
- 监视版本分发:跟踪哪些版本正在主动运行。
- 设置警报:监视任何与版本相关的错误。
故障排除
常见问题
问题:部署版本 2.0 后,使用版本 1.0 创建的业务流程实例失败
- 解决方案:确保业务流程协调程序中的版本 1.0 代码路径完全相同。 对执行序列所做的任何更改都可能会中断确定性重播。
问题:运行旧协调器版本的工作程序无法运行新的协调任务
-
解决方案:此行为应为预期。 运行时可防止较旧的工作程序使用较新版本编排。 确保所有工作进程都更新到最新版本,并且他们在
defaultVersion中的host.json设置也相应更新。
-
解决方案:此行为应为预期。 运行时可防止较旧的工作程序使用较新版本编排。 确保所有工作进程都更新到最新版本,并且他们在
问题:业务流程协调程序中不提供版本信息(无论
context.Version设置如何,context.getVersion()或defaultVersion为 null)- 解决方案:检查 “先决条件 ”部分,确保环境满足业务流程版本控制的所有要求。
问题:较新版本的业务流程进度非常缓慢或停滞
-
解决方法:此问题可能有不同的根本原因:
-
较新的辅助角色不足:确保部署并激活包含相同或更高版本的
defaultVersion足够辅助角色。 - 旧工件的编排路由干扰:旧工件可能会干扰编排路由机制,从而使新工件难以接手编排任务。 某些存储提供程序(Azure Storage或 MSSQL)可能会特别明显地受到这种干扰。 通常,Azure Functions平台会确保在发布后不久就删除旧的工作进程,因此任何延迟通常都不显著。 请考虑将 Durable Task Scheduler 用于改进的路由机制。
-
较新的辅助角色不足:确保部署并激活包含相同或更高版本的
-
解决方法:此问题可能有不同的根本原因: