RabbitMQ (AMQP 0-9-1)
Point a RabbitMQ app at KubeMQ by changing only the connection string — AMQP 0-9-1 over KubeMQ Queues, where every queue is a Queue channel.
Point a RabbitMQ app at KubeMQ by changing only the connection string. The RabbitMQ
(AMQP 0-9-1) connector is a built-in, wire-protocol bridge inside kubemq-server that
speaks the RabbitMQ wire dialect natively — any standard AMQP 0-9-1 client (amqp091-go,
pika, amqp-client, amqplib, RabbitMQ.Client, bunny, lapin) talks to KubeMQ's
Queues with no KubeMQ SDK, no library swap, and no code rewrite.
What is the RabbitMQ connector
AMQP 0-9-1 is the wire protocol that RabbitMQ popularized: a client opens a connection, multiplexes channels over it, declares queues and exchanges, and publishes messages routed by exchange type and routing key. The KubeMQ connector accepts every one of these operations from a stock client and bridges them onto KubeMQ — it is a gateway, not a client library, so your application only needs its existing AMQP 0-9-1 client.
Mental model — everything is a Queue. Every AMQP queue maps to exactly one KubeMQ
Queue channel named amqp.{vhost}.{queue}. Exchanges and bindings are virtual,
connector-side routing metadata resolved at publish time — not data stores. AMQP only ever
touches the KubeMQ Queue primitive, so the connector's "patterns" mirror AMQP routing
concepts (work queues, pub/sub, routing, topics, RPC), not KubeMQ's five messaging
patterns.
Key capabilities:
- Drop-in connection-string migration — keep your RabbitMQ client and code; change only the broker host in the URL.
- Everything is a Queue — every AMQP queue is a durable KubeMQ Queue channel
amqp.{vhost}.{queue}; exchanges (default, direct, fanout, topic, headers) route to those queues virtually at publish time. - Native RPC — request/reply uses RabbitMQ's
amq.rabbitmq.reply-to(direct reply-to); there is no gRPC responder. - Cross-protocol interop — a message published over AMQP to
amqp.default.ordersis consumable by a gRPC or REST KubeMQ client on the same channel, and vice-versa.
On by default. A stock kubemq-server binds port 5672 out of the box (5671 as well
once TLS is configured) — no enable variable is needed. To turn the connector off, set
CONNECTORS_AMQP_ENABLE=false (Docker) or spec.amqp.enabled: false (Kubernetes). If
5672 is already taken on the host (a real RabbitMQ running locally), the server logs an
error and keeps running without the connector, while the dashboard still lists it as
enabled — stop the other broker or move the port with CONNECTORS_AMQP_PORT. See
Getting started.
How it works
An AMQP client publishes to an exchange with a routing key. The connector resolves the exchange routing (default / direct / fanout / topic / headers) to a set of target queues at publish time, then writes each message to that queue's KubeMQ Queue channel through the message broker. A consumer on the same queue — over AMQP or any other KubeMQ transport — receives it.
A publish to queue orders on vhost / resolves to the KubeMQ Queue channel amqp.default.orders; any consumer on that channel — AMQP or gRPC/REST — receives the message.
Ports & protocol surface
| Port | Transport | Protocol | Notes |
|---|---|---|---|
5672 | Plain TCP (SASL PLAIN / AMQPLAIN) | AMQP 0-9-1 (RabbitMQ wire dialect) | Default plain listener. Shared with the AMQP 1.0 connector via the internal amqpmux. |
5671 | TLS / AMQPS over TCP | AMQP 0-9-1 | Binds only when the server-global Security block is configured. Shared TLS listener with AMQP 1.0. |
A single amqpmux listener accepts every connection on 5672/5671, reads the 8-byte AMQP
protocol header, and routes it to the matching dialect engine — so AMQP 0-9-1 and AMQP 1.0
coexist on the same ports. SASL offers PLAIN and AMQPLAIN always, and EXTERNAL
(certificate login) once it is switched on; ANONYMOUS is never offered. The AMQP vhost / maps to the
connector's configured DefaultVhost segment (literal "default"); see
Architecture for the dispatch detail.
Publish to a queue
The example below declares a queue, publishes one text/plain message to it through the
default exchange (routing key = queue name), and consumes it back. Queue hello on vhost
/ lands on the KubeMQ Queue channel amqp.default.hello. Every client reads the broker
endpoint from KUBEMQ_AMQP_URL (default amqp://guest:guest@localhost:5672/).
Which to use
This is the same round trip as the Getting started tutorial, shown here inline for reference. For a step-by-step walkthrough — enabling the connector, running a local broker, and verifying each step — use Getting started instead.
package main
import (
"context"
"log"
"os"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
func amqpURL() string {
if v := os.Getenv("KUBEMQ_AMQP_URL"); v != "" {
return v
}
return "amqp://guest:guest@localhost:5672/"
}
func main() {
conn, err := amqp.Dial(amqpURL())
if err != nil {
log.Fatalf("dial: %v", err)
}
defer func() { _ = conn.Close() }()
ch, err := conn.Channel()
if err != nil {
log.Fatalf("channel: %v", err)
}
defer func() { _ = ch.Close() }()
// Declare "hello" → KubeMQ Queue channel amqp.default.hello.
q, err := ch.QueueDeclare("hello", false, false, false, false, nil)
if err != nil {
log.Fatalf("declare queue: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
// Publish on the default exchange — routing key = queue name.
if err := ch.PublishWithContext(ctx, "", q.Name, false, false, amqp.Publishing{
ContentType: "text/plain",
Body: []byte("Hello World!"),
}); err != nil {
log.Fatalf("publish: %v", err)
}
log.Printf(" [x] Sent %q", "Hello World!")
// Consume with auto-ack and print the exact body.
msgs, err := ch.Consume(q.Name, "", true, false, false, false, nil)
if err != nil {
log.Fatalf("consume: %v", err)
}
select {
case d := <-msgs:
log.Printf(" [x] Received %q", string(d.Body))
case <-ctx.Done():
log.Fatalf("timed out: %v", ctx.Err())
}
}import os
import pika
URL = os.environ.get("KUBEMQ_AMQP_URL", "amqp://guest:guest@localhost:5672/")
QUEUE = "hello" # → KubeMQ Queue channel amqp.default.hello
def main() -> None:
connection = pika.BlockingConnection(pika.URLParameters(URL))
channel = connection.channel()
# Default (nameless) exchange: routing key == queue name.
channel.queue_declare(queue=QUEUE, durable=False, exclusive=False, auto_delete=False)
channel.basic_publish(
exchange="",
routing_key=QUEUE,
body=b"Hello World!",
properties=pika.BasicProperties(content_type="text/plain"),
)
print(" [x] Sent 'Hello World!'")
method, _props, body = channel.basic_get(queue=QUEUE, auto_ack=True)
if method is None:
raise SystemExit("no message received from queue")
print(f" [x] Received {body.decode()!r}")
channel.close()
connection.close()
if __name__ == "__main__":
main()import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public final class Main {
private static final String QUEUE = "hello"; // → amqp.default.hello
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setUri(System.getenv().getOrDefault(
"KUBEMQ_AMQP_URL", "amqp://guest:guest@localhost:5672/"));
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// queue.declare: non-durable, non-exclusive, no auto-delete.
channel.queueDeclare(QUEUE, false, false, false, null);
// Publish via the default exchange ("") with routing key = queue name.
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.contentType("text/plain")
.build();
channel.basicPublish("", QUEUE, props, "Hello World!".getBytes("UTF-8"));
System.out.println("[x] Sent 'Hello World!'");
BlockingQueue<String> received = new ArrayBlockingQueue<>(1);
DeliverCallback onDeliver = (tag, delivery) ->
received.offer(new String(delivery.getBody(), "UTF-8"));
channel.basicConsume(QUEUE, true, onDeliver, tag -> { });
String got = received.poll(15, TimeUnit.SECONDS);
if (got == null) {
throw new IllegalStateException("timed out waiting for the message");
}
System.out.println("[x] Received '" + got + "'");
}
}
}import amqp from "amqplib";
// amqplib reads the URL path as the vhost; the bare trailing "/" in the dev URL
// resolves to the default vhost (KubeMQ segment "default").
const URL = process.env.KUBEMQ_AMQP_URL ?? "amqp://guest:guest@localhost:5672/";
const QUEUE = "hello"; // → KubeMQ Queue channel amqp.default.hello
async function main() {
const conn = await amqp.connect(URL);
const ch = await conn.createChannel();
await ch.assertQueue(QUEUE, { durable: false, autoDelete: false, exclusive: false });
const received = new Promise((resolve) => {
ch.consume(
QUEUE,
(msg) => {
if (msg === null) return;
console.log(`[x] Received: ${msg.content.toString()}`);
resolve();
},
{ noAck: true },
);
});
// Default exchange ("") routes by queue name.
ch.publish("", QUEUE, Buffer.from("Hello World!"), { contentType: "text/plain" });
console.log("[x] Sent: Hello World!");
await received;
await ch.close();
await conn.close();
}
main().catch((err) => {
console.error("publish failed:", err);
process.exit(1);
});using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
const string queueName = "hello"; // → KubeMQ Queue channel amqp.default.hello
var factory = new ConnectionFactory
{
Uri = new Uri(Environment.GetEnvironmentVariable("KUBEMQ_AMQP_URL")
?? "amqp://guest:guest@localhost:5672/"),
};
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var ct = cts.Token;
await using var connection = await factory.CreateConnectionAsync(ct);
await using var channel = await connection.CreateChannelAsync(cancellationToken: ct);
// Non-durable, not exclusive, not auto-delete — a plain shared queue.
await channel.QueueDeclareAsync(queueName, durable: false, exclusive: false,
autoDelete: false, arguments: null, cancellationToken: ct);
var received = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += (_, ea) =>
{
received.TrySetResult(Encoding.UTF8.GetString(ea.Body.Span));
return Task.CompletedTask;
};
await channel.BasicConsumeAsync(queueName, autoAck: true, consumer: consumer, cancellationToken: ct);
// Publish to the default exchange with routing key = queue name.
var props = new BasicProperties { ContentType = "text/plain" };
await channel.BasicPublishAsync(exchange: "", routingKey: queueName, mandatory: false,
basicProperties: props, body: Encoding.UTF8.GetBytes("Hello World!"), cancellationToken: ct);
Console.WriteLine("[x] Sent 'Hello World!'");
var message = await received.Task.WaitAsync(ct);
Console.WriteLine($"[x] Received '{message}'");# frozen_string_literal: true
require "bunny"
URL = ENV.fetch("KUBEMQ_AMQP_URL", "amqp://guest:guest@localhost:5672/")
QUEUE = "hello" # → KubeMQ Queue channel amqp.default.hello
conn = Bunny.new(URL)
conn.start
ch = conn.create_channel
# Publisher confirms: wait for the broker to accept and route the publish before
# reading it back, so a fire-and-forget publish is never lost in flight.
ch.confirm_select
queue = ch.queue(QUEUE, durable: false, auto_delete: false, exclusive: false)
# Default ("") exchange routes by queue name.
ch.default_exchange.publish("Hello World!", routing_key: queue.name, content_type: "text/plain")
ch.wait_for_confirms
puts " [x] Sent 'Hello World!'"
body = nil
deadline = Time.now + 5
while body.nil? && Time.now < deadline
_info, _props, body = queue.pop(manual_ack: false)
sleep 0.2 if body.nil?
end
puts " [x] Received '#{body}'"
conn.closeuse futures_lite::StreamExt;
use lapin::{
options::{BasicConsumeOptions, BasicPublishOptions, QueueDeclareOptions},
types::FieldTable,
BasicProperties, Connection, ConnectionProperties,
};
const QUEUE: &str = "hello"; // → KubeMQ Queue channel amqp.default.hello
// lapin reads the URL path as the vhost and treats a bare trailing "/" as an
// empty vhost, which the connector rejects. The default "/" vhost must be
// percent-encoded as "%2f".
fn amqp_url() -> String {
let url = std::env::var("KUBEMQ_AMQP_URL")
.unwrap_or_else(|_| "amqp://guest:guest@localhost:5672/".into());
let host = url.rsplit_once('@').map_or(url.as_str(), |(_, h)| h);
if host.ends_with('/') && !host.ends_with("/%2f") {
format!("{url}%2f")
} else {
url
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let conn = Connection::connect(&amqp_url(), ConnectionProperties::default()).await?;
let channel = conn.create_channel().await?;
channel
.queue_declare(QUEUE, QueueDeclareOptions::default(), FieldTable::default())
.await?;
// Publish to the default exchange, routing key = queue name.
channel
.basic_publish(
"",
QUEUE,
BasicPublishOptions::default(),
b"Hello World!",
BasicProperties::default().with_content_type("text/plain".into()),
)
.await?
.await?;
println!("[x] Sent 'Hello World!'");
let mut consumer = channel
.basic_consume(
QUEUE,
"hello-consumer",
BasicConsumeOptions { no_ack: true, ..Default::default() },
FieldTable::default(),
)
.await?;
if let Some(delivery) = consumer.next().await {
let delivery = delivery?;
println!("[x] Received '{}'", String::from_utf8_lossy(&delivery.data));
}
conn.close(0, "done").await?;
Ok(())
}Supported languages
The connector speaks standard AMQP 0-9-1, so any conformant RabbitMQ client works. The examples pin one native client per language — there is no KubeMQ SDK, no proto bindings, and no published package.
| Language | Client library | Notes |
|---|---|---|
| Go | github.com/rabbitmq/amqp091-go | The RabbitMQ team's Go client. |
| Python | pika | BlockingConnection with URLParameters. |
| Java | com.rabbitmq:amqp-client | The official RabbitMQ Java client. |
| JavaScript / TypeScript | amqplib | Encode the default / vhost as %2f in the URL. |
| C# / .NET | RabbitMQ.Client | Task-based async API (v7+). |
| Ruby | bunny | Use publisher confirms before consuming. |
| Rust | lapin | async/await on Tokio; percent-encode the / vhost. |
Next steps
Getting started
Connect, declare, publish, and consume a message end-to-end through the RabbitMQ connector in minutes.
Configuration
The 12 connector settings, how to turn the on-by-default connector off, and the reserved default vhost.
Work queues
Competing consumers and fair dispatch over a single KubeMQ Queue channel.
Channel mapping
The amqp.{vhost}.{queue} grammar, name constraints, and the property/header mapping.
Was this page helpful?