KubeMQ
LearnConcepts

Channels & Routing

How messages find their destination — named channels, wildcard subscriptions, and multicast routing that fans one publish out to many channels.

Think of a channel like the address on an envelope. You do not hand a letter directly to a person — you drop it in the mail with an address, and the postal system delivers it to whoever picks up mail at that address. Senders and receivers never need to know about each other; they only need to agree on the address.

A channel is that address: a named destination that a broker uses to route messages. Publishers send to a channel name; subscribers express interest in a channel name. The broker connects the two. This indirection is what makes messaging loosely coupled — you can add, remove, or move services without either side changing the other's code.

Channels — the idea

A channel is just a string name, chosen by you. Publishers attach that name to each message; the broker matches it against the names subscribers asked for and delivers accordingly. Nothing is hard-wired — the channel is created the moment something uses it.

Good channel names follow a hierarchy, read left to right from broad to specific, with a separator between each level. A common convention is {domain}.{entity}.{action}:

orders.created
orders.updated
orders.us-east.created
payments.completed
inventory.reserved

That hierarchy is not decoration. It is what makes the next idea — wildcards — possible.

Channels are named destinations; the broker matches a publisher's channel name to the subscribers that asked for it.

Wildcards — subscribing to a pattern

Naming channels one by one works until you have dozens of them. A monitoring service that needs every order event would have to subscribe to orders.created, orders.updated, orders.shipped, and remember to add a new subscription every time someone invents orders.cancelled.

A wildcard subscription solves this: instead of naming exact channels, the subscriber names a pattern, and the broker delivers every message whose channel matches. The hierarchy makes the pattern meaningful — each level can be matched broadly or exactly.

Two wildcard tokens are common, and KubeMQ uses both:

TokenMatchesExample
*exactly one levelorders.* matches orders.created, not orders.us-east.created
>one or more levelsorders.> matches orders.created and orders.us-east.created

A standalone > subscribes to everything — a firehose for audit or debugging.

One pattern subscription captures many channels: orders.* matches single-level order events, while > captures everything.

Pitfall: wildcards belong to subscriptions, not publishes. A publisher must always send to a concrete channel name — orders.created, never orders.*. The * and > characters are illegal in a publish channel.

Routing — one publish, many channels

Wildcards let one subscriber listen to many channels. Multicast routing is the mirror image: it lets one publish reach many channels at once, without the publisher looping or opening multiple connections.

The publisher encodes a list of targets into the channel name, and the broker fans the message out to each. This is how you tee a single business event to several consumers with different needs — a live feed and a durable copy and a work queue — from one call.

A single publish fans out to channels of different types at once — a real-time feed, a durable store, and a reliable work queue.

Precise definition

  • Channel — a named destination string. Created on first use, addressed by publishers and subscribers; the unit the broker routes on.
  • Hierarchical naming — a dot-separated convention (domain.entity.action) that gives channels structure so patterns can match levels.
  • Wildcard subscription — a subscription whose channel is a pattern using * (one level) or > (one or more levels). The broker delivers every message whose channel matches.
  • Multicast routing — a single publish addressed to multiple channels at once, encoded in the channel string; the broker delivers a copy to each target.

Trade-offs

When it helpsWhen it bites
A consumer needs a whole category of channels — one orders.> beats a dozen explicit subscriptions.A too-broad pattern (>) hauls in traffic you do not need, wasting bandwidth and processing.
You want to tee one event to several patterns (live + durable + queue) without publisher-side loops.Routing to many targets multiplies broker work per publish; very wide fan-outs add latency.
A naming hierarchy lets new channels appear without changing subscribers.Inconsistent naming breaks wildcards — order.created and orders.created will not match the same pattern.
Routing keeps publishers simple — they do not track who consumes what.Routing hides destinations in a string; an inspectable, documented naming scheme is essential to avoid surprise delivery.

In KubeMQ

KubeMQ channels are exactly the named destinations above — a plain string you choose, created on first use. Wildcard subscriptions are supported for Events (Pub/Sub): subscribe to orders.* or > and the broker matches every event channel against your pattern. Multicast routing is encoded directly in the channel string: ; separates targets and a type: prefix selects the pattern.

