Zum Inhalt springen
Deutsch

Joining und Seeds

Ein Node tritt einem Cluster bei, indem er einen Seed-Node kontaktiert. Der Seed sendet per Gossip seine aktuelle Mitgliedschaftssicht zurück; der Joiner wird als joining hinzugefügt, propagiert per Gossip, und sobald der Leader ihn sieht (plus Konvergenz), wechselt er zu up.

ClusterSeed-Nodesjoining-NodeClusterSeed-Nodesjoining-Nodejoining → weakly-up? → upüber ein paar Gossip-RundenJoin-AnkündigungGossip JoinGossip — aktuelle Sicht

Diese Seite behandelt die Mechanik dieses Handshakes plus die Seed-Discovery-Ebene darüber.

import { ActorSystem, Cluster } from 'actor-ts';
const system = ActorSystem.create('my-app');
const cluster = await Cluster.join(system, {
host: '10.0.0.5',
port: 2552,
seeds: ['10.0.0.5:2552', '10.0.0.6:2552', '10.0.0.7:2552'],
});

Drei Seeds. Der Joiner kontaktiert sie der Reihe nach, bis einer antwortet. Sobald irgendein Seed akzeptiert, propagiert der Gossip des Clusters das neue Mitglied; Konvergenz zu up geschieht innerhalb weniger Sekunden in einem gesunden Netzwerk.

Die Seed-Liste ist nur ein Bootstrap-Hinweis — sobald der Node beigetreten ist, lernt er alle anderen Peers per Gossip kennen. Seeds müssen nach dem Join nicht mehr besonders sein.

interface ClusterSettings {
host: string; // Adresse dieses Nodes
port: number; // TCP-Port dieses Nodes
seeds?: string[]; // Peer-Adressen fürs Bootstrap
roles?: string[]; // Rollen-Tags
failureDetector?: Partial<...>;
transport?: Transport;
gossipIntervalMs?: number;
seedRetryIntervalMs?: number; // Retry-Intervall, falls kein Seed antwortet
// ...
}

Die seed-bezogenen Knöpfe:

EinstellungStandardWas
seeds[]Liste von "host:port"-Strings. Leer = “ich bin der erste”.
seedRetryIntervalMs3000Falls kein Seed antwortet, wiederhole die Liste so oft, bis einer antwortet.
const cluster = await Cluster.join(system, {
host: '0.0.0.0',
port: 2552,
seeds: [],
});

Eine leere seeds-Liste (oder eine, die komplett unerreichbar ist) bedeutet, dass dieser Node den Cluster selbst bootstrapt. Er befördert sich automatisch zum Leader; künftige Joiner kontaktieren ihn.

Das macht die Single-Node-Entwicklung trivial — keine zu pflegende Seed-Liste. Füge später einen zweiten Node hinzu, indem du ihm die Adresse des ersten als Seed gibst.

Für Produktion gib jedem Node dieselbe Seed-Liste (3-5 Adressen, idealerweise bekannte Nodes, von denen du keinen Wechsel erwartest). Reihenfolge spielt keine Rolle; der Joiner probiert jeden.

n3n2n1n3n2n1Alle drei frisch, alle in Seed-Liste [n1, n2, n3]n1 ist jetzt LeaderSeed kontaktierennoch kein Cluster / TimeoutSeed kontaktierennoch kein Cluster / Timeoutsich selbst kontaktieren → erkennt sich, bootstrapt automatischSeed kontaktierentritt über mich beiSeed kontaktierentritt über mich bei

Wenn ein Cluster aus dem Kaltstart hochfährt (alle Nodes kommen gleichzeitig hoch), liefern sich die Joiner ein Rennen. Die Seed-Retry-Logik des Frameworks regelt das:

  • Jeder Node wiederholt seine Seed-Liste in seedRetryIntervalMs.
  • Irgendwann kontaktiert ein Node zuerst sich selbst; das ist der Bootstrap.
  • Der Rest konvergiert auf den jetzt existierenden Cluster.

Der Standard-Retry von 3 Sekunden macht die Kaltstart-Konvergenz in wenigen Runden zuverlässig.

Seed-Discovery — jenseits einer statischen Liste

Abschnitt betitelt „Seed-Discovery — jenseits einer statischen Liste“

Eine hartcodierte Seed-Liste reicht für Tests und kleine Cluster. Für Produktion, in der Nodes dynamische IPs haben (Container, K8s Pods), nutze einen Seed-Provider:

ProviderWann
ConfigStatische Liste (der Fall oben).
DNSLöst _actor-ts._tcp.example.com SRV-Records auf.
Kubernetes APIListet Pods, die einem Label-Selector entsprechen.
AggregateFällt durch mehrere Provider durch (z. B. K8s, dann DNS).
import { KubernetesApiSeedProvider } from 'actor-ts/discovery';
const seedProvider = new KubernetesApiSeedProvider({
namespace: 'default',
labelSelector: 'app=actor-ts',
containerPort: 2552,
});
const seeds = await seedProvider.discover();
const cluster = await Cluster.join(system, {
host: process.env.POD_IP!,
port: 2552,
seeds,
});

Der Provider liefert eine Momentaufnahme von Seed-Adressen; das Framework nutzt sie, um den Join zu bootstrappen. Siehe Discovery-Überblick für das Seed-Provider-Modell.

import { SelfUp, MemberUp } from 'actor-ts';
cluster.subscribe(SelfUp, (evt) => {
console.log(`dieser Node ist jetzt Up`);
});
cluster.subscribe(MemberUp, (evt) => {
console.log(`Peer ${evt.member.address} hat Up erreicht`);
});

Zwei zentrale Events:

  • SelfUp feuert einmal, wenn dieser Node auf up übergeht. Nützliches Gate, um Arbeit zu starten, die Cluster-Mitgliedschaft erfordert.
  • MemberUp feuert jedes Mal, wenn irgendein Mitglied up erreicht.

Für Startup-Logik, die andere Mitglieder braucht (“warte, bis mindestens 3 Nodes up sind, bevor Traffic bedient wird”), zähle MemberUps nach SelfUp.