Fluens.Web.Discovery
0.7.4
dotnet add package Fluens.Web.Discovery --version 0.7.4
NuGet\Install-Package Fluens.Web.Discovery -Version 0.7.4
<PackageReference Include="Fluens.Web.Discovery" Version="0.7.4" />
<PackageVersion Include="Fluens.Web.Discovery" Version="0.7.4" />
<PackageReference Include="Fluens.Web.Discovery" />
paket add Fluens.Web.Discovery --version 0.7.4
#r "nuget: Fluens.Web.Discovery, 0.7.4"
#:package Fluens.Web.Discovery@0.7.4
#addin nuget:?package=Fluens.Web.Discovery&version=0.7.4
#tool nuget:?package=Fluens.Web.Discovery&version=0.7.4
Fluens.Web.Discovery
Consul self-registration and a central, blocking-query-refreshed service catalog for Fluens web applications that start on dynamic ports.
The library owns both halves of discovery per ADR-0004:
- Registration (write) — the application registers itself into Consul after Kestrel binds, using its real post-start address and port, and deregisters on shutdown. A self-healing reconciler re-applies the held registration on an interval (idempotent upsert), surviving Consul auto-deregistration after transient unhealth.
- Resolution (read) — a single long-polling worker keeps an in-memory
IServiceCatalog(the contract lives inFluens.Web) converged with Consul; the mesh and HTTP clients read the catalog snapshot instead of each issuing their own Consul query. The worker watches both registration topology and health-state transitions, so a peer recovering fromcriticalis picked up within seconds without a topology change.
The library owns the fluens- meta/tag namespace (DiscoveryMeta: fluens-version, fluens-cluster). The
hyphen separator is DNS-safe and legal in both Consul meta keys (which reject .) and tags.
Install
dotnet add package Fluens.Web.Discovery
Usage
builder.AddFluens()
.AddWeb()
.AddDiscovery(); // reads "Fluens:Web:Discovery"; no-op when Enabled == false
Configuration (appsettings.json):
{
"Fluens": {
"Web": {
"Discovery": {
"Enabled": true,
"Url": "http://localhost:8500",
"AdvertisedHost": null,
"ClusterId": "cluster-1",
"Tags": [ "grpc" ],
"Meta": { "region": "eu-west" },
"ReregisterIntervalSeconds": 30,
"HealthCheckPath": "/health",
"HealthCheckIntervalSeconds": 10,
"DeregisterCriticalServiceAfterSeconds": 60,
"CatalogWaitSeconds": 300
}
}
}
}
Options
| Option | Default | Meaning |
|---|---|---|
Enabled |
true |
Master flag; false makes AddDiscovery() a no-op. |
Url |
— | Consul agent base URL. Required when enabled. |
AdvertisedHost |
null |
Overrides the bound host (wildcard/Docker/NAT/cross-host). When unset the bound address is trusted. |
ClusterId |
null |
When set, published as the cluster service meta. |
Tags |
[] |
Extra tags merged with auto tags. |
Meta |
{} |
Extra meta merged with auto version/cluster (user wins on collision). |
ReregisterIntervalSeconds |
30 |
Interval of the idempotent-upsert reconciler. |
HealthCheckPath |
/health |
HTTP check path used when a health source is configured. |
HealthCheckIntervalSeconds |
10 |
Consul check poll interval. |
DeregisterCriticalServiceAfterSeconds |
60 |
Consul DeregisterCriticalServiceAfter auto-cleanup. |
CatalogWaitSeconds |
300 |
Max wait time (seconds) of each catalog blocking query (>0). |
Address normalization
The advertised address is computed once from IServerAddressesFeature and held. Wildcard/loopback bind
tokens ([::], [::1], *, 0.0.0.0) are normalized to localhost. Set AdvertisedHost to advertise a
routable host instead of the bound one.
Health check
A health check is registered only when a source is configured, with precedence:
- an explicit health path passed to the registration call,
- else
IHealthEndpointInforesolved from DI (e.g. provided byFluens.Web.Healthchecks), - else none.
When present it is a Consul HTTP check with DeregisterCriticalServiceAfter as the native auto-cleanup.
Resolution
AddDiscovery() registers a singleton IServiceCatalog (ServiceCatalog) and a CatalogRefreshWorker.
The worker runs two concurrent Consul blocking queries — Catalog.Services (registration topology) and
Health.State(HealthStatus.Any) (health transitions), each on its own WaitIndex and bounded by
CatalogWaitSeconds — and rebuilds the snapshot as soon as either advances. It then reads the current
topology plus per-service Health.Service(passingOnly), assembles an immutable snapshot, and swaps it into
the catalog. Reacting to the health index means a peer's critical→passing transition wakes a refresh within
seconds even when no service was added or removed. Consumers read the catalog directly:
// Resolve a single healthy endpoint (round-robins across instances).
ResolvedServiceEndpoint? endpoint = catalog.Resolve("orders.eu");
// Enumerate services by tag (and optional meta) — e.g. discover mesh peers.
IReadOnlyList<DiscoveredService> peers = catalog.List(
"fluens-mesh",
new Dictionary<string, string> { ["fluens-cluster"] = "cluster-1" });
// React to topology changes.
IChangeToken token = catalog.GetChangeToken();
Sibling libraries that require the catalog (for example the messaging mesh) call the idempotent
TryAddDiscovery() — it wires discovery exactly once, so the catalog worker is never registered twice.
ADR-0007 — endpoint resolution only. The catalog answers where to deliver now; it is not the source of mesh peer subscriptions. The messaging mesh stores subscriptions in a durable Consul-KV store (
fluens/<ClusterId>/subscriptions/<app>) read over the sameIConsulClientthis library registers, so a peer going health-critical drops from the catalog without dropping from the mesh's owed-app set. Because that KV store is not re-derivable from a fresh Consul, run Consul persistently (-server -bootstrap-expect=1 -data-dir+ a named volume), never-dev, wherever mesh durability is load-bearing.
Contributor model
A sibling library can advertise additive tags/meta into this application's registration without Discovery
depending on it, by registering an IServiceRegistrationContributor:
internal sealed class MeshContributor : IServiceRegistrationContributor
{
public ServiceRegistrationContribution Contribute() =>
new(["fluens-mesh"], new Dictionary<string, string>(StringComparer.Ordinal));
}
services.AddSingleton<IServiceRegistrationContributor, MeshContributor>();
Contributions merge into the descriptor with auto → contributor → user precedence; tags are unioned with
ordinal de-duplication. The mesh uses this to add the fluens-mesh tag so peers can be discovered by tag.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Consul (>= 1.8.0)
- Fluens.Web (>= 0.7.4)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Fluens.Web.Discovery:
| Package | Downloads |
|---|---|
|
Fluens.Web.Messaging
P2P HTTP/Protobuf mesh adapter for cross-application Fluens messaging, with shared-key signed cluster authentication. |
|
|
Fluens.Web.Messaging.Dashboard
Cluster-wide monitoring surface for the Fluens cross-application messaging mesh: dead-letter queries, inbox lag, and stats snapshots. |
GitHub repositories
This package is not used by any popular GitHub repositories.