在使用持久编排系统时要考虑的关键领域是如何处理编排的升级/降级。 当编排被中断并稍后恢复(例如,在主机更新期间),Durable Task Scheduler 将重播该编排的事件。 这样做是为了确保可靠性 -- 系统重播,以确保在执行下一步之前成功执行所有以前的步骤 - 这是持久执行范例的核心承诺。 因此,如果业务流程在部署之间发生更改,则执行的步骤可能不再相同。 如果发生这种情况,系统将抛出NonDeterministicError
并停止继续编排。
业务流程版本控制有助于防止与非确定性相关的问题,使你能够与新的(或旧)业务流程无缝协作。 持久任务计划程序具有两种不同的版本控制样式,下面将对此进行探讨。 请注意,不同的版本控制样式可以单独或一起使用。
重要
目前,持久任务 SDK 不适用于 JavaScript 和 PowerShell。
重要
目前,持久任务 SDK 不适用于 JavaScript 和 PowerShell。
重要
目前,Python SDK 中不提供版本控制。
基于客户端/上下文的条件版本控制
为了使业务流程具有版本,必须先在客户端中设置它。 对于 .NET SDK,这是通过标准主机生成器扩展完成的,如下所示:
注释
自 v1.9.0 起,在 .NET SDK(Microsoft.DurableTask.Client.AzureManaged)中可用。
builder.Services.AddDurableTaskClient(builder =>
{
builder.UseDurableTaskScheduler(connectionString);
builder.UseDefaultVersion("1.0.0");
});
注释
自 v1.6.0 起,Java SDK(com.microsoft:durabletask-client)中提供。
public DurableTaskClient durableTaskClient(DurableTaskProperties properties) {
// Create client using Azure-managed extensions
return DurableTaskSchedulerClientExtensions.createClientBuilder(properties.getConnectionString())
.defaultVersion("1.0")
.build();
}
添加后,此主机启动的任何业务流程都将使用该版本 1.0.0
。 版本本身是一个简单的字符串,接受任何值。 但是,SDK 将尝试将其转换为 。NET 的 System.Version
。 如果可以转换,则使用该库进行比较(如果不是)使用简单的字符串比较。
通过在客户端中提供版本,它也在 TaskOrchestrationContext
中变得可用。 这意味着版本可以在条件语句中使用。 只要编排的较新版本具有适当的版本控制,无论是旧版还是新版编排都可以在同一主机上协同运行。 如何使用版本的一个示例是:
[DurableTask]
class HelloCities : TaskOrchestrator<string, List<string>>
{
private readonly string[] Cities = ["Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo"];
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}"));
}
}
Console.WriteLine("HelloCities orchestration completed.");
return results;
}
}
public TaskOrchestration create() {
return ctx -> {
List<String> results = new ArrayList<>();
for (String city : new String[]{ "Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo" }) {
results.add(ctx.callActivity("SayHello", city, String.class).await());
if (VersionUtils.compareVersions(ctx.getVersion(), "2.0.0") >= 0) {
// Simulate a delay for newer versions
results.add(ctx.callActivity("SayGoodbye", city, String.class).await());
}
}
ctx.complete(results);
};
}
在此示例中,我们已将 SayGoodbye
活动添加到 HelloCities
编排中。 仅当业务流程至少为版本 2.0.0
时,才会调用此方法。 使用简单的条件语句时,版本小于 2.0.0
的任何业务流程将继续正常运行,并且任何新业务流程都将包含其中的新活动。
何时使用客户端版本控制
客户端版本控制为版本控制业务流程提供了最简单的机制,但与版本交互也是编程最密集的机制。 实质上,客户端版本控制提供的两项功能是能够为所有编排流程设置版本,以及能够以编程方式在编排流程中处理这些版本。 如果需要跨所有版本使用标准版本,或者需要特定版本的自定义逻辑,则应使用它。
基于工作线程的版本控制
可用于处理版本的附加策略是设置辅助角色版本控制。 业务流程仍需要客户端版本才能设置版本,但此方法允许用户避免其业务流程中的条件。 辅助角色版本控制允许辅助角色本身在业务流程开始执行之前,选择如何对这些不同版本的业务流程进行操作。 工作者版本控制需设置以下字段:
- 辅助角色本身的版本
- 将为辅助角色启动的子业务流程应用的默认版本
- 辅助角色将用于与业务流程版本匹配的策略
- 如果版本不符合匹配策略,工作者应采取的策略
不同的匹配策略如下所示:
名称 | DESCRIPTION |
---|---|
没有 | 处理工作时不考虑版本 |
严格 | 业务流程中的版本和辅助角色必须完全匹配 |
当前或更早版本 | 业务流程中的版本必须等于或小于辅助角色中的版本 |
不同的故障策略如下所示:
名称 | DESCRIPTION |
---|---|
拒绝 | 该业务流程将由辅助角色拒绝,但将保留在工作队列中以便稍后再次尝试执行 |
失败 | 业务流程将会失败并从工作队列中删除 |
与客户端版本控制类似,这些都通过标准主机生成器模式进行设置:
注释
自 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,
});
});
注释
自 v1.6.0 起,Java SDK(com.microsoft:durabletask-client)中提供。
private static DurableTaskGrpcWorker createTaskHubServer() {
DurableTaskGrpcWorkerBuilder builder = new DurableTaskGrpcWorkerBuilder();
builder.useVersioning(new DurableTaskGrpcWorkerVersioningOptions(
"1.0",
"1.0",
DurableTaskGrpcWorkerVersioningOptions.VersionMatchStrategy.CURRENTOROLDER,
DurableTaskGrpcWorkerVersioningOptions.VersionFailureStrategy.REJECT));
// Orchestrations can be defined inline as anonymous classes or as concrete classes
builder.addOrchestration(new TaskOrchestrationFactory() {
@Override
public String getName() { return "HelloCities"; }
@Override
public TaskOrchestration create() {
return ctx -> {
List<String> results = new ArrayList<>();
for (String city : new String[]{ "Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo" }) {
results.add(ctx.callActivity("SayHello", city, String.class).await());
}
ctx.complete(results);
};
}
});
// Activities can be defined inline as anonymous classes or as concrete classes
builder.addActivity(new TaskActivityFactory() {
@Override
public String getName() { return "SayHello"; }
@Override
public TaskActivity create() {
return ctx -> {
String input = ctx.getInput(String.class);
return "Hello, " + input + "!";
};
}
});
return builder.build();
}
当所需的行为是让业务流程稍后/在其他辅助角色上重试时,应使用Reject
故障策略。 当业务流程被拒绝时,它只会返回到工作队列。 当它再次被取消排队时,它可能会降落在同一个或不同的辅助角色。 此过程将重复进行,直到能够实际处理业务流程的辅助角色可用。 此策略允许无缝处理更新编排的部署。 随着部署的进行,无法处理业务流程的辅助角色将拒绝它,而可以处理的辅助角色将处理它。 具有混合辅助角色/业务流程版本的功能允许使用蓝绿部署等方案。
如果不需要其他版本,则应使用 Fail
失败策略。 在这种情况下,新版本是一个异常,任何工作者甚至都不应该尝试操作它。 因此,持久任务计划程序将是业务流程失败,使其处于终端状态。
何时使用工作进程版本控制
辅助角色版本控制应在根本不应该执行未知或不支持版本的业务流程的情况下使用。 辅助角色版本控制将停止业务流程执行,而不是将版本处理代码置于辅助角色中。 这样可以简化编排代码。 无需进行任何代码更改,即可处理各种部署方案,例如前面提到的蓝绿部署。