KubeMQ
Client SDKsRustHow-to guides

Fan-Out

Broadcast KubeMQ Events from one publisher to multiple subscribers in Rust so each subscriber receives every event.

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: calling subscribe_to_events with an empty group string puts that subscription in broadcast mode instead of load-balanced mode. send_event 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 string silently turns broadcast into competing-consumer load-balancing with no error raised. Events are not persisted — a subscriber that isn't registered yet when send_event runs misses that event permanently (use Events Store if you need replay). And send_event returns as soon as the broker accepts it, not after subscribers process it, so a publisher can outrun subscription setup on a cold start — hence the short sleep before publishing in this sample.

Prerequisites

  • KubeMQ server running on localhost:50000
  • Rust SDK installed (cargo add kubemq)

Code

main.rs
use kubemq::prelude::*;
use kubemq::EventBuilder;
use std::time::Duration;

#[tokio::main]
async fn main() -> kubemq::Result<()> {
    let client = KubemqClient::builder()
        .host("localhost")
        .port(50000)
        .build()
        .await?;

    let channel = "rust-patterns.fan-out";

    let sub1 = client.subscribe_to_events(channel, "",
        |event| Box::pin(async move {
            println!("Service-A: {}", String::from_utf8_lossy(&event.body));
        }), None,
    ).await?;

    let sub2 = client.subscribe_to_events(channel, "",
        |event| Box::pin(async move {
            println!("Service-B: {}", String::from_utf8_lossy(&event.body));
        }), None,
    ).await?;

    let sub3 = client.subscribe_to_events(channel, "",
        |event| Box::pin(async move {
            println!("Service-C: {}", String::from_utf8_lossy(&event.body));
        }), None,
    ).await?;

    tokio::time::sleep(Duration::from_millis(500)).await;

    let event = EventBuilder::new()
        .channel(channel)
        .body(b"order-created".to_vec())
        .build();
    client.send_event(event).await?;
    println!("Published event to 3 subscribers");

    tokio::time::sleep(Duration::from_secs(2)).await;
    sub1.unsubscribe().await;
    sub2.unsubscribe().await;
    sub3.unsubscribe().await;
    client.close().await?;
    Ok(())
}

How It Works

  • All subscribers use an empty group string, so each receives every event (fan-out).
  • This pattern is ideal for notification systems, audit logs, and cache invalidation.
  • Review timeouts, channel names, and client IDs before running against shared environments.
  • Run the program while the server from the prerequisites is available.

Was this page helpful?

On this page