Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Actuellement, les kits SDK Durable Task ne sont pas disponibles pour JavaScript et PowerShell.
Important
Actuellement, les kits SDK Durable Task ne sont pas disponibles pour JavaScript et PowerShell.
Dans ce guide de démarrage rapide, vous apprenez à :
- Configurez et exécutez l’émulateur Durable Task Scheduler pour le développement local.
- Exécutez les projets Worker et client.
- Consultez les journaux d’activité d’Azure Container Apps.
- Passez en revue l’état et l’historique de l’orchestration via le tableau de bord planificateur de tâches durables.
Conditions préalables
Avant de commencer :
- Vérifiez que vous disposez du Kit de développement logiciel (SDK) .NET 8 ou version ultérieure.
- Installez Docker pour exécuter l’émulateur.
- Installer Azure Developer CLI
- Clonez le dépôt GitHub du Planificateur de tâches durables pour utiliser l’exemple de démarrage rapide.
- Vérifiez que vous disposez de Python 3.9+ ou version ultérieure.
- Installez Docker pour exécuter l’émulateur.
- Installer Azure Developer CLI
- Clonez le dépôt GitHub du Planificateur de tâches durables pour utiliser l’exemple de démarrage rapide.
- Vérifiez que vous disposez de Java 8 ou 11.
- Installez Docker pour exécuter l’émulateur.
- Installer Azure Developer CLI
- Clonez le dépôt GitHub du Planificateur de tâches durables pour utiliser l’exemple de démarrage rapide.
Préparer le projet
Dans une nouvelle fenêtre de terminal, à partir du Azure-Samples/Durable-Task-Scheduler répertoire, accédez à l’exemple de répertoire.
cd /samples/durable-task-sdks/dotnet/FunctionChaining
cd /samples/durable-task-sdks/python/function-chaining
cd /samples/durable-task-sdks/java/function-chaining
Déployer à l’aide d’Azure Developer CLI
Exécutez
azd uppour approvisionner l’infrastructure et déployer l’application sur Azure Container Apps en une seule commande.azd upLorsque vous y êtes invité dans le terminal, fournissez les paramètres suivants.
Paramètre Descriptif Nom de l’environnement Préfixe du groupe de ressources créé pour contenir toutes les ressources Azure. Emplacement Azure L’emplacement Azure pour vos ressources. Abonnement Azure L’abonnement Azure pour vos ressources. Ce processus peut prendre un certain temps. Une fois la commande
azd upterminée, la sortie de l’interface CLI affiche deux liens du portail Azure pour surveiller la progression du déploiement. La sortie montre également commentazd up:- Crée et configure toutes les ressources Azure nécessaires via les fichiers Bicep fournis dans le répertoire
./infraà l’aide deazd provision. Une fois approvisionné par Azure Developer CLI, vous pouvez accéder à ces ressources via le portail Azure. Les fichiers qui approvisionnent les ressources Azure sont les suivants :main.parameters.jsonmain.bicep- Un répertoire de ressources
apporganisé par fonctionnalité - Une bibliothèque de référence
corequi contient les modules Bicep utilisés par le modèleazd
- Déploie le code à l’aide de
azd deploy
Sortie attendue
Packaging services (azd package) (✓) Done: Packaging service client - Image Hash: {IMAGE_HASH} - Target Image: {TARGET_IMAGE} (✓) Done: Packaging service worker - Image Hash: {IMAGE_HASH} - Target Image: {TARGET_IMAGE} Provisioning Azure resources (azd provision) Provisioning Azure resources can take some time. Subscription: SUBSCRIPTION_NAME (SUBSCRIPTION_ID) Location: West US 2 You can view detailed progress in the Azure Portal: https://portal.azure.com/#view/HubsExtension/DeploymentDetailsBlade/~/overview/id/%2Fsubscriptions%SUBSCRIPTION_ID%2Fproviders%2FMicrosoft.Resources%2Fdeployments%2FCONTAINER_APP_ENVIRONMENT (✓) Done: Resource group: GENERATED_RESOURCE_GROUP (1.385s) (✓) Done: Container Apps Environment: GENERATED_CONTAINER_APP_ENVIRONMENT (54.125s) (✓) Done: Container Registry: GENERATED_REGISTRY (1m27.747s) (✓) Done: Container App: SAMPLE_CLIENT_APP (21.39s) (✓) Done: Container App: SAMPLE_WORKER_APP (24.136s) Deploying services (azd deploy) (✓) Done: Deploying service client - Endpoint: https://SAMPLE_CLIENT_APP.westus2.azurecontainerapps.io/ (✓) Done: Deploying service worker - Endpoint: https://SAMPLE_WORKER_APP.westus2.azurecontainerapps.io/ SUCCESS: Your up workflow to provision and deploy to Azure completed in 10 minutes 34 seconds.- Crée et configure toutes les ressources Azure nécessaires via les fichiers Bicep fournis dans le répertoire
Confirmer la réussite du déploiement
Dans le portail Azure, vérifiez que les orchestrations s’exécutent correctement.
Copiez le nom du groupe de ressources à partir de la sortie du terminal.
Connectez-vous au portail Azure et recherchez ce nom de groupe de ressources.
Dans la page vue d’ensemble du groupe de ressources, cliquez sur la ressource d’application conteneur cliente.
Sélectionnez Monitoring>Diffusion en continu de journaux.
Confirmez que le conteneur client journalise les tâches de chaînage de fonctions.
Revenez à la page du groupe de ressources pour sélectionner le
workerconteneur.Sélectionnez Monitoring>Diffusion en continu de journaux.
Confirmez que le conteneur Worker journalise les tâches de chaînage de fonctions.
Vérifiez que l’exemple d’application conteneur journalisera les tâches de chaînage de fonctions.
Présentation du code
Projet client
Projet client :
- Utilise la même logique de chaîne de connexion que le travailleur
- Implémente un planificateur d’orchestration séquentiel qui :
- Planifie 20 instances d’orchestration, une à la fois
- Attend 5 secondes entre la planification de chaque orchestration
- Suit toutes les instances d’orchestration dans une liste
- Attend que toutes les orchestrations soient terminées avant de sortir
- Utilise la journalisation standard pour afficher la progression et les résultats
// Schedule 20 orchestrations sequentially
for (int i = 0; i < TotalOrchestrations; i++)
{
// Create a unique instance ID
string instanceName = $"{name}_{i+1}";
// Schedule the orchestration
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"GreetingOrchestration",
instanceName);
// Wait 5 seconds before scheduling the next one
await Task.Delay(TimeSpan.FromSeconds(IntervalSeconds));
}
// Wait for all orchestrations to complete
foreach (string id in allInstanceIds)
{
OrchestrationMetadata instance = await client.WaitForInstanceCompletionAsync(
id, getInputsAndOutputs: false, CancellationToken.None);
}
Projet de Worker
Le projet Worker contient :
- GreetingOrchestration.cs : définit les fonctions d’orchestrateur et d’activité dans un seul fichier
- Program.cs : Configure l’hôte de travail avec une gestion appropriée des chaînes de connexion
Implémentation de l’orchestration
L’orchestration appelle directement chaque activité en séquence à l’aide de la méthode standard CallActivityAsync :
public override async Task<string> RunAsync(TaskOrchestrationContext context, string name)
{
// Step 1: Say hello to the person
string greeting = await context.CallActivityAsync<string>(nameof(SayHelloActivity), name);
// Step 2: Process the greeting
string processedGreeting = await context.CallActivityAsync<string>(nameof(ProcessGreetingActivity), greeting);
// Step 3: Finalize the response
string finalResponse = await context.CallActivityAsync<string>(nameof(FinalizeResponseActivity), processedGreeting);
return finalResponse;
}
Chaque activité est implémentée en tant que classe distincte décorée avec l’attribut [DurableTask] :
[DurableTask]
public class SayHelloActivity : TaskActivity<string, string>
{
// Implementation details
}
L'ouvrier utilise Microsoft.Extensions.Hosting pour une gestion correcte du cycle de vie :
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDurableTaskWorker()
.AddTasks(registry => {
registry.AddAllGeneratedTasks();
})
.UseDurableTaskScheduler(connectionString);
var host = builder.Build();
await host.StartAsync();
Client
Projet client :
- Utilise la même logique de chaîne de connexion que le travailleur
- Implémente un planificateur d’orchestration séquentiel qui :
- Planifie 20 instances d’orchestration, une à la fois
- Attend 5 secondes entre la planification de chaque orchestration
- Suit toutes les instances d’orchestration dans une liste
- Attend que toutes les orchestrations soient terminées avant de sortir
- Utilise la journalisation standard pour afficher la progression et les résultats
# Schedule all orchestrations first
instance_ids = []
for i in range(TOTAL_ORCHESTRATIONS):
try:
# Create a unique instance name
instance_name = f"{name}_{i+1}"
logger.info(f"Scheduling orchestration #{i+1} ({instance_name})")
# Schedule the orchestration
instance_id = client.schedule_new_orchestration(
"function_chaining_orchestrator",
input=instance_name
)
instance_ids.append(instance_id)
logger.info(f"Orchestration #{i+1} scheduled with ID: {instance_id}")
# Wait before scheduling next orchestration (except for the last one)
if i < TOTAL_ORCHESTRATIONS - 1:
logger.info(f"Waiting {INTERVAL_SECONDS} seconds before scheduling next orchestration...")
await asyncio.sleep(INTERVAL_SECONDS)
# ...
# Wait for all orchestrations to complete
for idx, instance_id in enumerate(instance_ids):
try:
logger.info(f"Waiting for orchestration {idx+1}/{len(instance_ids)} (ID: {instance_id})...")
result = client.wait_for_orchestration_completion(
instance_id,
timeout=120
)
Travailleur
Implémentation de l’orchestration
L’orchestration appelle directement chaque activité en séquence à l’aide de la fonction standard call_activity :
# Orchestrator function
def function_chaining_orchestrator(ctx, name: str) -> str:
"""Orchestrator that demonstrates function chaining pattern."""
logger.info(f"Starting function chaining orchestration for {name}")
# Call first activity - passing input directly without named parameter
greeting = yield ctx.call_activity('say_hello', input=name)
# Call second activity with the result from first activity
processed_greeting = yield ctx.call_activity('process_greeting', input=greeting)
# Call third activity with the result from second activity
final_response = yield ctx.call_activity('finalize_response', input=processed_greeting)
return final_response
Chaque activité est implémentée en tant que fonction distincte :
# Activity functions
def say_hello(ctx, name: str) -> str:
"""First activity that greets the user."""
logger.info(f"Activity say_hello called with name: {name}")
return f"Hello {name}!"
def process_greeting(ctx, greeting: str) -> str:
"""Second activity that processes the greeting."""
logger.info(f"Activity process_greeting called with greeting: {greeting}")
return f"{greeting} How are you today?"
def finalize_response(ctx, response: str) -> str:
"""Third activity that finalizes the response."""
logger.info(f"Activity finalize_response called with response: {response}")
return f"{response} I hope you're doing well!"
L'ouvrier utilise DurableTaskSchedulerWorker pour une gestion correcte du cycle de vie :
with DurableTaskSchedulerWorker(
host_address=host_address,
secure_channel=endpoint != "http://localhost:8080",
taskhub=taskhub_name,
token_credential=credential
) as worker:
# Register activities and orchestrators
worker.add_activity(say_hello)
worker.add_activity(process_greeting)
worker.add_activity(finalize_response)
worker.add_orchestrator(function_chaining_orchestrator)
# Start the worker (without awaiting)
worker.start()
L’exemple d’application conteneur contient à la fois le code worker et le code client.
Client
Le code client :
- Utilise la même logique de chaîne de connexion que le travailleur
- Implémente un planificateur d’orchestration séquentiel qui :
- Planifie 20 instances d’orchestration, une à la fois
- Attend 5 secondes entre la planification de chaque orchestration
- Suit toutes les instances d’orchestration dans une liste
- Attend que toutes les orchestrations soient terminées avant de sortir
- Utilise la journalisation standard pour afficher la progression et les résultats
// Create client using Azure-managed extensions
DurableTaskClient client = (credential != null
? DurableTaskSchedulerClientExtensions.createClientBuilder(endpoint, taskHubName, credential)
: DurableTaskSchedulerClientExtensions.createClientBuilder(connectionString)).build();
// Start a new instance of the registered "ActivityChaining" orchestration
String instanceId = client.scheduleNewOrchestrationInstance(
"ActivityChaining",
new NewOrchestrationInstanceOptions().setInput("Hello, world!"));
logger.info("Started new orchestration instance: {}", instanceId);
// Block until the orchestration completes. Then print the final status, which includes the output.
OrchestrationMetadata completedInstance = client.waitForInstanceCompletion(
instanceId,
Duration.ofSeconds(30),
true);
logger.info("Orchestration completed: {}", completedInstance);
logger.info("Output: {}", completedInstance.readOutputAs(String.class))
Travailleur
L’orchestration appelle directement chaque activité en séquence à l’aide de la méthode standard callActivity :
DurableTaskGrpcWorker worker = (credential != null
? DurableTaskSchedulerWorkerExtensions.createWorkerBuilder(endpoint, taskHubName, credential)
: DurableTaskSchedulerWorkerExtensions.createWorkerBuilder(connectionString))
.addOrchestration(new TaskOrchestrationFactory() {
@Override
public String getName() { return "ActivityChaining"; }
@Override
public TaskOrchestration create() {
return ctx -> {
String input = ctx.getInput(String.class);
String x = ctx.callActivity("Reverse", input, String.class).await();
String y = ctx.callActivity("Capitalize", x, String.class).await();
String z = ctx.callActivity("ReplaceWhitespace", y, String.class).await();
ctx.complete(z);
};
}
})
.addActivity(new TaskActivityFactory() {
@Override
public String getName() { return "Reverse"; }
@Override
public TaskActivity create() {
return ctx -> {
String input = ctx.getInput(String.class);
StringBuilder builder = new StringBuilder(input);
builder.reverse();
return builder.toString();
};
}
})
.addActivity(new TaskActivityFactory() {
@Override
public String getName() { return "Capitalize"; }
@Override
public TaskActivity create() {
return ctx -> ctx.getInput(String.class).toUpperCase();
}
})
.addActivity(new TaskActivityFactory() {
@Override
public String getName() { return "ReplaceWhitespace"; }
@Override
public TaskActivity create() {
return ctx -> {
String input = ctx.getInput(String.class);
return input.trim().replaceAll("\\s", "-");
};
}
})
.build();
// Start the worker
worker.start();