Orchestrator functies kunnen andere orchestrator functies aanroepen als sub-orchestrations. Een suborchestratie wordt uitgevoerd als kind van de aanroepende (ouder) orchestrator en gedraagt zich als een activiteit vanuit het perspectief van de beller: het kan een waarde teruggeven, uitzonderingen opwerpen die door de ouder zijn opgevangen en automatische nieuwe pogingen ondersteunen.
Wanneer sub-orkestraties worden gebruikt
Gebruik sub-orkestraties wanneer u het nodig heeft:
-
Bouwblokken voor herbruikbare werkstromen opstellen: Extraheer een werkstroom met meerdere stappen naar een eigen orkestrator, zodat meerdere bovenliggende orkestraties deze kunnen aanroepen.
-
Parallel uitwaaierindelingen: Plan veel exemplaren van dezelfde orchestrator gelijktijdig en wacht totdat ze allemaal zijn voltooid.
-
Complexe werkstromen organiseren: Breek een grote indeling in benoemde, testbare stukken in plaats van één lange functie.
Opmerking
Sub-orkestraties moeten in dezelfde app worden gedefinieerd als de hoofdorkestratie. Als u orchestraties in een andere app wilt aanroepen, gebruikt u in plaats van het HTTP 202-pollingpatroon. Zie HTTP-functies voor meer informatie.
In dit artikel:
Een suborkestratie definiëren
In het volgende voorbeeld ziet u een IoT-scenario waarin meerdere apparaten moeten worden ingesteld. De functie vertegenwoordigt de installatiewerkstroom die voor elk apparaat wordt uitgevoerd:
Geïsoleerd werkermodel
public static async Task DeviceProvisioningOrchestration(
[OrchestrationTrigger] TaskOrchestrationContext context, string deviceId)
{
// Step 1: Create an installation package in blob storage and return a SAS URL.
Uri sasUrl = await context.CallActivityAsync<Uri>("CreateInstallationPackage", deviceId);
// Step 2: Notify the device that the installation package is ready.
await context.CallActivityAsync("SendPackageUrlToDevice", (deviceId, sasUrl));
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
await context.WaitForExternalEvent<bool>("DownloadCompletedAck");
// Step 4: ...
}
Model tijdens het proces
public static async Task DeviceProvisioningOrchestration(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
string deviceId = context.GetInput<string>();
// Step 1: Create an installation package in blob storage and return a SAS URL.
Uri sasUrl = await context.CallActivityAsync<Uri>("CreateInstallationPackage", deviceId);
// Step 2: Notify the device that the installation package is ready.
await context.CallActivityAsync("SendPackageUrlToDevice", Tuple.Create(deviceId, sasUrl));
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
await context.WaitForExternalEvent<bool>("DownloadCompletedAck");
// Step 4: ...
}
const df = require("durable-functions");
df.app.orchestration("deviceProvisioningOrchestration", function* (context) {
const deviceId = context.df.getInput();
// Step 1: Create an installation package in blob storage and return a SAS URL.
const sasUrl = yield context.df.callActivity("createInstallationPackage", deviceId);
// Step 2: Notify the device that the installation package is ready.
yield context.df.callActivity("sendPackageUrlToDevice", { id: deviceId, url: sasUrl });
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
yield context.df.waitForExternalEvent("downloadCompletedAck");
// Step 4: ...
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
device_id = context.get_input()
# Step 1: Create an installation package in blob storage and return a SAS URL.
sas_url = yield context.call_activity("CreateInstallationPackage", device_id)
# Step 2: Notify the device that the installation package is ready.
yield context.call_activity("SendPackageUrlToDevice", { "id": device_id, "url": sas_url })
# Step 3: Wait for the device to acknowledge that it has downloaded the new package.
yield context.call_activity("DownloadCompletedAck")
# Step 4: ...
param($Context)
$deviceId = $Context.Input
# Step 1: Create an installation package in blob storage and return a SAS URL.
$sasUrl = Invoke-DurableActivity -FunctionName "CreateInstallationPackage" -Input $deviceId
# Step 2: Notify the device that the installation package is ready.
$deviceInfo = @{
id = $deviceId
url = $sasUrl
}
Invoke-DurableActivity -FunctionName "SendPackageUrlToDevice" -Input $deviceInfo
# Step 3: Wait for the device to acknowledge that it has downloaded the new package.
Start-DurableExternalEventListener -EventName "DownloadCompletedAck"
# Step 4: ...
@FunctionName("DeviceProvisioningOrchestration")
public void deviceProvisioningOrchestration(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
// Step 1: Create an installation package in blob storage and return a SAS URL.
String deviceId = ctx.getInput(String.class);
String blobUri = ctx.callActivity("CreateInstallPackage", deviceId, String.class).await();
// Step 2: Notify the device that the installation package is ready.
String[] args = { deviceId, blobUri };
ctx.callActivity("SendPackageUrlToDevice", args).await();
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
ctx.waitForExternalEvent("DownloadCompletedAck").await();
// Step 4: ...
}
Durable Functions ondersteunt Go niet. Om Go-voorbeelden te zien, selecteer Durable Task SDKs bovenaan dit artikel.
using Microsoft.DurableTask;
[DurableTask]
public class DeviceProvisioningOrchestration : TaskOrchestrator<string, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, string deviceId)
{
// Step 1: Create an installation package in blob storage and return a SAS URL.
Uri sasUrl = await context.CallActivityAsync<Uri>("CreateInstallationPackage", deviceId);
// Step 2: Notify the device that the installation package is ready.
await context.CallActivityAsync("SendPackageUrlToDevice", (deviceId, sasUrl.ToString()));
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
await context.WaitForExternalEvent<bool>("DownloadCompletedAck");
// Step 4: ...
return null;
}
}
import { OrchestrationContext, TOrchestrator } from "@microsoft/durabletask-js";
const deviceProvisioningOrchestrator: TOrchestrator = async function* (
ctx: OrchestrationContext,
deviceId: string
): any {
// Step 1: Create an installation package in blob storage and return a SAS URL.
const sasUrl: string = yield ctx.callActivity(createInstallationPackage, deviceId);
// Step 2: Notify the device that the installation package is ready.
yield ctx.callActivity(sendPackageUrlToDevice, { id: deviceId, url: sasUrl });
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
yield ctx.waitForExternalEvent("DownloadCompletedAck");
// Step 4: ...
};
from durabletask import task
def device_provisioning_orchestrator(ctx: task.OrchestrationContext, device_id: str):
# Step 1: Create an installation package in blob storage and return a SAS URL.
sas_url = yield ctx.call_activity("create_installation_package", input=device_id)
# Step 2: Notify the device that the installation package is ready.
yield ctx.call_activity("send_package_url_to_device", input={"id": device_id, "url": sas_url})
# Step 3: Wait for the device to acknowledge that it has downloaded the new package.
yield ctx.wait_for_external_event("DownloadCompletedAck")
# Step 4: ...
Dit voorbeeld wordt weergegeven voor .NET, JavaScript, Java en Python.
import com.microsoft.durabletask.TaskOrchestration;
import com.microsoft.durabletask.TaskOrchestrationContext;
public class DeviceProvisioningOrchestration implements TaskOrchestration {
@Override
public void run(TaskOrchestrationContext ctx) {
String deviceId = ctx.getInput(String.class);
// Step 1: Create an installation package in blob storage and return a SAS URL.
String blobUri = ctx.callActivity("CreateInstallPackage", deviceId, String.class).await();
// Step 2: Notify the device that the installation package is ready.
String[] args = { deviceId, blobUri };
ctx.callActivity("SendPackageUrlToDevice", args).await();
// Step 3: Wait for the device to acknowledge that it has downloaded the new package.
ctx.waitForExternalEvent("DownloadCompletedAck").await();
// Step 4: ...
}
}
Deze orchestratorfunctie kan zelfstandig worden uitgevoerd voor het eenmalig instellen van een apparaat, of een bovenliggende orchestrator kan deze als suborkestratie inplannen met behulp van de call-sub-orchestrator API.
Suborkestraties parallel uitvoeren
In het volgende voorbeeld ziet u een bovenliggende orchestrator die meerdere subindelingen parallel uitgeeft. Sommige talen gebruiken een deterministische kindexemplaar-id (afgeleid van de bovenliggende exemplaar-id plus een index) om dubbele sub-orkestraties bij opnieuw afspelen te voorkomen.
Geïsoleerd werkermodel
[Function("ProvisionNewDevices")]
public static async Task ProvisionNewDevices(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
string[] deviceIds = await context.CallActivityAsync<string[]>("GetNewDeviceIds");
// Run multiple device provisioning flows in parallel
var provisioningTasks = new List<Task>();
foreach (string deviceId in deviceIds)
{
Task provisionTask = context.CallSubOrchestratorAsync("DeviceProvisioningOrchestration", deviceId);
provisioningTasks.Add(provisionTask);
}
await Task.WhenAll(provisioningTasks);
// ...
}
Model tijdens het proces
[FunctionName("ProvisionNewDevices")]
public static async Task ProvisionNewDevices(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
string[] deviceIds = await context.CallActivityAsync<string[]>("GetNewDeviceIds");
// Run multiple device provisioning flows in parallel
var provisioningTasks = new List<Task>();
foreach (string deviceId in deviceIds)
{
Task provisionTask = context.CallSubOrchestratorAsync("DeviceProvisioningOrchestration", deviceId);
provisioningTasks.Add(provisionTask);
}
await Task.WhenAll(provisioningTasks);
// ...
}
const df = require("durable-functions");
df.app.orchestration("provisionNewDevices", function* (context) {
const deviceIds = yield context.df.callActivity("getNewDeviceIds");
// Run multiple device provisioning flows in parallel
const provisioningTasks = [];
var id = 0;
for (const deviceId of deviceIds) {
const child_id = context.df.instanceId + `:${id}`;
const provisionTask = context.df.callSubOrchestrator(
"deviceProvisioningOrchestration",
deviceId,
child_id
);
provisioningTasks.push(provisionTask);
id++;
}
yield context.df.Task.all(provisioningTasks);
// ...
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
device_IDs = yield context.call_activity("GetNewDeviceIds")
# Run multiple device provisioning flows in parallel
provisioning_tasks = []
id_ = 0
for device_id in device_IDs:
child_id = f"{context.instance_id}:{id_}"
provision_task = context.call_sub_orchestrator("DeviceProvisioningOrchestration", device_id, child_id)
provisioning_tasks.append(provision_task)
id_ += 1
yield context.task_all(provisioning_tasks)
# ...
param($Context)
$deviceIds = Invoke-DurableActivity -FunctionName "GetNewDeviceIds"
# Run multiple device setting up flows in parallel
$provisioningTasks = @()
for ($i = 0; $i -lt $deviceIds.Count; $i++) {
$deviceId = $deviceIds[$i]
$childId = "$($Context.InstanceId):$i"
$provisionTask = Invoke-DurableSubOrchestrator `
-FunctionName "DeviceProvisioningOrchestration" `
-Input $deviceId `
-InstanceId $childId `
-NoWait
$provisioningTasks += $provisionTask
}
Wait-DurableTask -Task $provisioningTasks
# ...
@FunctionName("ProvisionNewDevices")
public void provisionNewDevices(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
List<?> deviceIDs = ctx.getInput(List.class);
// Schedule each device provisioning sub-orchestration to run in parallel
List<Task<Void>> parallelTasks = deviceIDs.stream()
.map(device -> ctx.callSubOrchestrator("DeviceProvisioningOrchestration", device))
.collect(Collectors.toList());
// ...
}
Durable Functions ondersteunt Go niet. Om Go-voorbeelden te zien, selecteer Durable Task SDKs bovenaan in dit artikel.
Volgende stappen
using Microsoft.DurableTask;
[DurableTask]
public class ProvisionNewDevices : TaskOrchestrator<object?, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
{
string[] deviceIds = await context.CallActivityAsync<string[]>("GetNewDeviceIds");
// Run multiple device provisioning flows in parallel
var provisioningTasks = new List<Task>();
foreach (string deviceId in deviceIds)
{
Task provisionTask = context.CallSubOrchestratorAsync("DeviceProvisioningOrchestration", deviceId);
provisioningTasks.Add(provisionTask);
}
await Task.WhenAll(provisioningTasks);
return null;
}
}
import { OrchestrationContext, TOrchestrator, whenAll } from "@microsoft/durabletask-js";
const provisionNewDevices: TOrchestrator = async function* (ctx: OrchestrationContext): any {
const deviceIds: string[] = yield ctx.callActivity(getNewDeviceIds);
// Run multiple device provisioning flows in parallel
const provisioningTasks = deviceIds.map((deviceId: string) =>
ctx.callSubOrchestrator(deviceProvisioningOrchestrator, deviceId)
);
yield whenAll(provisioningTasks);
};
from durabletask import task
def provision_new_devices(ctx: task.OrchestrationContext, _):
device_ids = yield ctx.call_activity("get_new_device_ids")
# Run multiple device provisioning flows in parallel
provisioning_tasks = []
for device_id in device_ids:
provision_task = ctx.call_sub_orchestrator(device_provisioning_orchestrator, input=device_id)
provisioning_tasks.append(provision_task)
yield task.when_all(provisioning_tasks)
Dit voorbeeld wordt getoond voor .NET, JavaScript, Java, Python en Go.
import com.microsoft.durabletask.TaskOrchestration;
import com.microsoft.durabletask.TaskOrchestrationContext;
public class ProvisionNewDevices implements TaskOrchestration {
@Override
public void run(TaskOrchestrationContext ctx) {
List<?> deviceIDs = ctx.getInput(List.class);
// Schedule each device provisioning sub-orchestration to run in parallel
List<Task<Void>> parallelTasks = deviceIDs.stream()
.map(device -> ctx.callSubOrchestrator("DeviceProvisioningOrchestration", device))
.collect(Collectors.toList());
ctx.allOf(parallelTasks).await();
}
}
De volgende zelfstandige workflowfuncties tonen de compositie van ouder/kind aan zonder externe apparaatservices. Registreer ze als ParentGreeting en ChildGreeting.
package main
import (
"fmt"
"github.com/microsoft/durabletask-go/task"
)
func childGreeting(ctx *task.OrchestrationContext) (any, error) {
var name string
if err := ctx.GetInput(&name); err != nil {
return nil, err
}
return fmt.Sprintf("Hello %s!", name), nil
}
func parentGreeting(ctx *task.OrchestrationContext) (any, error) {
var names []string
if err := ctx.GetInput(&names); err != nil {
return nil, err
}
pending := make([]task.Task, len(names))
for i, name := range names {
pending[i] = ctx.CallSubOrchestrator(
"ChildGreeting", task.WithSubOrchestratorInput(name))
}
if err := ctx.WhenAll(pending...); err != nil {
return nil, err
}
results := make([]string, len(pending))
for i, child := range pending {
if err := child.Await(&results[i]); err != nil {
return nil, err
}
}
return results, nil
}
Plan alle onderliggende processen voordat je wacht om ze parallel uit te voeren. De resultaten blijven in invoervolgorde. Voor kindresultaten, instance ID's, metadata en fouten, zie het voorbeeld van Go SDK sub-orchestraties.
Volgende stappen