Query Group
Load-balance KubeMQ queries across a group of handlers using the C# SDK RPC client.
Overview
A consumer group scales query handling horizontally without touching the caller's side. Instead of one process answering every query on a channel, you run several identical handler instances under the same group name, and the broker routes each query to exactly one member — never to all of them. That turns a single responder into a pool you can grow or shrink to match load, which matters for anything RPC-shaped: a lookup service, a cache-fill handler, a synchronous read path behind an API.
It works by tying group membership to the subscription: setting Group on QueriesSubscription passed to client.SubscribeToQueriesAsync load-balances across every subscriber sharing that Channel and Group. The sender calls client.SendQueryResponseAsync and the initial SendQuery exactly as it would against a single handler — it never knows how many members exist or which one answered.
Gotchas: channel and group name must match exactly, or a typo quietly creates a second, empty group instead of erroring. Omit Group and every subscriber reverts to broadcast, each answering independently. A stuck group member isn't bypassed — the caller just sees a timeout.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — Queries: Consumer Group Subscription
//
// This example demonstrates subscribing to queries with a consumer group.
// When multiple handlers join the same group, queries are load-balanced across them
// so that only one handler in the group processes each query.
//
// Prerequisites:
// - KubeMQ server running on localhost:50000
// - Run Queries.SendQuery in a separate terminal to send queries
// - dotnet run
using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Queries;
using System.Text;
await using var client = new KubeMQClient(new KubeMQClientOptions
{
ClientId = "csharp-queries-consumer-group-client",
});
await client.ConnectAsync();
Console.WriteLine("Subscribed to queries with consumer group 'handler-group'...");
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};
await foreach (var query in client.SubscribeToQueriesAsync(
new QueriesSubscription { Channel = "csharp-queries.consumer-group", Group = "handler-group" },
cts.Token))
{
var body = Encoding.UTF8.GetString(query.Body.Span);
Console.WriteLine($"Query: {query.RequestId} — {body}");
var responseBody = Encoding.UTF8.GetBytes("{\"status\":\"ok\",\"handler\":\"group-member\"}");
await client.SendQueryResponseAsync(new QueryResponse
{
RequestId = query.RequestId,
ReplyChannel = query.ReplyChannel!,
Body = responseBody,
Executed = true,
});
Console.WriteLine(" -> Responded with data");
}
Console.WriteLine("Done.");
How It Works
Group = "handler-group"inQueriesSubscriptionregisters this handler as a member of a named load-balancing group. Each incoming query is routed to exactly one member of the group.- Spin up multiple instances (different
ClientId) to distribute query load horizontally. The broker round-robins across registered group members. - Each handler must call
SendQueryResponseAsyncwithRequestIdandReplyChannelfrom the received query for the broker to return the response to the sender. - Omit
Groupto switch to broadcast: every subscriber independently receives every query — useful for fan-out read models.
Related
Was this page helpful?