你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
业务流程版本控制解决了将更改部署到业务流程协调程序函数的核心难题,同时维护了 Durable Functions 所需的确定性执行模型。 如果没有此功能,对业务流程协调程序逻辑或活动函数签名的重大更改将导致正在进行中的业务流程实例在重播期间失败,因为它们会破坏确保可靠业务流程执行的确定性要求。 此内置功能通过最少的配置提供自动版本隔离。 它与后端无关,因此它可由利用任何 Durable Function 存储提供程序(包括 Durable Task Scheduler)的应用使用。
Note
对于持久任务计划程序用户,如果使用 Durable Task SDK 而不是 Durable Functions,则应参阅 Durable Task SDK 版本控制文章。
Terminology
本文使用两个相关但不同的术语:
- 协调函数 (或简称“协调器”):是定义工作流逻辑的函数代码——工作流执行方式的模板或蓝图。
- 业务流程实例 (或只是“业务流程”):指业务流程协调程序函数的特定运行执行,其自己的状态、实例 ID 和输入。 多个协调程序实例可以从同一协调程序函数并发运行。
了解此区别对于业务流程版本控制至关重要,其中业务流程协调程序函数代码包含版本感知逻辑,而业务流程实例在创建时永久关联到特定版本。
工作原理
编排版本控制功能遵循以下核心原则:
版本关联:创建业务流程实例时,它会永久获取与其关联的版本。
版本感知执行:Orchestrator 函数代码可以相应地检查与当前业务流程实例和分支执行关联的版本值。
向后兼容性:运行较新的协调程序版本的工作者可以继续运行由旧协调程序版本创建的编排实例。
前向保护:运行时系统会自动阻止运行较旧版本协调器的辅助角色执行由较新版本协调器启动的协调任务。
Important
业务流程版本控制目前为 .NET 隔离模型中运行的应用提供公共预览版。 使用 Microsoft.Azure.Functions.Worker.Extensions.DurableTask
包版本 >=1.5.0。
Basic usage
业务流程版本控制最常见的用例是当你需要对业务流程协调程序逻辑进行重大更改,同时保持现有正在进行的业务流程实例与其原始版本一起运行时。 只需更新 defaultVersion
中的 host.json
并修改业务流程协调程序代码,以便相应地检查业务流程版本和分支执行。 让我们讲解所需的步骤。
Note
本节中所述的行为面向最常见的情况,这是默认配置提供的行为。 但是,如果需要,可以修改它(有关详细信息,请参阅 高级用法 )。
步骤 1:defaultVersion 配置
若要为业务流程配置默认版本,需要在 Azure Functions 项目中添加或更新 defaultVersion
文件中的 host.json
设置:
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>"
}
}
}
版本字符串可以遵循适合版本控制策略的任何格式:
- 多部分版本控制:
"1.0.0"
、"2.1.0"
- 简单编号:
"1"
"2"
- 基于日期:
"2025-01-01"
- 自定义格式:
"v1.0-release"
设置 defaultVersion
后,所有新的编排实例都将永久关联到该版本。
版本比较规则
当Strict
或CurrentOrOlder
策略被选择时(请参阅版本匹配),运行时使用以下规则将业务流程实例的版本与defaultVersion
工作节点的值进行比较:
- 空或 null 版本被视为相等。
- 空版本或 null 版本被视为早于任何定义的版本。
- 如果这两个版本都可以解析为
System.Version
,则使用CompareTo
方法。 - 否则,将执行不区分大小写的字符串比较。
步骤 2:协调器函数逻辑
若要在业务流程协调程序函数中实现版本感知逻辑,可以使用传递给业务流程协调程序的上下文参数来访问当前业务流程实例的版本,这使你可以基于版本对业务流程协调程序逻辑进行分支。
Important
在实现版本感知逻辑时,保留旧版本的确切协调器逻辑至关重要。 对现有版本的活动调用的序列、顺序或签名所做的任何更改都可能会中断确定性的重播,并导致正在进行的业务流程失败或产生不正确的结果。 部署后,旧版本代码路径必须保持不变。
[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
...
}
...
}
Note
该 context.Version
属性是 只读 的,反映了在创建编排实例时永久关联的版本。 在编排流程执行期间,不能修改此值。 如果要通过非 host.json
的方式指定版本,可以在使用协调客户端 API 启动协调实例时执行此操作(请参阅 使用特定版本启动新的协调和子协调)。
Tip
如果刚刚开始使用业务流程版本控制,并且已在指定 defaultVersion
之前创建了正在进行的业务流程,则现在仍然可以将 defaultVersion
设置添加到 host.json
。 对于以前创建的所有业务流程,context.Version
返回 null
(或等效的从属语言值),以便可以构建业务流程协调程序逻辑,以便相应地处理旧版(null 版本)和新版的业务流程。 在 C# 中,可以检查 context.Version == null
或 context.Version is null
以处理旧案例。 另请注意,指定 "defaultVersion": null
in host.json
相当于根本不指定它。
Tip
根据你的情况,你可能倾向于在不同的级别进行分支。 可以根据需要进行本地更改,如示例所示。 或者,你可以在更高层次进行分支,甚至在整个协调器实现层级进行分支,这会引入一些代码重复,但可能会使执行流程保持清晰。 由你决定选择最适合你的方案和编码样式的方法。
部署后会发生什么情况
下面是使用新版本逻辑部署更新的业务流程协调程序函数后预期的情况:
工作进程共存:包含新协调器函数代码的工作进程将启动,而具有旧代码的一些工作进程可能仍然活动。
新实例的版本分配:新辅助角色创建的所有新业务流程和子业务流程都将从分配给它们的
defaultVersion
中获取版本。新的辅助角色兼容性:新辅助角色将能够处理新创建的业务流程和以前存在的业务流程,因为上一部分的步骤 2 中执行的更改可确保通过版本感知分支逻辑实现向后兼容性。
旧辅助角色限制:旧辅助角色将仅被允许处理版本等于或低于在其自己的
defaultVersion
的host.json
中指定的版本的业务流程,因为它们不应具有与较新版本兼容的业务流程协调程序代码。 此限制可防止执行错误和意外行为。
Note
业务流程版本控制不会影响辅助角色生命周期。 Azure Functions 平台按照托管选项根据常规规则管理辅助角色预配和解除授权。
示例:替换序列中的活动
此示例演示如何使用业务流程版本控制将一个活动替换为序列中的另一个活动。
Version 1.0
host.json configuration:
{
"extensions": {
"durableTask": {
"defaultVersion": "1.0"
}
}
}
Orchestrator function:
[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 configuration:
{
"extensions": {
"durableTask": {
"defaultVersion": "2.0"
}
}
}
Orchestrator function:
using DurableTask.Core.Settings;
[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var orderId = context.GetInput<string>();
await context.CallActivityAsync("ValidateOrder", orderId);
if (VersioningSettings.CompareVersions(context.Version, "1.0") <= 0)
{
// Preserve original logic for existing instances
await context.CallActivityAsync("ProcessPayment", orderId);
}
else // a higher version (including 2.0)
{
// New logic with discount processing (replaces payment processing)
await context.CallActivityAsync("ApplyDiscount", orderId);
await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
}
await context.CallActivityAsync("ShipOrder", orderId);
return "Order processed successfully";
}
Advanced usage
对于更复杂的版本控制方案,可以配置其他设置来控制运行时如何处理版本匹配和不匹配。
Tip
对于大多数方案,使用默认配置(具有 CurrentOrOlder
的 Reject
)在版本转换期间保留业务流程状态时启用安全的滚动部署。 仅当有无法满足默认行为的特定要求时,我们建议继续执行高级配置。
Version matching
在加载编排器函数时,该 versionMatchStrategy
设置确定运行时如何匹配编排版本。 它根据版本兼容性控制工作者可以处理的编排实例。
Configuration
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>",
"versionMatchStrategy": "CurrentOrOlder"
}
}
}
Available strategies
None
(不推荐):完全忽略编排版本。 所有接收的工作都会被处理,无论版本如何。 此策略有效地禁用版本检查,并允许任何工作角色处理任何编排实例。Strict
:仅处理业务流程中其版本与辅助角色的defaultVersion
中的host.json
指定的版本完全相同的任务。 此策略提供最高级别的版本隔离,但需要仔细的部署协调以避免业务流程孤立。 版本不匹配的后果在 “版本不匹配处理 ”部分中介绍。CurrentOrOlder
(默认值):处理业务流程中版本小于或等于辅助角色的defaultVersion
中的host.json
指定的版本的任务。 此策略可实现向后兼容性,允许较新的工作节点处理由旧的协调程序版本启动的业务流程,同时阻止较旧的工作节点处理较新的业务流程。 版本不匹配的后果在 “版本不匹配处理 ”部分中介绍。
版本不匹配处理
该 versionFailureStrategy
设置决定当编排实例版本与当前 defaultVersion
版本不匹配时的处理方式。
Configuration:
{
"extensions": {
"durableTask": {
"defaultVersion": "<version>",
"versionFailureStrategy": "Reject"
}
}
}
Available strategies:
Reject
(默认):不处理编排。 编排实例保持其当前状态,当兼容的工作节点可用时,稍后可以重试。 此策略是保留业务流程状态的最安全选项。Fail
:编排失败。 此策略会立即终止具有失败状态的业务流程实例,在版本不匹配表示严重部署问题的情况下可能适用。
使用特定版本启动新的业务流程和子业务流程
默认情况下,所有新的业务流程实例都是使用 defaultVersion
配置中指定的当前 host.json
创建的。 但是,你可能会遇到需要创建特定版本的编排的情况,即使它与当前默认版本不同。
何时使用特定版本:
- 逐步迁移:即使在部署较新版本后,你仍希望使用较旧版本继续创建业务流程。
- 测试方案:需要在生产环境中测试特定版本行为。
- 回滚情况:需要暂时还原为使用以前的版本创建实例。
- 特定于版本的工作流:不同的业务流程需要不同的编排版本。
使用业务流程客户端 API 创建新的业务流程实例时,可以通过提供特定的版本值来替代默认版本。 这允许对每个新的编排实例使用的版本进行细粒度的控制。
[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);
// ...
}
删除旧代码路径
随着时间的推移,你可能想要从业务流程协调程序函数中删除旧代码路径,以简化维护和减少技术债务。 但是,必须仔细删除代码,以避免中断现有的业务流程实例。
如果删除旧代码是安全的:
- 使用旧版本的所有业务流程实例都已完成(成功、失败或已终止)
- 不会使用旧版本创建新的编排实例。
- 你已通过监视或查询验证,没有实例在运行旧版本。
- 自上次部署旧版本以来已过足够的时间段(考虑到业务连续性要求)
删除最佳做法:
- 监视正在运行的实例:使用 Durable Functions 管理 API 查询使用特定版本的实例。
- 设置保留策略:定义要为每个版本保持向后兼容性的时间。
- 以增量方式删除:请考虑一次删除一个版本,而不是同时删除多个版本。
- 文档删除:保留有关删除版本和原因的清晰记录。
Warning
在业务流程实例仍在运行这些版本时移除旧代码路径可能会导致确定性重播失败或意外行为。 在删除代码之前,请始终验证没有实例使用旧版本。
Best practices
Version management
- 使用多部分版本控制:采用一致的版本控制方案,例如
major.minor.patch
。 - 记录重大变更:清楚地记录哪些更改需要新版本。
- 计划版本生命周期:定义何时删除旧代码路径。
Code organization
- 独立的版本逻辑:对不同版本使用清晰分支或独立的方法。
- 保留确定性:避免在部署后修改现有版本逻辑。 如果更改是绝对必要的(如关键 bug 修复),请确保它们保持确定性行为,并且不会更改操作序列,或者预期较新的业务流程协调程序版本在处理较旧的业务流程时会失败。
- 全面测试:测试所有版本路径,尤其是在转换期间。
监视和可观测性
- 日志版本信息:在日志记录中包含版本,以便更轻松地进行调试。
- 监视版本分发:跟踪哪些版本正在主动运行。
- 设置警报:监视任何与版本相关的错误。
Troubleshooting
Common issues
问题:部署版本 2.0 后,使用版本 1.0 创建的业务流程实例失败
- 解决方案:确保业务流程协调程序中的版本 1.0 代码路径保持不变。 对执行序列所做的任何更改都可能会中断确定性重播。
问题:运行旧版本协调器的工作人员无法执行新的业务流程
- 解决方案:这是预期行为。 运行时会有意防止较旧的辅助角色使用较新版本执行业务流程,以维护安全。 确保所有辅助角色都更新到最新的业务流程协调程序版本,并相应地更新其
defaultVersion
中的host.json
设置。 如果需要,可以使用高级配置选项来修改此行为(有关详细信息,请参阅 高级用法 )。
- 解决方案:这是预期行为。 运行时会有意防止较旧的辅助角色使用较新版本执行业务流程,以维护安全。 确保所有辅助角色都更新到最新的业务流程协调程序版本,并相应地更新其
问题:编排器中不提供版本信息(无论
context.Version
设置如何,defaultVersion
都为 null)- 解决方案:验证你使用的是受支持的编程语言和支持业务流程版本化的 Durable Functions 扩展版本。
- 对于 .NET 独立版,请使用
Microsoft.Azure.Functions.Worker.Extensions.DurableTask
版本 1.5.0 或更高版本。
- 对于 .NET 独立版,请使用
- 解决方案:验证你使用的是受支持的编程语言和支持业务流程版本化的 Durable Functions 扩展版本。
问题:较新版本的业务流程进度非常缓慢或完全停滞
- 解决方法:问题可能有不同的根本原因:
- 较新的辅助角色不足:确保在
defaultVersion
中部署并激活包含相同或更高版本的足够数量的辅助角色来处理较新的业务流程。 - 来自旧工人的协调路由干扰:旧工人可能会影响协调路由机制,使新工人更难接收协调进行处理。 使用某些存储提供程序(Azure 存储或 MSSQL)时,这尤其明显。 通常,Azure Functions 平台会在部署后不久清理旧的工作者,因此任何延迟通常并不显著。 但是,如果使用允许你控制旧辅助角色生命周期的配置,请确保旧辅助角色最终会被关闭。 或者,请考虑使用 持久任务计划程序,因为它提供了一种改进的路由机制,使其不太容易受到此问题的影响。
- 较新的辅助角色不足:确保在
- 解决方法:问题可能有不同的根本原因: