02. Topology discovery¶
This category covers the topology registration entry points IZLinkFrameworkOptions provides,
and the entry points that query RouteMesh·ClientServer·Fanout operational status. The exact
signatures are owned by the
RouteMesh·MeshNode exact interface
and the
Topology monitoring exact interface
(both Korean-only). Every registration entry is a host configuration-time call.
AddRouteMesh (configuration time)¶
Registers one physical MeshNode. It is the starting point for RouteMesh-based topology.
services.AddZLinkFramework(options =>
{
var play = options.AddRouteMesh("play")
.Listen(5501)
.SetRoutingIdPrefix("play")
.SetPlacementWeight(100);
});
Options. Commonly used modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.Listen(endpoint) / .Listen(port) |
does not bind if omitted | this MeshNode's inbound endpoint. It is fine to skip listening if Server membership is zero |
.SetBindHost(string) / .SetAdvertiseHost(string) |
follows ConfigureNetwork()'s root default |
the bind·advertise host that applies only to this MeshNode. It takes priority over the root default |
.SetRoutingId(RoutingId) / .SetRoutingIdPrefix(string) |
Framework-issued | a fixed RID, or the prefix for an issued RID |
.SetPlacementWeight(int) |
100 (range 0..10000) |
relative weight for placing new Actors·Spots on this node |
.SetActorLimit(int) / .SetSpotLimit(int) |
0 (unlimited) |
the Actor·Spot cap this node accepts |
.SetActivationConcurrency(int) |
128 | the cap on activation admissions in progress concurrently — not object population |
.SetDefaultRequestTimeout(TimeSpan) |
this MeshNode's default request timeout | the value RequestToNode/RequestToChannel (messaging-execution category) etc. use when .Timeout(...) is omitted |
.SetInstanceSpotIdleTimeout(TimeSpan) |
TimeSpan.Zero (no reclamation) |
idle reclamation time for Instance Spots. The valid range is TimeSpan.Zero or greater; a negative value is a startup error |
.Objects() |
— | enters Object role (Client/Server) registration. See the Object role registration entry |
.Channel(channelName) |
— | enters this MeshNode's RouteMesh Channel role registration. See the RouteMesh Channel registration entry |
.AddRouteSendHandler<THandler, TMessage>(packetName?) |
packet name is derived from the message type | registers a Node direct one-way handler (IZLinkRouteSendHandler<TMessage>) — the target SendToNode (messaging-execution category) calls |
.AddRouteRequestHandler<THandler, TRequest, TReply>(packetName?) |
packet name is derived from the message type | registers a Node direct request handler (IZLinkRouteRequestHandler<TRequest, TReply>) — the target RequestToNode calls |
Completion. Registers synchronously with no return value. An invalid combination
(duplicate MeshName, a missing listener setting, etc.) surfaces as
ZLinkConfigurationException during host startup validation. The same packet name can be
registered separately in the RouteMesh Channel handler family and the Node direct handler
family — only a duplicate key within the same family is a startup error.
When to use it. Every host that uses RouteMesh registers at least one MeshNode. A node that only uses manual peers and needs no distributed discovery can start without a Location Store.
Object role registration (configuration time)¶
Registers how a MeshNode handles Actors·Spots — whether it is Client-only or hosts them as a Server.
play.Objects().Server()
.AddEntrySpot<GameEntrySpot>()
.AddSpotFactory<RoomSpot>(
"room",
factory => factory
.ExecutionMode(ZLinkUserSpotExecutionMode.SpotWide)
.PreserveStateWith<RoomRelocationAdapter>())
.AddActorFactory<PlayerActor, PlayerActorFactory>(
"player",
factory => factory
.PreserveStateWith<PlayerRelocationAdapter>());
Options. Commonly used modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.AddEntrySpot<TEntrySpot>() |
none | registers the Entry Spot type dedicated to external entry |
.AddSpotFactory<TSpot>(type, configure) |
none | registers a stable User Spot type. configure takes StableTypeLimit(int)·ExecutionMode·RelocationReadiness, plus exactly one of PreserveStateWith/RecreateOnRelocation/DisableRelocation |
.AddInstanceSpotFactory<TSpot>(type, configure) |
none | registers a cold-activation Instance Spot type. configure takes StableTypeLimit(int), plus exactly one of PreserveStateWith/RecreateOnRelocation/DisableRelocation |
.AddActorFactory<TActor, TFactory>(type, configure) |
none | registers a stable Actor type. configure takes exactly one of PreserveStateWith/RecreateOnRelocation/DisableRelocation (the Actor factory has no StableTypeLimit) |
Completion. Registers synchronously with no return value. An adapter/factory mismatch for a
stable type using relocation, or a duplicate type, surfaces as ZLinkConfigurationException
during host startup validation.
When to use it. Register the corresponding role when this node actually hosts (Server) an Actor·Spot, or only references an Actor·Spot hosted by another node as a messaging target (Client). See the actor-relocation category for the relocation-policy selection criteria.
RouteMesh Channel registration (configuration time)¶
Registers logical ChannelName membership within the same MeshNode.
play.Channel("play.api").Server()
.SetWeight(100)
.AddRequestHandler<GetPlayerHandler, GetPlayer, Player>()
.AddSendHandler<PlayerOnlineHandler, PlayerOnline>();
Options. After Channel(channelName), call exactly one of .Client() or .Server().
.Client() only creates the send path and has no modifiers. .Server()'s commonly used
modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.SetWeight(int) |
100 (range 0..10000) |
relative weight for this Server being selected as a request/send target. 0 excludes it from selection |
.AddHandlerGroup(groupName) |
none | specifies the handler group assembly scanning looks for |
.AddSendHandler<THandler, TMessage>(packetName?) |
packet name is derived from the message type | registers a one-way handler |
.AddRequestHandler<THandler, TRequest, TReply>(packetName?) |
packet name is derived from the message type | registers a request/reply handler |
Completion. Registers synchronously with no return value. A duplicate handler key within
the same owner namespace surfaces as ZLinkConfigurationException during host startup
validation.
When to use it. Use .Server() to register the handler that SendToChannel/RequestToChannel
(messaging-execution category) will reach. If this MeshNode only calls another node's Server and
places no handler of its own, register .Client() alone. For messaging between separate
processes, use ClientServer Channel registration instead.
AddClientServerChannel (configuration time)¶
Registers an independent ClientServer Channel unrelated to RouteMesh.
options.AddClientServerChannel("payments.api").Server()
.Listen(6001)
.SetWeight(100)
.AddRequestHandler<ChargeHandler, Charge, ChargeResult>();
options.AddClientServerChannel("payments.api").Client()
.Connect("payments-1:6001");
Options. Commonly used modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.Server().Listen(port) |
0 if the port is omitted (automatic discovery auto-binds whether the port is omitted or 0) |
this Server's inbound port. There is no Listen(string endpoint) overload — it takes only an int port, not an endpoint string |
.Server().SetBindHost(string) / .SetAdvertiseHost(string) |
follows the root ConfigureNetwork() default |
the bind·advertise host that applies only to this Server |
.Server().SetWeight(int) / .AddSendHandler/.AddRequestHandler |
same as RouteMesh Channel Server | weight and handler registration |
.Client().Connect(endpoint) |
manual | manually connects to a specific Server. Omitting it lets automatic discovery find the target |
Completion. Registers synchronously with no return value. A Client·Server using automatic
discovery without a registered Location Store surfaces as ZLinkConfigurationException during
host startup validation.
When to use it. Use it for request/reply or one-way messaging between independent services that are not RouteMesh members. Between nodes in the same RouteMesh, use RouteMesh Channel registration instead.
AddFanoutChannel (configuration time)¶
Registers a classic fanout-only channel — the target Publish (messaging-execution category)
publishes to.
options.AddFanoutChannel("lobby.events")
.EnablePublisher(7001)
.AddHandler<PlayerJoinedHandler, PlayerJoined>();
// automatic subscriber — discovers every publisher of the same ChannelName through the Location Store
options.AddFanoutChannel("lobby.events")
.EnableSubscriber();
// manual subscriber — uses only the specified endpoint. Combining it with EnableSubscriber() fails startup
options.AddFanoutChannel("lobby.events")
.Connect("lobby-1:7001");
Options. Commonly used modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.EnablePublisher(endpoint) / .EnablePublisher(port) |
none | registers this channel's publisher role and inbound endpoint |
.SetBindHost(string) / .SetAdvertiseHost(string) / .SetRoutingId(RoutingId) / .SetRoutingIdPrefix(string) |
the root default, or Framework-issued | the bind·advertise host and RID that apply only to the publisher |
.EnableSubscriber() |
— | an automatic subscriber. Discovers every valid publisher of the same ChannelName through the Location Store |
.Connect(endpoint) |
— | a manual subscriber. Uses only the specified endpoint. Registering it alongside .EnableSubscriber() on the same channel fails host startup |
.AddHandler<THandler, TEvent>(packetName?) |
packet name is derived from the event type | registers a typed event handler |
Completion. Registers synchronously with no return value. Configuring an automatic
subscriber and a manual subscriber together on the same fanout channel surfaces as
ZLinkConfigurationException.
When to use it. Use it to create a new observation/notification channel where the publisher does not need to know its subscribers. For messaging that needs a reply, use RouteMesh Channel or ClientServer Channel registration instead.
AddStreamNode (configuration time)¶
Registers a listener that accepts external STREAM connections.
options.AddStreamNode("public-gateway")
.Bind(9001)
.EnableActorDispatch()
.AddSession<GameSession>();
Options. Commonly used modifiers:
| Modifier | Default | Meaning |
|---|---|---|
.Bind(endpoint) / .Bind(port) |
0 if the port is omitted (automatic discovery auto-binds whether the port is omitted or 0) |
this STREAM listener's inbound port. A Bind(string endpoint) overload also exists |
.SetBindHost(string) / .SetAdvertiseHost(string) |
follows the root ConfigureNetwork() default |
the bind·advertise host that applies only to this listener |
.MaxMessageSize(bytes) |
64 KiB |
the complete STREAM message cap from client to server; 0 means no Framework cap |
.ConfigureSocket() |
socket defaults | tuning of this listener socket's remaining HWM·buffer·timeout options (IZLinkStreamSocketConfig) |
.EnableActorDispatch() |
disabled | dispatches incoming messages to the Actor bound to the Session |
.SetTlsServer(certPath, keyPath, requireClientCertificate?) |
no TLS | the TLS server certificate/key and whether mutual authentication is required |
.AddSession<TSession>() |
none | registers the Session type to create per connection |
Completion. Registers synchronously with no return value. A TLS configuration error
surfaces as ZLinkConfigurationException during host startup validation.
When to use it. Use it to open a gateway that external clients connect to directly with the STREAM protocol. See the stream-session category for the exact Session·Actor binding rules.
Manual peer connections (configuration time·runtime)¶
Connects manually to a specific endpoint without automatic discovery. Call it through
MeshNodeBuilder.PeerConnections.
play.PeerConnections.Connect(RoutingId.From("play-node-2"), "play-node-2:5501");
IReadOnlyList<ZLinkMeshPeerConnection> connections = play.PeerConnections.ListConnections();
Options. The following modifiers attach to this call.
| Modifier | Default | Meaning |
|---|---|---|
.Connect(endpoint) |
no expected RID | the admission handshake determines the remote identity |
.Connect(expectedRoutingId, endpoint) |
— | does not admit the connection if the handshake identity differs |
.Disconnect(endpoint) |
— | releases a registered connection |
.ListConnections() |
— | lists the currently registered connections |
Completion. Registers/releases synchronously with no return value. If both MeshNodes are
Object Clients and neither has RouteMesh Channel Server membership, this connection intent can
remain in the list without becoming a ready peer, and it is excluded from the ready-peer count
and the liveness target set. If either side has Channel Server membership that includes weight
0, ordinary peer admission·liveness rules apply.
When to use it. Use it to configure RouteMesh with a fixed peer list instead of automatic discovery (Location Store).
UseFilter<TFilter> (configuration time)¶
Inserts common logic (authentication, logging, etc.) in front of every handler dispatch.
options.UseFilter<AuthenticationFilter>();
public sealed class AuthenticationFilter : IZLinkHandlerFilter
{
public async ValueTask InvokeAsync(
IZLinkHandlerFilterContext context,
ZLinkHandlerFilterNext next,
CancellationToken cancellationToken)
{
if (!IsAuthenticated(context))
{
return; // not calling next() ends the request as Rejected
}
await next();
}
}
Options. The following modifier attaches to this call.
| Modifier | Default | Meaning |
|---|---|---|
.UseFilter<TFilter>() |
none (runs in registration order) | adds an IZLinkHandlerFilter implementation to the dispatch chain |
Completion. Registers synchronously with no return value. Calling next() runs the
remaining filters and the handler. Not calling next() on a request ends it with a Rejected
reply; calling next() twice fails with InvalidOperation and does not run the handler again.
context.DispatchKind distinguishes
NodeDirectSend/NodeDirectRequest/ChannelSend/ChannelRequest/ClassicFanout —
ChannelSend/ChannelRequest cover both RouteMesh and ClientServer.
When to use it. Use it when common preprocessing or validation needs to repeat for every handler. A filter never builds the business reply directly — it only expresses rejection, and the handler handles the rest.
Other host-wide options (configuration time)¶
Simple property-bag configuration IZLinkFrameworkOptions provides. Each setting is called
independently.
services.AddZLinkFramework(options =>
{
options.AddHandlersFromAssemblyOf<GameEntrySpot>(); // finds and registers attribute-marked handlers from an assembly
options.ConfigureNetwork().BindHost = "0.0.0.0";
IZLinkDispatchOptions dispatch = options.ConfigureDispatch();
dispatch.CoreHwmProfile = ZLinkCoreHwmProfile.LowLatency;
dispatch.ApplicationJobQueueProfile = ZLinkApplicationJobQueueProfile.LowLatency;
options.ConfigureMetadata()
.AllowSessionToActor("trace-id")
.AllowActorToSession("server-region"); // the metadata-key allowlist to forward
options.ConfigureStreamCompression().UseLz4();
});
Options. Commonly used items:
| Modifier | Default | Meaning |
|---|---|---|
.AddHandlersFromAssemblyOf<TMarker>() / .AddHandlersFromAssembly(assembly) / .AddHandlersFromAssemblyOf(Type markerType) |
implicit auto-registration is active | specifies the target assembly for attribute-based handler scanning. All three overloads perform the same scan |
.DisableImplicitHandlerAutoRegistration() |
active (auto scan) | turns off attribute-based auto-registration and uses only explicit builder registration |
.ConfigureMetadata().AllowSessionToActor(key) / .AllowActorToSession(key) |
a key not specified is not forwarded | adds a metadata key to the per-direction allowlist to forward over STREAM session↔Actor relay |
.ConfigureNetwork() |
BindHost is all interfaces |
the default bind·advertise host used unless an individual Listen call overrides it. Returns IZLinkNetworkOptions (a property bag) |
.ConfigureDispatch() |
Framework dispatch/diagnostics defaults; both profiles are Balanced; nullable manual values are null |
configures unmatched-packet handling/diagnostics and the Core HWM memory/budget/profile inputs plus the host-wide Application Job Queue profile or exact manual permit limit on IZLinkDispatchOptions |
.ConfigureStreamCompression() |
no compression | the default STREAM compression codec (UseDefault()/UseLz4()/Use(codec)/Disable()) |
.ConfigureRouterSocket() / .ConfigureSpotPublisher() (on MeshNodeBuilder) |
socket defaults | individual HWM·buffer·timeout tuning for the MeshNode ROUTER socket and the Spot publisher |
Completion. .AddHandlersFromAssemblyOf(...)/.DisableImplicitHandlerAutoRegistration()
run synchronously with no return value.
.ConfigureMetadata()/.ConfigureNetwork()/.ConfigureDispatch()/.ConfigureStreamCompression()
synchronously return the corresponding builder or options object, on which properties are set or
further modifiers are called. A value outside range surfaces as ZLinkConfigurationException
during host startup validation.
Core owns its byte-budget calculation. The Framework does not divide that budget by connection
count. The Application Job Queue is a separate job-count limit; MaxQueuedApplicationJobs accepts
1..2,147,483,647, while null selects the Auto profile. See the
Core/Framework API contract.
When to use it. Use it to adjust host-wide settings that end in a single simple value and do not belong to one of the dedicated entries above (host lifecycle·topology registration· diagnostics). For diagnostics-related settings, use the observability-diagnostics category.
Runtime weight read·write¶
Changes the placement weight or channel weight without redeploying.
IZLinkMeshPlacementRuntimeOptions placement = routeMeshRuntimeOptions.Mesh("play");
placement.PlacementWeight = 50; // lowers this node's share of new Actor·Spot placement
IZLinkMeshChannelRuntimeOptions channel = routeMeshRuntimeOptions.Channel("play.api");
channel.Weight = 0; // excludes this Channel Server from selection
Options. This entry point has two independent properties.
| Property | Default | Meaning |
|---|---|---|
Mesh(meshName).PlacementWeight |
the value set at registration | node-scoped Actor·Spot placement weight |
Channel(channelName).Weight |
the value set at registration | ChannelName-scoped Server selection weight |
Completion. A synchronous get/set. It applies immediately, with no separate completion signal.
When to use it. Use it to adjust placement or traffic share while operating. Transport
options including MaxMessageSize cannot change through this path — they are set only before
startup.
Topology status read·observe¶
Checks the operational status of RouteMesh·ClientServer·Fanout individually. The three
runtimes share the same shape — one GetStatus read, or streamed observation via
ObserveAsync.
ZLinkRouteMeshStatus status = routeMeshRuntime.GetStatus("play");
bool canPlaceNewObjects = status.IsReady && status.Placement.IsAvailable;
await foreach (var observed in routeMeshRuntime.ObserveAsync("play", ct))
{
// check observed.Status.Channels, observed.Status.Peers
}
Options. The three runtimes correspond as follows.
| Runtime | Target | Returned status |
|---|---|---|
IZLinkRouteMeshRuntime |
MeshName | ZLinkRouteMeshStatus (includes Channels, Peers, Placement) |
IZLinkClientServerRuntime |
ChannelName | ZLinkClientServerStatus (includes Targets) |
IZLinkFanoutRuntime |
ChannelName | ZLinkFanoutStatus (includes Publishers) |
Completion. GetStatus is a synchronous call that returns a value immediately.
ObserveAsync streams ZLinkObservedStatus<TStatus> in the same shape as the host-lifecycle
category's ObserveAsync, and the Loss field tells whether an observation was lost.
Querying a manual ChannelName through IZLinkFanoutRuntime completes with
ZLinkConfigurationException.
When to use it. Use it to judge the availability of a specific MeshName·ChannelName or
narrow the scope of a failure. If the entire host's status is needed, use the host-lifecycle
category's Status/ObserveAsync.
The full basis is RouteMesh·MeshNode exact interface and Topology monitoring exact interface (both Korean-only).