External Tasks
External Tasks sind der primäre Mechanismus, um externe Geschäftslogik an BPMN-Prozesse in der ProcessCube® Engine anzubinden. Ein External Task repräsentiert eine Aufgabe im Prozess, die von einem externen Worker abgearbeitet wird.
Architektur
Konzepte
Fetch-and-Lock-Muster
Der Worker pollt die Engine per Long Polling: Er sendet eine Anfrage an /fetch_and_lock, die von der Engine gehalten wird, bis entweder Tasks verfügbar sind oder der Timeout (Standard: 60 Sekunden) erreicht ist. Dadurch wird ein quasi-echtzeitnahes Verhalten ohne hohe Last auf der Engine erreicht.
Beim Fetch werden die Tasks sofort für den Worker gesperrt (Standard: 30 Sekunden). Nur der Worker mit der passenden WorkerId kann den Task abschließen oder den Lock verlängern.
Kapazitätssteuerung
Der Worker holt nur so viele Tasks, wie er gleichzeitig verarbeiten kann:
MaxTasksToFetch = MaxParallelWorkerCount - AktuelleTasksInBearbeitungWenn alle Slots belegt sind, pausiert der Fetch-Zyklus, bis Kapazität frei wird.
Lock-Management
Der LockScheduler läuft als Hintergrund-Task und überwacht alle aktiven Locks:
- Prüft jede Sekunde, ob ein Lock in weniger als 5 Sekunden abläuft
- Verlängert den Lock rechtzeitig per
PUT /extend_lock - Falls ein Lock bereits abgelaufen ist, wird der Task abgebrochen
- Handler-Ausführung und Lock-Erneuerung laufen als Race (
Task.WhenAny) — wer zuerst fertig ist, bestimmt das Ergebnis
Exponential Backoff
Bei Fetch-Fehlern (z.B. Engine nicht erreichbar) greift ein exponentieller Backoff:
Wartezeit = 2^Fehleranzahl + Zufallsoffset (30-300ms)Die Sequenz ist: 2s → 4s → 8s → 16s → 30s (Maximum, konfigurierbar). Bei erfolgreichem Fetch wird der Zähler zurückgesetzt.
ExternalTask-Datenmodell
public class ExternalTask
{
public string Id { get; set; } // Eindeutige Task-ID
public string WorkerId { get; set; } // ID des zugewiesenen Workers
public string Topic { get; set; } // Topic-Name (z.B. "BestellungVerarbeiten")
public string FlowNodeInstanceId { get; set; } // Zugehöriger Flow-Node
public string CorrelationId { get; set; } // Korrelations-ID der Prozessinstanz
public string ProcessModelId { get; set; } // Prozessmodell-ID
public string ProcessInstanceId { get; set; } // Prozessinstanz-ID
public IIdentityReference Identity { get; set; } // Identität des Prozessstarters
public IPayloadAccessor Payload { get; set; } // Typisierter Zugriff auf den Payload
public DateTime? LockExpirationTime { get; set; }// Ablaufzeit des Locks
public DateTime CreatedAt { get; set; } // Erstellungszeitpunkt
}Handler-Aufrufstrategien
| Strategie | Verwendung |
|---|---|
UseHandlerMethod (async) | Einfache Fälle, Logik direkt als Lambda |
UseHandlerMethod (sync) | Synchrone Verarbeitung ohne async/await |
UseHandlerFactory | Neue Handler-Instanz pro Task via Factory |
UseHandlerActivator | Eigener Aktivierungsmechanismus (z.B. Custom DI) |
| DI-basiert (Hosting) | Automatisch über [ExternalTaskHandler] + DI-Container |
Weiterführende Seiten
- Manuelle Verarbeitung — Handler mit Callback und Factory
- Hosting Integration — .NET Generic Host mit DI und automatischer Handler-Erkennung