Langer Workflow benötigt? Durable Function erstellen

Veröffentlicht am:

CloudTrips benötigt einen Workflow, der Arbeit ausführen, warten und fortfahren kann, ohne eine einzelne Funktionsausführung aktiv zu halten. Durable Functions erweitert Azure Functions um gespeicherten Workflowzustand, Checkpoints, Wiederholungen und Wiederherstellung.

HTTP starter  → startet und liefert eine Instanz-ID
Orchestrator  → definiert die Workflow-Reihenfolge
Activity      → führt eine echte Arbeitseinheit aus
Durable timer → speichert die Wartezeit, ohne einen Worker zu belegen

Eine gewöhnliche Funktion sollte eine Ausführung schnell beenden. Eine Durable Orchestration kann Minuten, Tage oder länger warten, weil Azure ihren Verlauf in Storage speichert und den Zustand beim Fortsetzen rekonstruiert.

Erstelle die Function App

Suche nach Function App, wähle Create, dann Flex Consumption und Select. Trage ein:

Subscription: CloudTrips TEST
Resource group: rg-cloudtrips-durable-test-weu
Function App name: func-cloudtrips-durable-dmytro-test-weu
Region: West Europe
Runtime stack: Node.js
Version: 22 LTS
Instance size: 2048 MB

Verwende die neueste angebotene Node.js-LTS-Version, falls 22 LTS nicht mehr aufgeführt ist. Erstelle unter Storage das standardmäßige Host-Storage-Konto. Durable Functions verwendet Storage, um Orchestrierungsinstanzen, Verlauf, Nachrichten und Timer dauerhaft zu speichern.

Lasse Application Insights unter Monitoring aktiviert. Wähle unter Authentication Managed identity for all resources. Übernimm die übrigen Standardwerte und wähle Review + create > Create.

Der App-Name muss global eindeutig sein. Flex Consumption skaliert auf null, Storage und Application Insights können jedoch geringe Kosten verursachen.

Create Function App mit eigenständigem Durable-Functions-Host auf Flex Consumption

Erteile die Durable-Storage-Rollen

Managed Identity vermeidet einen Storage-Schlüssel, die Identität benötigt jedoch weiterhin Datenzugriff, um den Durable Task Hub zu erstellen und zu verwenden. Öffne in der Function App Settings > Identity, wähle User assigned und notiere die für die App ausgewählte verwaltete Identität.

Öffne das Host-Storage-Konto der Function App, wähle Access control (IAM) und weise dieser verwalteten Identität alle drei Rollen zu:

Storage Blob Data Contributor
Storage Queue Data Contributor
Storage Table Data Contributor

Durable Functions verwendet Blobs für Leases und große Nachrichten, Queues zur Planung von Orchestrator- und Activity-Arbeit und Tabellen für Instanzzustand und Ausführungsverlauf. Owner oder Contributor auf Subscription-Ebene ist kein Ersatz: Diese Management-Plane-Rollen gewähren keinen Zugriff auf Storage-Daten.

Existiert eine Rolle bereits für die Identität, füge sie nicht doppelt hinzu. Warte vor dem ersten Orchestrierungsstart einige Minuten auf die RBAC-Übertragung.

Storage-IAM mit Blob, Queue und Table Data Contributor für die Managed Identity der Function App

Ohne die Tabellenrolle kann der HTTP-Starter beim Aufruf von Table.CreateIfNotExistsAsync mit DurableTaskStorageException fehlschlagen.

Erstelle das lokale Projekt

Erstelle den Projektordner:

mkdir -p cloudtrips-durable/src/functions
cd cloudtrips-durable

Erstelle und validiere host.json:

cat > host.json <<'EOF'
{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}
EOF

jq . host.json

Das Extension Bundle liefert die Durable-Functions-Runtime-Bindings. jq muss gültiges JSON ausgeben; stelle keine leere host.json bereit.

Erstelle package.json:

cat > package.json <<'EOF'
{
  "name": "cloudtrips-durable",
  "version": "1.0.0",
  "main": "src/functions/*.js",
  "scripts": {
    "start": "func start"
  },
  "dependencies": {
    "@azure/functions": "^4.7.2",
    "durable-functions": "^3.3.0"
  }
}
EOF

jq . package.json

Das Node.js-v4-Programmiermodell registriert Funktionen im Code. Das Paket durable-functions 3.x ergänzt Orchestrator-, Activity- und Durable-Client-APIs.

Füge den Durable Workflow hinzu

Erstelle src/functions/tripWorkflow.js:

cat > src/functions/tripWorkflow.js <<'EOF'
const { app } = require('@azure/functions');
const df = require('durable-functions');

df.app.activity('recordTripStage', {
  handler: (stage, context) => {
    context.log(`Processing stage: ${stage}`);
    return `${stage} completed`;
  }
});

