Zum Inhalt springen
Deutsch

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.

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-1
pool.tell({ payload: 'b' }); // → routee-2
pool.tell({ payload: 'c' }); // → routee-3
pool.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.

Wenn du eine Router-Props mit spawn ausführst, tut die Runtime:

  1. Erstellt eine RouterActor-Instanz. Das ist der Actor mit dem Pfad, den du angegeben hast ('workers' im Beispiel).
  2. In RouterActor.preStart spawnt es size Kinder mit routeeProps, benannt routee-1 bis routee-N.
  3. 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 Routee
pool.tell(new Broadcast({ payload: 'announce' })); // jeder Routee

Broadcast<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.

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.

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.

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).

  • 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.