Send & Receive
Send and receive messages on a KubeMQ queue channel with the C# SDK for durable point-to-point delivery.
Overview
Queue send/receive is the foundational operation for guaranteed-delivery, point-to-point messaging: you reach for it whenever work needs to survive past the moment it's created and be handled by exactly one consumer, not broadcast to every subscriber. Unlike pub/sub, a queued message sits durably on the broker until something pulls it, so the producer and consumer never need to be online at the same time — a slow or offline worker adds latency, it doesn't drop the message.
This tutorial builds the smallest possible version of that round trip: SendQueueMessageAsync enqueues a message on a channel, and a QueueDownstreamReceiver's PollAsync pulls it back within a bounded WaitTimeoutSeconds. With AutoAck = false, settlement is manual — each message must be explicitly confirmed with AckAsync(), rejected with NackAsync(), or returned to the queue with ReQueueAsync() once your handler decides its outcome.
Gotchas: if your handler crashes before calling AckAsync(), the message stays locked to that receiver only until the broker's visibility timeout expires, after which it reappears for redelivery — write handlers that tolerate seeing the same message twice. Polling an empty queue isn't an error; PollAsync just blocks up to WaitTimeoutSeconds and returns a batch with no messages. And the downstream receiver is meant to be created once and reused across many PollAsync calls, not recreated per poll.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — Queues: Basic Send and Receive
//
// This example demonstrates sending a queue message and receiving it with acknowledgment.
// Queue messages are pull-based and processed by exactly one consumer.
// Uses QueueDownstreamReceiver.PollAsync for transactional message settlement.
//
// Prerequisites:
// - KubeMQ server running on localhost:50000
// - dotnet run
using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Queues;
using System.Text;
// TODO: Replace with your KubeMQ server address
await using var client = new KubeMQClient(new KubeMQClientOptions
{
ClientId = "csharp-queues-send-receive-client",
});
await client.ConnectAsync();
Console.WriteLine("Connected to KubeMQ server");
// Send a queue message
var sendResult = await client.SendQueueMessageAsync(new QueueMessage
{
Channel = "csharp-queues.send-receive",
Body = Encoding.UTF8.GetBytes("Process order #1234"),
Tags = new Dictionary<string, string> { ["priority"] = "high" }
});
Console.WriteLine($"Sent message: {sendResult.MessageId}");
// Receive via downstream receiver (supports manual settlement)
await using var receiver = await client.CreateQueueDownstreamReceiverAsync();
var batch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.send-receive",
MaxMessages = 1,
WaitTimeoutSeconds = 10,
AutoAck = false,
});
if (batch.HasMessages)
{
foreach (var msg in batch.Messages)
{
Console.WriteLine($"Received: {Encoding.UTF8.GetString(msg.Body.Span)}");
await msg.AckAsync();
Console.WriteLine("Message acknowledged");
}
}
else
{
Console.WriteLine("No messages received");
}
Console.WriteLine("Done.");
// Expected output:
// Connected to KubeMQ server
// Sent message: <message-id>
// Received: Process order #1234
// Message acknowledged
// Done.
How It Works
SendQueueMessageAsyncdelivers the message to the broker, which persists it and assigns a uniqueMessageId. The returnedsendResultconfirms delivery.CreateQueueDownstreamReceiverAsyncopens a long-lived gRPC stream to the broker. One receiver instance can be reused across manyPollAsynccalls — create it once and dispose it withawait using.PollAsyncwithAutoAck = falselocks the batch for this receiver. Each message must be explicitly settled withAckAsync(),NackAsync(), orReQueueAsync().msg.AckAsync()removes the message from the queue permanently. Unanswered messages return to the queue after the broker's visibility timeout.
Related
Was this page helpful?