df.app.orchestration('tripOrchestrator', function* (context) {
  const results = [];

  results.push(
    yield context.df.callActivity('recordTripStage', 'Reservation requested')
  );

  const resumeAt = new Date(context.df.currentUtcDateTime);
  resumeAt.setMinutes(resumeAt.getMinutes() + 2);
  yield context.df.createTimer(resumeAt);

  results.push(
    yield context.df.callActivity('recordTripStage', 'Reservation confirmed')
  );

  return results;
});

app.http('startTripWorkflow', {
  methods: ['POST'],
  authLevel: 'anonymous',
  route: 'workflows/trips',
  extraInputs: [df.input.durableClient()],
  handler: async (request, context) => {
    const client = df.getClient(context);
    const instanceId = await client.startNew('tripOrchestrator');

    context.log(`Started orchestration ${instanceId}`);
    return client.createCheckStatusResponse(request, instanceId);
  }
});
EOF

Der Orchestrator ist eine Generatorfunktion, weil Durable Functions an jeder ausgegebenen Durable Task pausiert und den Workflowverlauf später wiedergeben kann. Halte Orchestratorcode deterministisch: Verwende für Workflowentscheidungen context.df.currentUtcDateTime statt new Date() und führe Netzwerk-, Datenbank- oder Zufallsarbeit in Activities aus.

Der zweiminütige Durable Timer hält keinen Worker und berechnet nicht durchgehend eine untätige Ausführung. Azure setzt einen Checkpoint, weckt die Orchestrierung nach dem Timer und plant die zweite Aktivität. Zwei Minuten lassen genügend Zeit, den Workflow im Zustand Running abzufragen.

Stelle den Workflow bereit

Paketisiere den Projektinhalt:

zip -r cloudtrips-durable.zip host.json package.json src

Stelle ihn mit einem Remote-Build der Abhängigkeiten bereit:

az functionapp deployment source config-zip \
  --resource-group rg-cloudtrips-durable-test-weu \
  --name func-cloudtrips-durable-dmytro-test-weu \
  --src cloudtrips-durable.zip \
  --build-remote true

Warte auf das Deployment-Ende und öffne Functions. Die Liste sollte zeigen:

startTripWorkflow
tripOrchestrator
recordTripStage

Function App mit HTTP-Starter, Orchestrator und Activity-Funktion

Starte eine Orchestrierung

Öffne startTripWorkflow, wähle Get Function URL und kopiere die URL. Starte einen Workflow und speichere die Azure-Managementantwort:

curl --fail --show-error \
  --request POST \
  '<FUNCTION_URL>' \
  | tee durable-start.json

Der HTTP-Starter antwortet mit 202 Accepted, einer eindeutigen id und Management-URLs. Er hält die ursprüngliche HTTP-Anfrage nicht während des gesamten Workflows offen.

Extrahiere die Status-URL:

jq -r '.statusQueryGetUri' durable-start.json

Kopiere die vollständige Ausgabe dieses Befehls. Die URL enthält Instanz-ID, Task-Hub-Angaben und den Autorisierungscode für den Management-Endpunkt.

Terminal mit Orchestrierungsinstanz-ID und Status-Management-URLs

Frage den gespeicherten Status ab

Rufe die zurückgegebene Status-URL auf:

curl --fail --show-error \
  '<VOLLSTAENDIGE_statusQueryGetUri_HIER_EINFUEGEN>' \
  | jq '{instanceId, runtimeStatus, output}'

Ersetze den gesamten Platzhalter einschließlich der spitzen Klammern durch die vollständige aus statusQueryGetUri kopierte URL. Kürze die URL nicht und entferne ihren Query-String nicht.

Während des Durable Timers sollte runtimeStatus den Wert Running haben und output ist null. Wiederhole den Befehl nach etwa zwei Minuten. Erwartetes Ergebnis:

{
  "instanceId": "<unique-instance-id>",
  "runtimeStatus": "Completed",
  "output": [
    "Reservation requested completed",
    "Reservation confirmed completed"
  ]
}

Über die Instanz-ID korreliert Azure jeden Checkpoint und setzt auch nach einem Host-Neustart den richtigen Workflow fort. Öffne Monitor des Orchestrators, um die abgeschlossene Ausführung und Application-Insights-Telemetrie zu prüfen.

Abgeschlossene Durable-Functions-Orchestrierung mit beiden Activity-Ausgaben

Bereinigen

Lösche die isolierte Ressourcengruppe:

az group delete \
  --name rg-cloudtrips-durable-test-weu \
  --yes

Bestätige die Löschung:

az group exists --name rg-cloudtrips-durable-test-weu

Erwartetes Ergebnis: false.