Cluster client
Este conteúdo não está disponível em sua língua ainda.
ClusterClient lets a process outside the cluster talk to
actors inside. The external process isn’t a cluster member —
no gossip, no membership state — but it can tell and ask
cluster-internal actors via known receptionist contacts.
import { ClusterClient, ClusterClientOptions } from 'actor-ts';
const clusterClientOptions = ClusterClientOptions.create().withContactPoints(['actor-ts://my-app@10.0.0.5:2552/system/cluster/receptionist']);const client = new ClusterClient( clusterClientOptions,);
// Send to a well-known cluster actor:client.send('/user/api/orders', { kind: 'place', ... });
// Or ask and await the reply:const orders = await client.ask('/user/api/orders', { kind: 'list-orders' });When to use it
Section titled “When to use it”Three legitimate use cases:
- External services that don’t belong in the actor cluster but need to talk to it — a Python ML service that posts to a Kafka actor inside the cluster.
- Mobile / desktop clients via a bridge — a server-side bridge holds a ClusterClient + exposes a REST/WS API externally.
- Cross-cluster federation — two clusters where one needs to talk to specific actors in the other without merging.
For typical setups, everything is part of the cluster — ClusterClient is the escape hatch for cases where it can’t be.
How it works
Section titled “How it works”The client:
- Connects to one or more contact points (cluster nodes
running a
ClusterClientReceptionist). - Sends each message in an envelope addressed to a target
actor path —
sendfor fire-and-forget,askfor request/reply. - The receptionist on the contact node resolves that path in its local actor tree and delivers the message.
- Handles failover when a contact point becomes unreachable — reconnects to another.
Configuration
Section titled “Configuration”type ClusterClientOptionsType = { contactPoints: ReadonlyArray<string>; // at least one node: host:port or <system>@host:port systemName?: string; // synthetic system name in the client's hello clientIdentity?: { host: string; port: number }; // identity used for reply routing askTimeoutMs?: number; // default ask timeout (ms; default 5000) tls?: TlsTransportOptionsType; // must match the cluster's logger?: Logger; // default: ConsoleLogger at WARN};contactPoints is the list of cluster nodes to dial — each a
host:port or <system>@host:port string. The client tries
them in order; on failure, it falls back to the next.
For stable contact addresses, the cluster side typically
runs a ClusterClientReceptionist on a fixed set of nodes,
whose addresses become the client’s contact points.
Server side — ClusterClientReceptionist
Section titled “Server side — ClusterClientReceptionist”import { ClusterClientReceptionistId } from 'actor-ts';
// The receptionist is a per-system extension — start it on each// node that should accept outside-in client connections:system.extension(ClusterClientReceptionistId).start(cluster);There’s no service registry to populate. The receptionist
resolves each envelope’s target path against the local actor
tree and delivers the message — any actor already running under
/user is reachable by the path the client sends to (e.g.
client.send('/user/api/orders', ...)).
Its one option, askTimeoutMs, bounds the wait when a client
envelope carries an ask; set it via
ClusterClientReceptionistOptions.
What a failure tells the client
Section titled “What a failure tells the client”A ClusterClient is not a peer. It speaks the same wire as a
cluster member, which is exactly why the distinction is worth
stating: it never joined the membership ring, carries no gossip
or heartbeat duty, and a contact point is by design reachable
from outside whatever boundary protects the cluster’s own links.
Completing a hello does not entitle the party on the other end
to the cluster’s internals.
So when an ask fails inside the cluster, the receptionist does not forward the rejection text. That text is written by whatever actor happened to fail — the same class of string as an HTTP 500’s, carrying file paths, SQL fragments or a stack. The client gets a fixed sentence and a correlation id instead:
ask failed on the cluster node (correlationId=6f1c…-…) — the reason is in that node's logThe full text is logged on the node that ran the ask, under the
same id, at warn. An outside caller quotes the id; an operator
greps for it. Nothing carries the reason over the wire.
An unknown path is reported differently, because there the client only learns about its own request:
path not found: user/api/ordersNote what is not in it — the node’s own address. A contact point is often behind a load balancer or NAT, so the address it binds on is not the one the client dialled, and there is no reason to hand it out.
If a client genuinely needs to act on a specific failure, model it as a reply the actor authors rather than as a throw:
class OrderActor extends Actor<OrderCommand> { override onReceive(message: OrderCommand): void { if (!this.stock.has(message.sku)) { // A deliberate, authored answer — it reaches the client verbatim. this.sender.forEach((s) => s.tell({ kind: 'rejected', reason: 'out-of-stock' })); return; } // … }}A successful reply is passed through untouched; only failures are redacted. That keeps the domain contract explicit instead of turning whatever an exception happened to say into an API.
Comparison with cluster-aware ActorRef
Section titled “Comparison with cluster-aware ActorRef”// Inside the cluster — actors talk directly:const ref = await system.actorSelection('actor-ts://my-app@host:2552/user/api').resolveOne();ref.tell(...);
// Outside the cluster — ClusterClient:const clusterClientOptions = ClusterClientOptions.create().withContactPoints([...]);const client = new ClusterClient(clusterClientOptions);client.send('/user/api', ...);Differences:
- Inside: requires cluster membership. Refs propagate via gossip; you can hold long-lived refs.
- ClusterClient: no membership; refs are resolved per message via the receptionist.
When NOT to use it
Section titled “When NOT to use it”Where to next
Section titled “Where to next”- Refs across nodes — in-cluster ref semantics for comparison.
- Receptionist — the in-cluster service registry (a distinct component with a similar name).
- HTTP overview — the more common external-facing alternative.
