Dispatcher-Tuning
Der Dispatcher entscheidet, wann Actor-Nachrichten auf der JavaScript-Event-Loop laufen. Drei Formen werden ausgeliefert:
| Dispatcher | Schedult via | Passt zu |
|---|---|---|
MicrotaskDispatcher | queueMicrotask | CPU-tight; kein I/O. |
ImmediateDispatcher (Default) | setImmediate / setTimeout(0) | HTTP-Server + gemischtes I/O. |
ThroughputDispatcher | setImmediate mit N-dann-yield | Batch-Verarbeitung. |
Der Default — ImmediateDispatcher — passt für die meisten Apps.
Diese Seite behandelt, wann er nicht passt und wie du eine bessere
Wahl triffst.
Default-Verhalten
Abschnitt betitelt „Default-Verhalten“const system = ActorSystem.create('my-app');// ↑ nutzt per Default ImmediateDispatcherActor-Nachrichten laufen via setImmediate, das zwischen jeder
Nachricht yieldet — damit I/O-Callbacks (HTTP-Handler,
Broker-Nachrichten, Timer-Feuer) natürlich verschränken.
Für HTTP-Server + Broker-Actor-Cluster (der häufige Fall) ergibt das gute HTTP-Latenz auf Kosten von etwas höherem Per-Message-Overhead.
Symptom: hohe HTTP-Latenz unter Actor-Last
Abschnitt betitelt „Symptom: hohe HTTP-Latenz unter Actor-Last“P99-HTTP-Response-Time ist 200ms; Actors verarbeiten ZehntausendeNachrichten/sec. Die Actor-Arbeit ist nicht das Problem — sonderndass HTTP-Requests nicht drankommen.Ursache: Ein Actor (oder eine Gruppe Actors) verarbeitet Nachrichten so schnell, dass HTTP-Handler auf ihren Turn warten.
Fix: Beim ImmediateDispatcher (Default) bleiben und die
beschäftigten Actors mit einem Throughput-Dispatcher pro Actor
bounden:
import { ThroughputDispatcher } from 'actor-ts';
const heavyActor = system.spawn( Props.create(() => new HeavyWorker()) .withDispatcher(new ThroughputDispatcher({ throughput: 100 })),);Der schwere Actor verarbeitet 100 Nachrichten, yieldet, lässt HTTP aufholen, verarbeitet 100 weitere. HTTP-Latenz sinkt; Durchsatz beim schweren Actor wird kaum beeinträchtigt.
Symptom: niedriger Actor-Durchsatz, niedrige CPU-Nutzung
Abschnitt betitelt „Symptom: niedriger Actor-Durchsatz, niedrige CPU-Nutzung“Das Actor-System macht 1000 msg/sec auf einer idle CPU. Profilzeigt Zeit in setImmediate verbracht.Ursache: ImmediateDispatcher hat Per-Message-Overhead durch
das Yield zur Event Loop bei jeder Nachricht. Für Tight-Loop-CPU-Arbeit
ohne I/O ist das Verschwendung.
Fix: MicrotaskDispatcher nutzen:
import { MicrotaskDispatcher } from 'actor-ts';
const cpuActor = system.spawn( Props.create(() => new CpuIntensive()) .withDispatcher(new MicrotaskDispatcher()),);Microtasks umgehen die Event Loop, ~50× schnelleres Scheduling. Caveat: Ein CPU-tighter Actor auf Microtask kann I/O aushungern (Netzwerk-Reads, Timer). Nur nutzen, wenn:
- Der Actor das System nicht mit HTTP-Traffic teilt (Compute-Only-Worker).
- Der Actor selbst nicht
awaitauf I/O macht (rein CPU).
ThroughputDispatcher-Optionen
Abschnitt betitelt „ThroughputDispatcher-Optionen“new ThroughputDispatcher({ throughput: 100, // Nachrichten pro Batch yieldStrategy: 'setImmediate', // oder 'setTimeout' oder 'microtask'});throughput— Nachrichten, die pro Actor vor dem Yield verarbeitet werden. Höher = mehr Durchsatz, schlechtere I/O-Verschränkung. Übliche Werte: 10-1000.yieldStrategy— wie zwischen Batches geyieldet wird.setImmediateist der I/O-freundliche Default;microtaskyieldet kürzer, gibt aber die Event Loop nicht frei.
Für einen Batch-Prozessor, der Broker-Nachrichten verarbeitet:
throughput: 200 ist ein vernünftiger Startpunkt.
Per-Actor-Dispatcher
Abschnitt betitelt „Per-Actor-Dispatcher“const heavy = system.spawn( Props.create(() => new BulkProcessor()) .withDispatcher(new ThroughputDispatcher({ throughput: 500 })),);
const httpHandler = system.spawn( Props.create(() => new HttpHandler()), // → nutzt den ImmediateDispatcher-Default des Systems);Frei mischen. Schwere Actors bekommen ihren eigenen throughput-getuneten Dispatcher; HTTP-Handler bleiben auf dem Default. Das ist das empfohlene Produktionsmuster.
Systemweiter Dispatcher
Abschnitt betitelt „Systemweiter Dispatcher“const system = ActorSystem.create('my-app', { dispatcher: new ThroughputDispatcher({ throughput: 100 }),});Override den Default für jeden Actor, der nichts anderes spezifiziert. Nützlich für Batch-only-Systeme ohne HTTP-Traffic.
Nutze die
Stock-Metrik
actor_message_duration_ms:
P50 ≈ ArbeitszeitP99 - P50 ≈ Dispatcher-Latenz (Queueing)Wenn P99 deutlich höher als P50 mit kleiner Varianz der Arbeitszeit ist, hilft Dispatcher-Tuning.
Das Gauge
actor_mailbox_size unter Last zeigt, ob Actors mitkommen.
Dauerhaft wachsende Tiefe = entweder ein langsamer Handler oder
ein fehlkonfigurierter Dispatcher.
Heuristiken
Abschnitt betitelt „Heuristiken“HTTP-Server + Actors → ImmediateDispatcher (Default)Compute-heavy + kein HTTP → MicrotaskDispatcherBatch-Verarbeitung → ThroughputDispatcher (throughput 100-500)Gemischt: schwerer Actor + HTTP → ImmediateDispatcher-Default + ThroughputDispatcher pro schwerem ActorWohin als nächstes
Abschnitt betitelt „Wohin als nächstes“- Dispatcher — die konzeptuelle Referenz.
- Mailbox-Sizing — der komplementäre Knopf.
- Stock-Metriken — die Per-Actor-Performance-Metriken.