KubeMQ
Client SDKsC#How-to guides

Fan-Out

Fan out messages to multiple subscribers with KubeMQ events using the C# SDK pub/sub pattern.

Overview

Fan-out is the default delivery behavior of KubeMQ Events pub/sub: when subscribers don't join a consumer group, every subscriber gets its own independent copy of each published event. Reach for it whenever several unrelated services need to react to the same occurrence — an order placed, a config change, an audit event — without the publisher knowing or caring who's listening, and without one subscriber's slowness affecting another's delivery.

The mechanism is simply omission: an EventsSubscription with no Group set puts that subscription in broadcast mode instead of load-balanced mode. SendEventAsync doesn't change at all — the publisher sends once, and the broker independently pushes a copy to every active subscriber on the channel.

Gotchas: fan-out is opt-out by default, so a typo'd or accidentally shared Group value silently turns broadcast into competing-consumer load-balancing with no error raised. Events are not persisted — a subscriber whose SubscribeToEventsAsync loop hasn't started yet when SendEventAsync runs misses that event permanently (use Events Store if you need replay). And SendEventAsync is fire-and-forget at the protocol level — it completes once the broker accepts it, not after subscribers process it — so a publisher can outrun subscription setup on a cold start, hence the delay before publishing in this sample.

Prerequisites

  • KubeMQ server running on localhost:50000
  • C# SDK installed (dotnet add package KubeMQ.SDK.CSharp)

Code

Program.cs
// KubeMQ .NET SDK — Patterns: Fan-Out
//
// This example demonstrates the fan-out pattern using events.
// A single publisher sends events that are received by all subscribers
// on the channel. Each subscriber independently receives every event.
//
// Prerequisites:
//   - KubeMQ server running on localhost:50000
//   - dotnet run

using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Events;
using System.Text;

await using var client = new KubeMQClient(new KubeMQClientOptions
{
    ClientId = "csharp-patterns-fan-out-client",
});
await client.ConnectAsync();

Console.WriteLine("Connected to KubeMQ server");

var cts = new CancellationTokenSource();

// Start 3 independent subscribers — all will receive every event (no group)
for (int n = 1; n <= 3; n++)
{
    var subscriberName = $"Subscriber-{n}";
    _ = Task.Run(async () =>
    {
        await foreach (var msg in client.SubscribeToEventsAsync(
            new EventsSubscription { Channel = "csharp-patterns.fan-out" }, cts.Token))
        {
            Console.WriteLine($"[{subscriberName}] {Encoding.UTF8.GetString(msg.Body.Span)}");
        }
    });
}

// Allow subscriptions to establish
await Task.Delay(1000);

// Publish events — each subscriber receives all of them
for (var i = 1; i <= 3; i++)
{
    await client.SendEventAsync(new EventMessage
    {
        Channel = "csharp-patterns.fan-out",
        Body = Encoding.UTF8.GetBytes($"Broadcast #{i}"),
    });
    Console.WriteLine($"Published broadcast #{i}");
}

await Task.Delay(2000);
cts.Cancel();

Console.WriteLine("Done.");

How It Works

  • All three subscribers call SubscribeToEventsAsync on the same channel name without a Group — this is fan-out mode, where every subscriber receives every message independently.
  • Each subscription runs in its own Task.Run loop so all three receive concurrently; the subscriberName variable is captured per iteration with the loop counter n.
  • SendEventAsync is fire-and-forget at the protocol level — the broker delivers to all active subscribers but does not wait for acknowledgements.
  • The 1-second delay after starting subscribers gives the gRPC streams time to register on the broker before the first event is published.

Was this page helpful?

On this page