Router
Router ist der lokale Pool-Router — die Routees sind Kinder des
Router-Actors, beim Spawnen des Routers erstellt und von ihm
supervised.
Die API hat vier Factories:
Router.roundRobin(size, routeeProps)Router.random(size, routeeProps)Router.broadcast(size, routeeProps)Router.custom(size, routeeProps, strategy)Jede gibt ein Props<TMsg | Broadcast<TMsg>> zurück — übergib es
an system.spawn oder context.spawn wie alle anderen Props.
Der Typ-Parameter von routeeProps fließt durch, die resultierende
Ref ist also typisiert.
Ein minimales Beispiel
Abschnitt betitelt „Ein minimales Beispiel“import { ActorSystem, Props, Router, Actor, Broadcast } from 'actor-ts';
type Msg = { payload: string };
class Worker extends Actor<Msg> { override onReceive(msg: Msg): void { this.log.info(`processed ${msg.payload}`); }}
const system = ActorSystem.create('demo');
const pool = system.spawn( Router.roundRobin(4, Props.create(() => new Worker())), 'workers',);
// Eine Nachricht pro Routee, zyklisch:pool.tell({ payload: 'a' }); // → routee-1pool.tell({ payload: 'b' }); // → routee-2pool.tell({ payload: 'c' }); // → routee-3pool.tell({ payload: 'd' }); // → routee-4
// Strategie für eine einzelne Nachricht überschreiben — an ALLE Routees senden:pool.tell(new Broadcast({ payload: 'announce' }));Der Pfad des Pools ist actor-ts://demo/user/workers; die Routees
sind actor-ts://demo/user/workers/routee-1 bis routee-4.
Was zur Spawn-Zeit passiert
Abschnitt betitelt „Was zur Spawn-Zeit passiert“Wenn du eine Router-Props mit spawn ausführst, tut die Runtime:
- Erstellt eine
RouterActor-Instanz. Das ist der Actor mit dem Pfad, den du angegeben hast ('workers'im Beispiel). - In
RouterActor.preStartspawnt essizeKinder mitrouteeProps, benanntroutee-1bisroutee-N. - Es
watcht jeden Routee, damit es reagieren kann, wenn einer stoppt.
Der Router ist jetzt bereit. Jedes tell an die Router-Ref führt
die Strategie aus und leitet weiter.
Broadcast — Strategie pro Nachricht überschreiben
Abschnitt betitelt „Broadcast — Strategie pro Nachricht überschreiben“import { Broadcast } from 'actor-ts';
pool.tell({ payload: 'a' }); // normal: ein Routeepool.tell(new Broadcast({ payload: 'announce' })); // jeder RouteeBroadcast<T> wickelt eine Payload. Der Router packt sie aus,
ignoriert die Strategie und sendet die innere Nachricht an jeden
Routee. Nützlich für gelegentliche Fan-Out-Nachrichten
(Cache-Invalidierung, Schema-Update-Notifications), die nicht ins
Routine-Routing-Pattern passen.
Der Router akzeptiert sowohl TMsg als auch Broadcast<TMsg> —
der Typ-Parameter auf den zurückgegebenen Props reflektiert das.
Router.custom — eigene Strategie mitbringen
Abschnitt betitelt „Router.custom — eigene Strategie mitbringen“import { Router, type RoutingStrategy } from 'actor-ts';
// Sende für die ersten 100 Nachrichten an den ERSTEN Routee, dann Round-Robin.const warmupStrategy: RoutingStrategy = (routees, state) => { if (state.messageIndex < 100) return [routees[0]]; return [routees[state.messageIndex % routees.length]];};
const pool = system.spawn( Router.custom(4, Props.create(() => new Worker()), warmupStrategy), 'workers',);Eine RoutingStrategy ist eine Funktion von (routees, state) zu
einem Iterable<ActorRef> — gib eine Ref für Single-Target-Routing
zurück, mehrere für Fan-Out. Leeres Iterable bedeutet “verwirf
diese Nachricht” (still, kein Dead-Letter-Routing — deine
Verantwortung zu loggen, wenn nötig).
Siehe Strategien für den vollen Strategie-Typ und eingebaute Implementierungen.
Routees selbst spawnen
Abschnitt betitelt „Routees selbst spawnen“Router.roundRobin(size, props) ist ein Pool — der Router erstellt
Routees aus props. Wenn du stattdessen an existierende Actors
routen willst (z.B. Shard-Regionen, spezifische benannte Workers),
unterstützt der lokale Router das nicht; du würdest einen eigenen
Router-Actor schreiben.
Für Cluster-Setups hat ClusterRouter einen “finde existierende
Actors per Pfad”-Modus — siehe
Pool vs Group für die Unterscheidung
und Cluster-Router für die
Cluster-API.
Den Pool stoppen
Abschnitt betitelt „Den Pool stoppen“pool.stop() (oder pool.tell(PoisonPill.instance)) stoppt den
Router, der jeden Routee kaskadiert stoppt. Oder stoppe einen
einzelnen Routee, indem du ihn direkt adressierst:
const oneRoutee = (await system.actorSelection( '/user/workers/routee-2').resolveOne()) as ActorRef<Msg>;
oneRoutee.stop();Wenn ein Routee stoppt, beobachtet der Router ihn und… tut standardmäßig nichts im aktuellen Router des Frameworks. Die Pool-Größe schrumpft effektiv, bis sie restarted. Für selbstheilende Pools wickle die Routee-Props in eine Supervisor-Strategie, die bei Stop restartet (oder wickle den ganzen Pool in einen BackoffSupervisor).
Wie es weitergeht
Abschnitt betitelt „Wie es weitergeht“- Strategien — Round-Robin, Random, Broadcast, Custom; plus das cluster-only Consistent-Hashing.
- Pool vs Group — wenn du bestehende Routees statt pool-gespawnte willst.
- Cluster-Router — das mitgliedschafts-getriebene Cluster-Äquivalent.
- Props — die Routee-Props-Form;
withSupervisorStrategy,withDispatcher, etc., gelten alle individuell für Routees.
Die Router- und
Broadcast-API-Referenzen decken die
volle Schnittstelle ab.