Back to: Microservices using ASP.NET Core Web API Tutorials
RabbitMQ End-to-End Test using Management UI
In this post, I will explain how to perform RabbitMQ End-to-End Testing using the Management UI. Please read our previous article, which discusses how to download, install, and Configure RabbitMQ locally on a Windows OS step by step.
RabbitMQ is a powerful open-source message broker that allows applications to communicate asynchronously through exchanges, queues, and bindings. While many developers interact with RabbitMQ programmatically, the Management UI at http://localhost:15672 offers a visual way to explore its core concepts and message flow.
- Basics of RabbitMQ Architecture – Producers, Exchanges, Queues, Bindings, Consumers
- How to create queues and configure durability, TTL, max-length, DLX
- How to create exchanges (direct, topic, fanout, headers) and understand their routing behaviour
- How to bind exchanges to queues using routing keys and rules
- How to publish messages to exchanges?
- How to consume messages via the Management UI and test ACK/NACK behaviours
- What happens when messages are unroutable, and how to handle them with Alternate Exchanges
- How to set up a Dead Letter Exchange (DLX) for expired, rejected, or overflowed messages
- How to create and use Virtual Hosts (vhosts) for application/environment isolation
- How to assign user permissions (configure, write, read) with regex-based patterns
RabbitMQ Architecture
Please have a look at the following image to understand the Message Flow in RabbitMQ.
Producer → Exchange → [via Routing Key + Bindings] → Queue → Consumer
- A Producer sends a message to an Exchange.
- The Exchange uses Bindings and Routing Keys to determine which Queue(s) should receive the message.
- One or more Consumers pick up the message from the queue.
Step 1: Create a New Queue – TestQueue
What is a Queue?
A Queue is a buffer where messages are stored until they are consumed.
- Producers send messages → Queues hold them → Consumers retrieve them.
- Queues support FIFO order (First-In-First-Out).
- Messages remain in a queue until they are either acknowledged and removed or expire.
Steps to Create a Queue in RabbitMQ UI:
- Log in to RabbitMQ Management UI (http://localhost:15672)
- Go to the Queues and Streams tab.
- Click Add a new queue.
- Fill in:
- Name: TestQueue
- Durability: Durable (recommended)
- Leave everything else as the default.
- Click Add queue
Explanation of Queue Settings
- Virtual Host (vhost): A virtual host (vhost) is a namespace. You can think of it as an isolated context in RabbitMQ. Each vhost has its own queues, exchanges, bindings, and so on. Multiple apps can use different vhosts to avoid interference. It helps isolate applications (like /dev, /prod). Default is /.
- Type: The queue implementation. For testing, “classic” is the default. Common types:
-
-
- Classic (default; general purpose).
- Quorum (replicated, consensus-based, HA; ideal for durability).
- Stream (high-throughput append-only streaming use-cases).
-
- Name: Unique name of the queue (e.g., TestQueue).
- Durability:
-
-
- Durable queues survive broker restarts (metadata is persisted).
- Transient queues do not survive restarts.
-
- Arguments: Optional advanced behaviors, for example:
- x-message-ttl (expire messages after N ms)
- x-expires (auto-delete the queue after N ms idle)
- x-max-length / x-max-length-bytes (cap size)
- x-dead-letter-exchange (where to send expired/rejected messages)
- x-queue-mode=lazy (store messages on disk to reduce RAM)
Queue Types with Layman Example:
- Classic: Think of a food delivery counter at a restaurant. Orders (messages) are placed on the counter, and once the delivery person (consumer) picks them up, they’re gone. Nothing is kept for later. It is a simple pass-through system where items wait until collected.
- Quorum: Like storing files in Google Drive. Although you only see one copy, Google maintains multiple replicas across different servers. If one server crashes, your file remains safe and retrievable from another, providing reliability at the cost of a little extra effort.
- Stream: Like Netflix streaming a movie. You can start watching live, rewind to the beginning, or re-watch the same content days later, while others watch at their own pace. The recording remains available so multiple people can view it independently.
Step 2: Create a New Exchange – TestExchange (Type: direct)
What is an Exchange?
An Exchange is a routing engine that receives messages from a producer (publisher) and determines which queue(s) to send it to, based on the Exchange Type (Direct, Fanout, Topic, Headers), routing key, and binding rules.
- Producers never send directly to queues.
- Producers send messages to exchanges, which route them to queues based on rules (exchange type, bindings, and keys).
Direct Exchange Example:
Direct Exchanges are ideal when you want precise message routing. Messages are delivered to queues only if the routing key exactly matches a queue’s binding key. Use this when each message is intended for a specific queue.
Steps to Create an Exchange in RabbitMQ UI:
- Go to the Exchanges tab.
- Click Add a new exchange.
- Fill in:
- Name: TestExchange
- Type: direct
- Durability: Durable
- Leave other options unchecked.
- Click Add exchange
Explanation of Exchange Fields
- Name: Unique name of the exchange (e.g., TestExchange).
- Type: Defines routing behavior:
- direct: Routes messages to queues with an exact match routing key.
- fanout: Sends to all bound queues, ignores routing key.
- topic: Route using pattern matching (* and # wildcards).
- headers: Routes using header values.
- Durability: Persistent or transient. Durable survives restarts, transient does not
- Auto Delete: Deletes the exchange when no queues are bound to it.
- Internal: Used by RabbitMQ only; not for external publishing. That means, if checked, only other exchanges can publish to this exchange. Normally, keep this unchecked.
- Arguments: Advanced configurations like Alternate Exchanges, TTL, etc. Leave empty unless needed.
What are Features (D), Message Rate In, Message Rate Out?
- Features (D) → Shows if the exchange is durable (D).
- Message rate in → How many messages per second are being published into the exchange?
- Message rate out → How many messages are being routed out to queues?
- Features (I) → The I flag means the exchange is internal.
Step 3: Create a Binding from Exchange → Queue
What is a Binding?
A binding is a rule that connects an exchange to a queue and optionally includes a routing key (for direct/topic exchanges) or a header match (for header exchanges). It’s the rule that defines how messages are routed. Without a binding, messages have nowhere to go.
Steps to Create a Binding in RabbitMQ UI:
Click on the TestExchange, which will open the TestExchange details page.
- Scroll to Bindings
- Under Bind to, fill:
- To Queue: TestQueue
- Routing Key: test
- Click Bind
Binding Parameters:
- To Queue: Which queue to connect (e.g., TestQueue).
- Routing Key: A string used by direct/topic exchanges to match messages (e.g., test).
- Arguments: Usually empty for direct/topic. They matter for header exchanges.
You’ll now see this binding: TestExchange —[test]→ TestQueue. This allows messages published with the routing key test to be delivered to TestQueue. Without binding, the exchange cannot deliver messages to the queue.
Step 4: Publish a Message to the Exchange
What Does “Publish a Message” Mean?
It means sending a message (with optional headers and metadata) to an exchange with a routing key. The exchange routes it to a matching queue based on bindings.
Steps to Publish a Message to an Exchange in RabbitMQ
- On the TestExchange page, scroll down to Publish message
- Fill in:
- Routing Key: test (must match the binding key exactly)
- Payload: e.g., Hello RabbitMQ!
- (Optional) Leave properties as-is.
- Click Publish Message
Fields on the Publish message form
- Routing Key: The label used for routing. With direct exchanges, it must exactly match a binding key (e.g., test).
- Headers: Custom key-value metadata. Mainly used with header exchanges for routing, but also useful for your app logic.
- Properties: Standard Properties examples include
- content_type (application/json, text/plain), content_encoding (gzip, etc.)
- delivery_mode: 1 (non-persistent) or 2 (persistent)
- priority (0–9), expiration (per-message TTL), timestamp, etc.
- Payload: The actual message body.
- Payload Encoding: Indicates to the UI how to display/encode the body; “Auto” is suitable for most tests.
After clicking “Publish message,” the UI confirms, “Message published.”
Step 5: Get the Message from the Queue (Act as Consumer)
In RabbitMQ, the “Get Message” option in the Management UI is a manual way of consuming messages from a queue. Typically, applications (consumers) connect to RabbitMQ and continuously listen for new messages via a subscription. However, in the UI, you don’t have a running consumer; instead, you can manually fetch messages on demand by clicking Get Message(s).
Steps to Read a Message from a Queue:
Go to the Queues and Streams tab → Click TestQueue
Queue Panel Details:
- Type: Queue type (classic, quorum, stream).
- State: Running, or idle.
- Ready: Messages ready for delivery.
- Unacked: Messages already delivered to consumers but not yet acknowledged.
- Total: The sum of Ready and Unacked.
- Message Rates: Publish/Deliver/Ack rates for this queue. These help you spot backlogs (Ready↑) or stuck consumers (Unacked stays high).
Step to Get Messages in RabbitMQ:
Scroll down to Get messages
- Set options:
- Ack mode: Ack message (recommended)
- Number of messages: 1
- Encoding: Auto
- Click Get Message(s)
The Get messages form
- Ack mode:
-
-
- Automatic ack → Message removed instantly.
- Nack requeue true → Message rejected but returned to the queue.
- Reject requeue true → Message rejected, requeued (like nack for one message).
- Reject requeue false → Message rejected, discarded (or sent to DLX).
-
- Number of messages: How many to fetch.
- Encoding: How to display the body.
ACK/NACK Modes in RabbitMQ
ACK (Acknowledge)
- Meaning: “I’ve processed this message successfully, remove it from the queue.”
- Once the ACK is sent, the message is permanently deleted (if no DLX is configured).
- If a consumer crashes before ACK, RabbitMQ will requeue the message (assuming autoAck=false).
NACK (Negative Acknowledge)
- Meaning: “I could not process this message.”
- Can be used with requeue true/false:
- Requeue true: The message is sent back into the queue for redelivery.
- Requeue false: Message is discarded (or forwarded to a DLX if one is configured).
Click Get Message(s) to pull.
You will see the following:
Message Properties
When you click “Get Messages” on a queue, RabbitMQ displays not only the payload but also metadata about the message. Here’s what each field means:
Exchange
- The name of the exchange that routed this message into the queue.
- If the message came via the default exchange (empty string “”), the field shows “”.
- It indicates the location of the message’s origin within the broker.
Routing Key
- The routing key used when publishing the message.
- Determines which queue(s) the message was routed to (for direct/topic exchanges).
- If using fanout exchange, the routing key is ignored but still shown in metadata.
Redelivered
- True → Message was delivered earlier, but the consumer did not acknowledge it. RabbitMQ requeued it and delivered it again.
- False → Message is being delivered for the first time.
Properties / Delivery_Mode
This defines the persistence level of the message:
- 1: Transient
- The message stays in memory only.
- Lost if RabbitMQ restarts or crashes.
- Best for fast, non-critical workloads (e.g., log messages).
- 2: Persistent
- The message is written to disk (if the queue itself is durable).
- Survives broker restarts.
- Best for important tasks like orders, payments, and transactions.
Note on Defaults in the Management UI
When you publish a message through the RabbitMQ Management UI (http://localhost:15672):
- If you don’t explicitly set any message properties, RabbitMQ automatically assigns defaults, and by default, the UI’s publisher sets delivery_mode=2 (persistent).
- That’s why you may see delivery_mode=2 in your test messages even if you didn’t set it manually.
- For production systems, it’s best practice to explicitly set delivery_mode to avoid ambiguity.
Headers
- Key–value pairs of metadata attached during publishing.
- Unlike properties (which are fixed fields), headers are fully custom.
- Often used in header exchanges (routing decisions based on header values).
Durability vs Persistence
In RabbitMQ, durability and persistence work together but are not the same thing:
Durable Queue
- A durable queue means the queue definition (its name, type, arguments) is saved to disk.
- If RabbitMQ restarts, the queue itself still exists.
- But if the messages inside were not marked persistent, they will be lost.
Persistent Message
- A persistent message (set by delivery_mode=2) is written to disk before RabbitMQ confirms it.
- If RabbitMQ restarts, that message can be restored.
- However, if the queue holding it wasn’t durable, the queue itself would not exist after a restart, and the message would be gone too.
Only when both conditions are met can you rely on RabbitMQ to survive crashes/restarts without message loss.
Message Flow Confirmed!
Publisher → TestExchange (direct) → TestQueue → Manual Consumer (Get Message)
If everything worked, RabbitMQ has:
- Stored the message in the queue
- Delivered it when manually requested
- Acknowledged it (removed from the queue)
How to Edit an Exchange or Queue?
In RabbitMQ, Exchanges and Queues cannot be edited once created. This is a design constraint in RabbitMQ’s architecture to ensure message routing stability and consistency.
Pattern-Based Routing using Topic Exchange:
Topic Exchanges are perfect when you want flexible, pattern-based routing using wildcards. This is commonly used for log systems, regional events, or modular architectures, where multiple queues need to subscribe to patterns like log.* or order.#.
Step 1: Create a Topic Exchange
- Go to the Exchanges tab.
- Click Add a new exchange.
- Fill the form:
- Name: PatternExchange
- Type: topic
- Durability: (Durable)
- Leave other checkboxes unchecked.
- Click Add exchange.
A topic exchange routes messages to queues whose binding keys match the routing key pattern.
Step 2: Create Queues
We will create 2 example queues:
Queue 1: ErrorQueue
- Go to the Queues and Streams tab.
- Click Add a new queue.
- Fill:
- Name: ErrorQueue
- Durability: Durable
- Click Add queue.
Queue 2: AllLogsQueue
Repeat the above:
- Name: AllLogsQueue
- Durability: Durable
Step 3: Bind Queues with Patterns to Exchange
Now bind each queue to the PatternExchange with a routing pattern.
Bind ErrorQueue to PatternExchange
- Go to the Exchanges tab → Click PatternExchange.
- Scroll down to Bindings → Under “Bind to”, fill:
- To queue: ErrorQueue
- Routing key: log.error (Exact match)
- Click Bind
This queue will receive only messages with the routing key log.error.
Bind AllLogsQueue with a wildcard pattern
- Stay on PatternExchange
- Again, under “Bind to”, fill:
- To queue: AllLogsQueue
- Routing key: log.* (Wildcard match)
- Click Bind
This queue will receive any message where the routing key starts with log and has one word after it (e.g., log.info, log.error, log.warn).
Step 4: Publish Messages to Test Routing
- On the PatternExchange page → Scroll to Publish Message
- Fill:
- Routing key: log.error
- Payload: Error occurred
- Click Publish Message
Now go to:
- ErrorQueue → Click Get Messages → You will see the message.
- AllLogsQueue → Click Get Messages → You will also see the same message.
The message was routed to both queues because log.error matches both:
- Exact match log.error
- Pattern log.*
Try Another Test:
Routing Key: log.info
Payload: Info log
→Only AllLogsQueue will receive it
→ ErrorQueue will not get it
Note: Delete queues and exchange from their respective tabs once you’re done testing.
Header-Based Routing using Header Exchange:
Headers Exchanges are ideal when routing rules are based on metadata rather than fixed routing keys. Headers enable complex, multi-condition routing logic, making them suitable for large systems where routing decisions depend on message attributes such as app, env, or region.
So, create a Headers Exchange, attach queues with specific header conditions (such as app=mobile), and publish messages with headers to route them accordingly.
Step 1: Create a Headers Exchange
- Go to the Exchanges tab.
- Click Add a new exchange.
- Fill the form:
- Name: HeadersExchange
- Type: headers
- Durability: Durable
- Leave all other checkboxes unchecked
- Click Add exchange
A header exchange routes messages based on message headers, not routing keys.
Step 2: Create Queues
Let’s create 2 example queues.
Queue 1: MobileQueue
- Go to the Queues and Streams tab.
- Click “Add a new queue”
- Fill:
- Name: MobileQueue
- Durability: Durable
- Click Add queue
Queue 2: WebQueue
Repeat:
- Name: WebQueue
- Durability: Durable
Step 3: Bind Queues to Headers Exchange with Header Conditions
Now bind each queue to the HeadersExchange using header matching rules.
Bind MobileQueue with header app=mobile
- Go to the Exchanges tab → Click on HeadersExchange
- Scroll to Bindings
- Under Bind to:
- To Queue: MobileQueue
- Leave the Routing key empty (not used in the headers exchange)
- Click +Add argument
- Key: app → Value: mobile
- Key: x-match → Value: all (case-insensitive match)
- Click Bind
This sets up a rule: Only messages with the header app=mobile will be delivered to MobileQueue.
Bind WebQueue with header app=web
- Repeat the above steps
- For To Queue: select WebQueue
- Arguments:
- app → web
- x-match → all
- Click Bind
Understanding x-match
- all: All headers must match exactly
- any: At least one header must match
Example: If you set x-match = any and headers app=mobile, env=prod, a message with either one will be routed.
Step 4: Publish Messages with Headers
- Go to the HeadersExchange page
- Scroll to Publish message
- Fill:
- Routing Key: Leave blank (ignored by headers exchanges)
- Headers:
- Key: app → Value: mobile
- Payload: Message for mobile app
- Click Publish Message
Step 5: Get Messages from Queues
Go to MobileQueue
- Click Get Messages
- You should see the message
Go to WebQueue
- Click Get Messages
- No message should be present (headers did not match)
Try Another Test
- Publish another message with:
- Header: app=web
- Payload: Message for web app
Only WebQueue will receive it.
Create Fanout Exchange and Attach Queues
Fanout Exchanges are best when you want to broadcast a message to all interested queues. Fanout exchanges ignore routing keys completely and are ideal for real-time alerts, logs, or notification systems where every subscriber should get the message. It ignores routing keys completely and delivers every message to all queues bound to it.
We will create a Fanout Exchange, attach multiple queues, and publish messages. Every bound queue will receive every message published, regardless of the routing key.
Step 1: Create a Fanout Exchange
- Go to the Exchanges tab.
- Click Add a new exchange
- Fill:
- Name: FanoutExchange
- Type: fanout
- Durability: Durable
- Leave all other checkboxes unchecked
- Click Add exchange
Fanout exchanges broadcast messages to all bound queues, ignoring routing keys.
Step 2: Create Queues
We’ll create two queues to test broadcast behavior.
Queue 1: EmailQueue
- Go to the Queues and Streams tab.
- Click Add a new queue
- Fill:
- Name: EmailQueue
- Durability: Durable
- Click Add queue
Queue 2: SMSQueue
Repeat the above:
- Name: SMSQueue
- Durability: Durable
Step 3: Bind Queues to Fanout Exchange
Fanout exchange ignores routing keys, so leave it blank.
Bind EmailQueue to FanoutExchange
- Go to Exchanges → Click on FanoutExchange
- Scroll to Bindings
- Under Bind to:
- To Queue: EmailQueue
- Routing Key: (Leave it empty!)
- Click Bind
Bind SMSQueue to FanoutExchange
Repeat:
- To Queue: SMSQueue
- Routing Key: (Leave empty)
- Click Bind
Now both queues are bound to the FanoutExchange
Step 4: Publish a Message
- Stay on the FanoutExchange page
- Scroll to Publish message
- Fill:
- Routing Key: Leave empty (ignored anyway)
- Payload: e.g., Hello All Subscribers!
- Click Publish Message
You will see: Message published
Step 5: Verify Delivery to Both Queues
Check EmailQueue
- Go to Queues tab → Click EmailQueue
- Click Get Message
- You’ll see the payload
Check SMSQueue
- Repeat the above → You’ll see the same message
All bound queues received the message!
Why Fanout is Useful
Use fanout exchange when:
- You want to broadcast messages to all subscribers.
- You don’t need filtering based on routing keys.
- Useful for logs, notifications, and real-time dashboards.
What Happens to a Message Published to an Exchange with No Bindings?
If the Exchange Has No Bindings (and No Alternate Exchange is configured)
- RabbitMQ drops the message silently.
- It does not go into any queue, because there’s nowhere to deliver it.
- There’s no error returned to the publisher unless you explicitly request it.
If the Publisher Uses the mandatory Flag
- The AMQP protocol has a mandatory flag on publish.
- If you set Mandatory = True when publishing:
- If the message cannot be routed to any queue, RabbitMQ returns it to the publisher via a basic.return callback (instead of silently dropping it).
- If mandatory = false (default), the message is simply dropped with no notice.
The Management UI does not expose the mandatory flag, so in UI testing, you’ll always see the “silent drop” behaviour if no binding exists.
Why This Matters
- Without bindings or an Alternate Exchange (AE), misconfigured or mistyped routing keys cause message loss.
- That’s why an AE or mandatory flag is important in production:
- AE → Automatically reroutes unroutable messages into a “catch-all” dead-letter queue.
- Mandatory Flag → Lets the publisher know a message couldn’t be delivered.
What is AMQP (Advanced Message Queuing Protocol)?
AMQP stands for Advanced Message Queuing Protocol. It is an open standard application-layer protocol for message-oriented middleware. Think of it as the “language” that messaging systems (like RabbitMQ) use to exchange data reliably between producers and consumers.
Example to Understand the Alternate Exchange
An Alternate Exchange (AE) is a fallback route for messages that cannot be delivered because:
- The exchange has no matching bindings, or
- The routing key didn’t match any binding.
Instead of dropping such messages, RabbitMQ forwards them to the AE.
Step 1: Create the Alternate Exchange (AE)
This is the exchange that will receive unroutable messages.
- Go to Exchanges → Click Add a new exchange.
- Fill in:
- Name: UnroutableExchange
- Type: fanout (so it forwards to all bound queues regardless of routing key)
- Durability: Durable
- Click Add exchange.
Step 2: Create a Queue for the Alternate Exchange
- Go to Queues and Streams → Click Add a new queue.
- Fill in:
- Name: UnroutableQueue
- Durability: Durable
- Click Add queue.
- Now bind this queue to UnroutableExchange:
- Go to Exchanges → Click on UnroutableExchange
- Scroll to Bindings
- Bind to UnroutableQueue with Routing key: (leave blank) → Click Bind
Now, any message sent to this exchange will go to the UnroutableQueue.
Step 3: Create a Main Exchange with an Alternate Exchange configured
This is the main exchange with no bindings, so that we can force message routing to fail and watch the fallback.
- Go to Exchanges → Click Add a new exchange.
- Fill in:
- Name: MainExchangeWithAE
- Type: direct
- Durability: Durable
- Arguments:
- Key: alternate-exchange
- Value: UnroutableExchange
- Click Add exchange.
This links MainExchangeWithAE to UnroutableExchange as its alternate exchange.
Step 4: Do not bind any queue to MainExchangeWithAE
We want the message to be unrouteable, so this is intentional.
Step 5: Publish a Message with a Routing Key (that won’t match anything)
- Go to Exchanges → Click on MainExchangeWithAE.
- Scroll down to Publish message.
- Fill in:
- Routing Key: nonexistent.key
- Payload: Hello AE test!
- Click Publish message
The message will be routed to the alternate exchange because MainExchangeWithAE has no bindings, so it can’t route it directly.
Step 6: Fetch the Message from the Alternate Queue
- Go to Queues and Streams → Click UnroutableQueue
- Scroll to Get messages
- Choose:
- Ack mode: Automatic ack
- Number of messages: 1
- Encoding: Auto
- Click Get Message(s)
You will now see the message Hello AE test!, even though the original exchange had no bindings.
Note: RabbitMQ usually drops unroutable messages silently. By using an Alternate Exchange, you can catch and log such messages. It’s extremely useful in production systems to detect misconfigurations, dead queues, or client-side bugs.
Example to Understand Dead Letter Exchanges (DLX)
A Dead Letter Exchange (DLX) is a special exchange that catches messages that cannot be delivered normally. Messages become dead letters in these cases:
- A message is rejected (NACK) and not requeued.
- A message expires (TTL exceeded).
- A queue reaches its max length or max size.
Instead of silently dropping these messages, RabbitMQ can forward them to a DLX for logging, retry, or analysis.
Now, we will set up a main queue that dead-letters into a DLX + DLQ, then trigger DLX in three ways: TTL expiry, explicit reject, and max-length overflow.
- TTL Expiry (messages expire automatically).
- Explicit Reject (reject requeue=false).
- Max-Length Overflow (queue size limit reached)
Step 1: Create a DLX (Dead Letter Exchange)
- Go to the Exchanges tab.
- Click Add a new exchange.
- Fill in:
- Name: DLXExchange
- Type: fanout (so it sends to all bound queues)
- Durability: Durable
- Click Add exchange.
Why fanout? Because we don’t care about routing keys, we just want all dead letters to go somewhere.
Step 2: Create a DLX Queue
- Go to Queues and Streams → Click Add a new queue.
- Fill in:
- Name: DLXQueue
- Durability: Durable
- Click Add queue.
Now bind it:
- Go to Exchanges → click on DLXExchange.
- Scroll to Bindings → Under “Bind to”:
- To Queue: DLXQueue
- Routing Key: (leave empty)
- Click Bind.
Now, any dead-lettered message routed to DLXExchange will land in DLXQueue.
Step 3: Create the Main Exchange
- Go to the Exchanges tab → Add a new exchange.
- Fill in:
- Name: MainExchange
- Type: direct
- Durability: Durable
- Click Add exchange.
This will be our application’s main exchange.
Step 4: Create Main Queue with DLX, TTL, and Max-Length
- Go to Queues and Streams → Add a new queue.
- Fill in:
- Name: MainQueue
- Durability: Durable
- Arguments:
- x-dead-letter-exchange → DLXExchange
- x-message-ttl → 10000 (to auto-expire after 10 sec)
- x-max-length → 3 (queue holds max three messages; excess are dead-lettered)
- Click Add queue.
Now, expired or rejected messages from MainQueue will go to DLXExchange.
Step 5: Bind the Main Queue to the Main Exchange
- Go to Exchanges → Click MainExchange.
- Scroll to Bindings.
- Fill in:
- To Queue: MainQueue
- Routing Key: work.key
- Click Bind.
Messages published with work.key will go into the MainQueue.
Step 6: Publish a Message
- Go to Exchanges → Click MainExchange.
- Scroll to Publish message.
- Fill in:
- Routing Key: work.key
- Payload: Hello DLX with Exchange!
- Click Publish Message.
The message is now in MainQueue.
Step 7: Trigger Dead-Lettering
Option A – Expiration (TTL):
- Wait 10 seconds → the message expires → automatically moves to DLX.
Option B – Reject manually:
- Go to MainQueue → Scroll down → Get Messages.
- Under Ack mode, choose: Reject requeue = false.
- Click Get Message(s).
The message will be dead-lettered and forwarded to DLXExchange.
Step 8: Fetch the Dead-Lettered Message
- Go to Queues and Streams → Click DLXQueue.
- Scroll to Get Messages.
- Select Ack Mode = Automatic ack, Number = 1.
- Click Get Message(s).
You will see Hello DLX with Exchange! inside DLXQueue.
We created a MainExchange → MainQueue for normal flow, and attached a DLXExchange → DLXQueue for handling failures. When a message expired, was rejected, or the queue reached the maximum capacity, RabbitMQ automatically routed it to the DLX.
Creating a New Virtual Host (vhost) in RabbitMQ Management UI
What is a Virtual Host?
A Virtual Host (vhost) is a namespace within RabbitMQ.
- Each vhost has its own queues, exchanges, bindings, users, and permissions.
- Applications connected to different vhosts are completely isolated, like separate databases on the same server.
- The default vhost is / (root).
By default, RabbitMQ comes with one vhost: /. But in real projects, you create separate vhosts for dev, test, prod, or different apps to avoid interference.
Step 1: Open the Admin Tab
- Log in to the RabbitMQ Management UI (http://localhost:15672).
- In the top menu, click Admin.
Step 2: Add a New Virtual Host
- In the left panel under Virtual Hosts, click Add a new virtual host.
- Fill in the form:
- Name: test_vhost (you can pick dev_vhost, prod_vhost, etc.)
- Description: (optional) – e.g., “For development testing”
- Tags / Node: Usually leave the default unless you want an advanced setup.
- Click Add virtual host.
A new vhost is now created.
Step 3: Assign User Permissions
A vhost is empty by default — no user can access it until you assign permissions.
- Go to Admin → Users.
- Click the user you want to grant access to (e.g., a guest or a custom user).
- Scroll to Set Permissions.
- Under Configure permissions for a virtual host:
- Select your vhost (test_vhost).
- Set regex patterns for:
- Configure regexp: .* (means allow configuring any resource).
- Write regexp: .* (allow publishing messages).
- Read regexp: .* (allow consuming messages).
- For simplicity in testing, you can set all three to .* (means full access).
- Click Set permission.
Now the user can create exchanges/queues, publish, and consume in that vhost.
Step 4: Use the New vhost
- When connecting from an application (or Management UI), specify the vhost.
- In the Management UI, select the vhost from the dropdown menu at the top-right corner.
- All exchanges, queues, and bindings you create will now belong only to that vhost.
Step 5: Verify in the UI
- Go to the Queues or Exchanges tab.
- In the top-right corner, select Virtual host = test_vhost.
- You’ll see an empty list (since it’s isolated).
- Add queues/exchanges here without affecting the default / vhost.
RabbitMQ Permissions
Each permission has three parts (all regex):
- Configure – What resources the user can create/modify/delete.
- Write – Where the user can publish messages.
- Read – From where the user can consume messages.
Examples of Permission Patterns
Full Access (everything)
.*
- Matches everything (all queues and exchanges).
- Equivalent to admin access inside that vhost.
Only Queues Starting with a Prefix
^orders.*
- The user can only access queues/exchanges whose names start with orders.
- Example: orders.new, orders.pending, orders_archive.
- Useful for giving access to one module of an app (e.g., Orders service).
Exact Match
^payments_queue$
- Only allows the queue/exchange with the exact name payments_queue.
- Good for tight control.
Allow Multiple Specific Names
^(payments|invoices|refunds).*$
- Matches names starting with ‘payments’, ‘invoices’, or ‘refunds’.
- The user can access only those three categories of resources.
Read-Only Consumer
- Configure: “” (empty regex → no configure access).
- Write: “” (cannot publish).
- Read: .* (can read all queues).
This makes the user a consumer only.
Write-Only Producer
- Configure: “”
- Write: .* (can publish everywhere).
- Read: “” (cannot consume).
This makes the user a publisher only.
No Access
“”
- Means the user has no permission for that action.
What are the Limitations of the Management UI?
While the RabbitMQ Management UI is excellent for learning, debugging, and small-scale testing, it has several limitations that make it unsuitable for production workflows:
Not for Production Workloads
- The UI is designed for manual inspection and testing, not for handling real, continuous workloads.
- The “Get Messages” feature only works as a manual pull and cannot simulate subscription-based consumers (basic.consume).
- You can’t scale or automate consumer/producer actions from the UI.
Limited Publisher Features
- The Publish Message form is basic and intended for simple testing.
- Advanced publishing features such as:
- Publisher Confirms (broker acknowledgment of persistence),
- Transactions,
- High-throughput publishing,
cannot be realistically tested from the UI.
No Real-Time Automation or Load Testing
- You can manually publish messages, but you cannot simulate:
- Multiple concurrent producers/consumers,
- High-volume message publishing,
- Throughput or latency benchmarking.
- For performance testing, you must use client libraries or dedicated tools (PerfTest, custom scripts).
Manual Consumer Actions Only
- The Get Messages option acts like a manual pull consumer.
- You cannot test subscription-based (push) consumption, consumer groups, or advanced error-handling logic.
- This makes the UI unsuitable for validating real consumer workflows.
Cannot Edit Queues/Exchanges
- Once created, queues and exchanges cannot be modified from the UI.
- Properties like type, durability, or arguments can only be changed by deleting and recreating them, which is not practical in production environments.
Security & Access Control Risks
- Anyone with UI access can create or delete queues, exchanges, and bindings.
- While vhosts and user roles provide some control, the UI itself does not prevent accidental destructive actions (e.g., deleting a queue full of messages).
- Exposing the UI over the internet is a security risk unless tightly secured.
Performance Overhead
- Enabling the management plugin adds a small but noticeable performance overhead.
- In high-throughput systems, continuous monitoring via the UI can have a slight impact on RabbitMQ performance.
The Management UI is best suited for learning, debugging, and one-off manual tests. However, for production systems, automation, monitoring, and high-scale message processing must rely on the CLI, APIs, or client libraries.
By completing this end-to-end test using the RabbitMQ Management UI, you now have a clear understanding of the internal flow of messages, from publisher to exchange, from exchange to queue, and finally to the consumer. This no-code visual test illustrates the core RabbitMQ architecture and introduces essential concepts, including routing keys, bindings, acknowledgments, and alternate exchanges. With this foundational knowledge, you are now ready to implement and debug RabbitMQ-based messaging in your own distributed systems or microservices applications.