KubeMQ
Client SDKsJavaHow-to guides

Fan-Out

Fan out KubeMQ Events to multiple subscribers at once with the Java SDK so every consumer receives each message.

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: building an EventsSubscription without calling .group(...) puts that subscription in broadcast mode instead of load-balanced mode. publishEvent() 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 name silently turns broadcast into competing-consumer load-balancing with no error raised. Events are not persisted — a subscriber that hasn't called subscribeToEvents() yet when publishEvent() runs misses that event permanently (use Events Store if you need replay). And publishEvent() is fire-and-forget — it returns as soon as the broker accepts the send, not after subscribers process it — so a publisher can outrun subscription setup on a cold start, which is why this sample sleeps briefly before publishing.

Prerequisites

  • KubeMQ server running on localhost:50000
  • Java SDK installed (implementation 'io.kubemq.sdk:kubemq-sdk-Java:3.1.1' (Gradle) or Maven dependency from Getting Started)

Code

FanOutExample.java
package io.kubemq.example.patterns;

import io.kubemq.sdk.common.ServerInfo;
import io.kubemq.sdk.pubsub.*;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Fan-Out Pattern Example
 *
 * Demonstrates the fan-out pattern where one publisher sends to multiple subscribers.
 */
public class FanOutExample {

    private static final String ADDRESS = "localhost:50000";
    private static final String CLIENT_ID = "java-patterns-fan-out-client";
    private static final String CHANNEL = "java-patterns.fan-out";

    public static void main(String[] args) throws InterruptedException {
        // Create a client connected to the KubeMQ server
        PubSubClient client = PubSubClient.builder().address(ADDRESS).clientId(CLIENT_ID).build();
        ServerInfo info = client.ping();
        System.out.println("Connected to: " + info.getHost());
        // Create the events channel
        client.createEventsChannel(CHANNEL);

        // Subscribe multiple handlers (each receives all messages)
        int numSubscribers = 3;
        int numMessages = 3;
        AtomicInteger[] counts = new AtomicInteger[numSubscribers];
        CountDownLatch latch = new CountDownLatch(numSubscribers * numMessages);
        EventsSubscription[] subs = new EventsSubscription[numSubscribers];

        for (int i = 0; i < numSubscribers; i++) {
            final int id = i + 1;
            counts[i] = new AtomicInteger(0);
            final AtomicInteger counter = counts[i];

            subs[i] = EventsSubscription.builder()
                    .channel(CHANNEL)
                    .onReceiveEventCallback(event -> {
                        counter.incrementAndGet();
                        System.out.println("  Subscriber " + id + ": " + new String(event.getBody()));
                        latch.countDown();
                    })
                    .onErrorCallback(err -> {}).build();
            // Subscribe each subscriber to the channel
            client.subscribeToEvents(subs[i]);
        }
        Thread.sleep(500);

        // Publish messages (fan-out to all subscribers)
        System.out.println("Publishing " + numMessages + " messages to " + numSubscribers + " subscribers...\n");
        for (int i = 1; i <= numMessages; i++) {
            client.publishEvent(EventMessage.builder()
                    .id("fan-" + i).channel(CHANNEL)
                    .body(("Broadcast #" + i).getBytes()).build());
            Thread.sleep(100);
        }

        latch.await(5, TimeUnit.SECONDS);
        System.out.println("\nResults:");
        for (int i = 0; i < numSubscribers; i++) {
            System.out.println("  Subscriber " + (i + 1) + ": " + counts[i].get() + " messages");
        }

        // Clean up resources
        for (EventsSubscription s : subs) { s.cancel(); }
        client.deleteEventsChannel(CHANNEL);
        client.close();
    }
}

How It Works

  • A single PubSubClient creates three EventsSubscription objects on the same channel without a group() — every subscriber receives every published message independently, which is the fan-out property.
  • Each subscriber is wired with its own AtomicInteger counter and shares a CountDownLatch initialized to numSubscribers * numMessages; latch.await() blocks main until all deliveries are confirmed.
  • publishEvent() is fire-and-forget: it enqueues the gRPC send and returns immediately without waiting for subscriber acknowledgements.
  • Thread.sleep(500) after subscribing gives the SDK time to register all three subscriptions with the broker before publishing begins; this avoids race conditions in examples but is not needed in long-running services.

Was this page helpful?

On this page