Routing stringEffect
events:order-live;events_store:order-archiveone publish → a live feed and a durable copy
events:notify;queues:order-fulfilmentbroadcast and a reliable work queue

The snippet below is the canonical wildcard subscribe from the Events tutorial — one subscription to orders.* captures every single-level order channel.

orders_monitor.go
sub, err := client.SubscribeToEvents(ctx, "orders.*", "",
    kubemq.WithOnEvent(func(event *kubemq.Event) {
        fmt.Printf("[Orders Monitor] channel=%s body=%s\n",
            event.Channel, string(event.Body))
    }),
    kubemq.WithOnError(func(err error) {
        log.Println("Error:", err)
    }),
)
orders_monitor.py
client.subscribe_to_events(
    subscription=EventsSubscription(
        channel="orders.*",
        on_receive_event_callback=lambda e: print(
            f"[Orders Monitor] channel={e.channel} body={e.body.decode()}"
        ),
        on_error_callback=lambda e: print(f"Error: {e}"),
    ),
    cancel=CancellationToken(),
)
orders_monitor.js
client.subscribeToEvents({
  channel: "orders.*",
  onEvent: (msg) =>
    console.log(
      `[Orders Monitor] channel=${msg.channel} body=${Buffer.from(msg.body).toString()}`
    ),
  onError: (err) => console.error("Error:", err.message),
});
OrdersMonitor.java
client.subscribeToEvents(EventsSubscription.builder()
    .channel("orders.*")
    .onReceiveEventCallback(event ->
        System.out.printf("[Orders Monitor] channel=%s body=%s%n",
            event.getChannel(), new String(event.getBody())))
    .onErrorCallback(err ->
        System.err.println("Error: " + err.getMessage()))
    .build());
OrdersMonitor.cs
await foreach (var msg in client.SubscribeToEventsAsync(
    new EventsSubscription { Channel = "orders.*" }))
{
    Console.WriteLine($"[Orders Monitor] channel={msg.Channel} "
        + $"body={Encoding.UTF8.GetString(msg.Body.Span)}");
}
OrdersMonitor.kt
client.subscribeToEvents(
    channel = "orders.*",
    onEvent = { event ->
        println("[Orders Monitor] channel=${event.channel} body=${String(event.body)}")
    },
    onError = { err -> System.err.println("Error: ${err.message}") }
)
orders_monitor.cpp
client.subscribeToEvents("orders.*", "",
    [](const kubemq::Event& event) {
        std::cout << "[Orders Monitor] channel=" << event.channel
                  << " body=" << event.body << std::endl;
    },
    [](const std::string& err) {
        std::cerr << "Error: " << err << std::endl;
    }
);
orders_monitor.rs
// A single wildcard subscription matches every channel under the pattern.
let sub = client
    .subscribe_to_events(
        "orders.*",
        "",
        |event| {
            Box::pin(async move {
                println!(
                    "[Orders Monitor] channel={}, body={}",
                    event.channel,
                    String::from_utf8_lossy(&event.body)
                );
            })
        },
        None,
    )
    .await?;
orders_monitor.rb
cancel = KubeMQ::CancellationToken.new

sub = KubeMQ::PubSub::EventsSubscription.new(channel: "orders.*")
client.subscribe_to_events(sub, cancellation_token: cancel,
  on_error: ->(e) { puts "Error: #{e.message}" }) do |event|
  puts "[Orders Monitor] channel=#{event.channel} body=#{event.body}"
end
orders_monitor.exs
{:ok, sub} =
  KubeMQ.Client.subscribe_to_events(client, "orders.*",
    on_event: fn event ->
      IO.puts("[Orders Monitor] channel=#{event.channel} body=#{event.body}")
    end
  )

In KubeMQ: wildcard subscriptions are an Events feature. Events Store, Queues, and RPC use exact channel names — they do not match * or > patterns. Multicast routing, by contrast, works across patterns: one routing string can tee an event into Events, Events Store, and Queues at the same time.

How KubeMQ does this →

Was this page helpful?

On this page