Fan-Out
Fan out messages to multiple consumers over a KubeMQ pub/sub channel in Node.js, delivering every event to all active subscribers.
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 subscribeToEvents without a group option puts that subscription in broadcast mode instead of load-balanced mode. sendEvent 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 stream hasn't registered yet when sendEvent runs misses that event permanently (use Events Store if you need replay). And sendEvent resolves 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 delay before publishing in this sample.
Prerequisites
- KubeMQ server running on
localhost:50000 - Node.js SDK installed (
npm install kubemq-js)
Code
/**
* Example: Fan-Out Pattern
*
* Demonstrates one publisher sending events to multiple independent
* subscribers. Each subscriber receives a copy of every published event.
* This is the default behavior for events (no consumer group).
*
* Prerequisites:
* - KubeMQ server running on localhost:50000
*
* Run: npx tsx examples/patterns/fan-out.ts
*/
import { KubeMQClient, createEventMessage } from 'kubemq-js';
async function main(): Promise<void> {
const client = await KubeMQClient.create({
address: 'localhost:50000',
clientId: 'js-patterns-fan-out-client',
});
try {
// Create three independent subscribers — each receives all events.
const logger = client.subscribeToEvents({
channel: 'js-patterns.fan-out',
onEvent: (event) => {
console.log('[Logger]', new TextDecoder().decode(event.body));
},
onError: (err) => {
console.error('Logger error:', err.message);
},
});
const monitor = client.subscribeToEvents({
channel: 'js-patterns.fan-out',
onEvent: (event) => {
console.log('[Monitor]', new TextDecoder().decode(event.body));
},
onError: (err) => {
console.error('Monitor error:', err.message);
},
});
const alerter = client.subscribeToEvents({
channel: 'js-patterns.fan-out',
onEvent: (event) => {
const body = new TextDecoder().decode(event.body);
if (body.includes('critical')) {
console.log('[Alerter] ALERT:', body);
} else {
console.log('[Alerter] (ignored non-critical):', body);
}
},
onError: (err) => {
console.error('Alerter error:', err.message);
},
});
// Publish events — all three subscribers receive each one.
const events = [
'cpu_usage=45% status=normal',
'cpu_usage=92% status=critical',
'disk_io=120MB/s status=normal',
];
for (const data of events) {
await client.sendEvent(
createEventMessage({
channel: 'js-patterns.fan-out',
body: data,
tags: { source: 'system-monitor' },
}),
);
}
console.log(`\nPublished ${events.length} events to 3 subscribers`);
await new Promise((resolve) => setTimeout(resolve, 1000));
logger.cancel();
monitor.cancel();
alerter.cancel();
} finally {
await client.close();
}
}
main().catch(console.error);
How It Works
- Three separate
subscribeToEvents()calls —logger,monitor,alerter— all subscribe to the same channel without agroupoption, putting them in fan-out mode where each subscriber independently receives every published event. - The
alertersubscriber inspects the event body and reacts differently based on content: it logs a highlighted message for'critical'events and ignores others — demonstrating per-subscriber routing logic. - The 1-second wait after subscribing and before publishing ensures all three gRPC subscription streams are registered on the server, preventing a race where events are published before subscriptions are active.
- Cancelling each handle (
logger.cancel(),monitor.cancel(),alerter.cancel()) terminates the three streams independently before the client is closed.
Related
Was this page helpful?