Command Group
Load-balance KubeMQ Commands across multiple handlers with a consumer group using the Go SDK.
Overview
A command consumer group turns a single command handler into a scalable worker pool: run multiple identical instances subscribed with the same group name, and the broker load-balances each incoming command to exactly one member instead of broadcasting it to all of them. This is how you add capacity to handle a growing command volume — start more workers in the same group — without changing anything on the caller's side.
Every subscriber passes the same group alongside the channel to SubscribeToCommands; the broker tracks membership and picks one live member per command. SendCommand on the caller side is unaware groups exist — it just blocks for a CommandReply, which comes back from whichever worker happened to handle it.
Gotchas: group membership is scoped per channel — subscribers on the same channel with different group names each get their own full copy of every command (fan-out), which looks like a bug when you expected load-balancing. A slow handler still holds up the caller's timeout, since only one worker is ever picked. And if every member of the group is offline when a command arrives, the send simply fails or times out — commands aren't queued or replayed for a group that has no active listener.
Prerequisites
- KubeMQ server running on
localhost:50000 - Go SDK installed (
go get github.com/kubemq-io/kubemq-go/v2)
Code
// Example: commands/consumer-group
//
// Demonstrates load-balanced command handling with consumer groups.
// Multiple handlers in the same group share the command workload,
// with each command delivered to exactly one handler.
//
// Channel: go-commands.consumer-group
// Client ID: go-commands-consumer-group-client
//
// Run with a KubeMQ server on localhost:50000
// (see https://docs.kubemq.io/deploy).
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/kubemq-io/kubemq-go/v2"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
client, err := kubemq.NewClient(ctx,
kubemq.WithAddress("localhost", 50000),
kubemq.WithClientId("go-commands-consumer-group-client"),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
channel := "go-commands.consumer-group"
group := "go-commands-worker-group"
done := make(chan struct{})
// Subscribe with a consumer group for load-balanced command handling.
sub, err := client.SubscribeToCommands(ctx, channel, group,
kubemq.WithOnCommandReceive(func(cmd *kubemq.CommandReceive) {
fmt.Printf("Worker received: body=%s\n", cmd.Body)
resp := kubemq.NewCommandReply().
SetRequestId(cmd.Id).
SetResponseTo(cmd.ResponseTo).
SetExecutedAt(time.Now())
_ = client.SendCommandResponse(ctx, resp)
close(done)
}),
kubemq.WithOnError(func(err error) {
log.Println("Group error:", err)
}),
)
if err != nil {
log.Fatal(err)
}
defer sub.Unsubscribe()
time.Sleep(300 * time.Millisecond)
// Send a command to the group.
cmdResp, err := client.SendCommand(ctx, kubemq.NewCommand().
SetChannel(channel).
SetBody([]byte("group-task")).
SetTimeout(10*time.Second))
if err != nil {
log.Fatal(err)
}
fmt.Printf("Group response: executed=%v\n", cmdResp.Executed)
<-done
}
How It Works
client.SubscribeToCommands(ctx, channel, group, opts...)passesgroup := "go-commands-worker-group", activating load-balanced command delivery across all subscribers in the group.- The broker routes each command to exactly one member of the group rather than broadcasting — when multiple worker instances subscribe with the same
channelandgroup, the workload is shared. client.SendCommandblocks until one group member sends back aCommandReply; the caller does not need to know which specific worker handled the command.defer sub.Unsubscribe()cleanly removes this subscriber from the group; the broker will route subsequent commands to the remaining active members.
Related
Was this page helpful?