Implementing Routing Pattern
1. Creating Direct Exchange
| Property | Value | Description |
|---|---|---|
| exchange.name | direct_exchange |
Name of the direct exchange to be created |
| exchange.type | direct |
Exchange type for direct routing |
| exchange.durable | true |
Whether the exchange survives broker restarts |
| exchange.autoDelete | false |
Whether the exchange is deleted when no longer in use |
Example: Creating a Direct Exchange in RabbitMQ
const amqp = require('amqplib');
async function createDirectExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchangeName = 'direct_exchange';
const exchangeType = 'direct';
await channel.assertExchange(exchangeName, exchangeType, { durable: true });
console.log(`Direct exchange '${exchangeName}' created successfully.`);
await channel.close();
await connection.close();
}
createDirectExchange().catch(console.error);
2. Using Routing Keys
| Property | Description | Example |
|---|---|---|
| routing_key | Used to route messages to queues based on a match between the message's routing key and the binding key. | info |
| binding_key | Defines the pattern used to bind a queue to an exchange for routing. | #.error |
Example: Publishing a Message with Routing Key
const amqp = require('amqplib');
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchangeName = 'logs';
const routingKey = 'error';
await channel.assertExchange(exchangeName, 'topic', { durable: false });
channel.publish(exchangeName, routingKey, Buffer.from('Error log message'));
channel.close();
connection.close();
Example: Binding Queue with Routing Key Pattern
const queueName = 'error_logs';
const bindingKey = '#.error';
await channel.assertQueue(queueName, { durable: true });
await channel.bindQueue(queueName, exchangeName, bindingKey);
Note: The
# wildcard matches zero or more words, and * matches exactly one word in routing keys.
3. Binding with Multiple Keys
| Key | Binding Pattern | Description |
|---|---|---|
| routing.key1 | routing.key1 |
Binds to messages with the exact routing key routing.key1 |
| routing.* | routing.* |
Binds to messages with routing keys that start with routing. followed by any single word |
| routing.# | routing.# |
Binds to messages with routing keys that start with routing. followed by any sequence of words |
Example: Binding Multiple Keys to a Queue
#!/bin/bash
# Create exchange
rabbitmqadmin declare exchange name=topic_logs type=topic
# Bind queue to multiple keys
rabbitmqadmin declare queue name=error_queue durable=true
rabbitmqadmin bind exchange=topic_logs queue=error_queue routing_key="*.error"
rabbitmqadmin bind exchange=topic_logs queue=error_queue routing_key="*.critical"
Note: The
# wildcard matches zero or more words, while * matches exactly one word.
4. Implementing Multi-Criteria Routing
Example: Direct Exchange with Multiple Bindings
const amqp = require('amqplib');
async function setupDirectExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Declare a direct exchange
await channel.assertExchange('multi-criteria-exchange', 'direct', { durable: true });
// Bind multiple routing keys to the same queue
await channel.bindQueue('user-queue', 'multi-criteria-exchange', 'user.created');
await channel.bindQueue('user-queue', 'multi-criteria-exchange', 'user.updated');
await channel.bindQueue('user-queue', 'multi-criteria-exchange', 'user.deleted');
// Bind another queue to different criteria
await channel.bindQueue('admin-queue', 'multi-criteria-exchange', 'admin.access');
await channel.bindQueue('admin-queue', 'multi-criteria-exchange', 'admin.config');
console.log('Exchange and bindings set up');
}
| Routing Key | Queue | Description |
|---|---|---|
| user.created | user-queue |
Handles user creation events |
| user.updated | user-queue |
Handles user update events |
| user.deleted | user-queue |
Handles user deletion events |
| admin.access | admin-queue |
Handles admin access logs |
| admin.config | admin-queue |
Handles admin configuration changes |
Note: The
user-queue receives messages for all user-related events, while admin-queue handles only admin-specific messages.
Example: Topic Exchange for Flexible Routing
const amqp = require('amqplib');
async function setupTopicExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Declare a topic exchange
await channel.assertExchange('topic-exchange', 'topic', { durable: true });
// Bind with wildcards for flexible routing
await channel.bindQueue('user-queue', 'topic-exchange', 'user.*');
await channel.bindQueue('admin-queue', 'topic-exchange', 'admin.*');
// Specific binding for user creation
await channel.bindQueue('user-created-queue', 'topic-exchange', 'user.created');
console.log('Topic exchange and bindings set up');
}
| Routing Key Pattern | Queue | Description |
|---|---|---|
| user.* | user-queue |
Captures all user-related messages |
| admin.* | admin-queue |
Captures all admin-related messages |
| user.created | user-created-queue |
Specifically captures user creation events |
Multi-Criteria Routing Summary
- Direct Exchange: Best for exact routing based on a single routing key.
- Topic Exchange: Best for flexible routing using pattern matching with wildcards.
- Queue Bindings: Multiple routing keys can bind to the same queue for multi-criteria matching.
- Wildcards:
*matches one word,#matches zero or more words in topic exchanges.
5. Using Topic Exchange
| Exchange Name | Type | Description |
|---|---|---|
| topic | topic |
Matches messages based on routing keys using wildcards. |
Example: Publishing to Topic Exchange
const amqp = require('amqplib');
async function publishToTopic() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchangeName = 'logs';
await channel.assertExchange(exchangeName, 'topic', { durable: false });
// Publish with routing key
channel.publish(exchangeName, 'error.warning', Buffer.from('Warning log'));
await channel.close();
await connection.close();
}
publishToTopic();
Example: Binding with Wildcards
const amqp = require('amqplib');
async function bindWithWildcards() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchangeName = 'logs';
const queueName = 'error_queue';
await channel.assertExchange(exchangeName, 'topic', { durable: false });
await channel.assertQueue(queueName, { durable: false });
// Bind with wildcard pattern
await channel.bindQueue(queueName, exchangeName, 'error.*');
await channel.close();
await connection.close();
}
bindWithWildcards();
Topic Exchange Rules
#matches zero or more words*matches exactly one word- Routing keys are dot-separated words (e.g.,
error.server.app)
Note: Topic exchanges are ideal for routing messages to multiple consumers based on patterns in the routing key.
6. Understanding Wildcard Routing
Wildcard routing in RabbitMQ allows for flexible message routing using pattern matching on routing keys. It's particularly useful when you need to route messages based on partial matches of the routing key.
| Pattern | Description | Example Match |
|---|---|---|
| * | Matches exactly one word in the routing key | stock.usd matches stock.* |
| # | Matches zero or more words in the routing key | stock.usd.eur matches stock.# |
Example: Setting up wildcard exchange
const amqp = require('amqplib');
async function setupWildcardExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Declare a topic exchange with wildcard routing
await channel.assertExchange('topic_logs', 'topic', { durable: false });
// Bind queue with wildcard pattern
await channel.bindQueue('queue_name', 'topic_logs', 'stock.*');
console.log('Wildcard exchange and binding created');
}
setupWildcardExchange().catch(console.error);
Example: Publishing messages with wildcard matching
const amqp = require('amqplib');
async function publishMessages() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Publish messages with different routing keys
channel.publish('topic_logs', 'stock.usd', Buffer.from('USD price update'));
channel.publish('topic_logs', 'stock.eur', Buffer.from('EUR price update'));
channel.publish('topic_logs', 'stock.usd.eur', Buffer.from('USD to EUR conversion'));
console.log('Messages published');
}
publishMessages().catch(console.error);
Note: The
# wildcard can match zero or more words, making it very flexible for complex routing scenarios.
Warning: Using
# at the beginning of a pattern (e.g., #.usd) can lead to performance issues as it matches all routing keys.
7. Combining Routing Patterns
Routing Pattern Combinations
- Direct exchange with topic exchange for flexible message routing
- Fanout and headers for broadcast and header-based routing
- Headers exchange with direct for advanced filtering
| Pattern | Description | Use Case | Example |
|---|---|---|---|
| Direct + Topic | Route messages to specific queues based on routing keys and patterns | Microservices with multiple message types | |
| Fanout + Headers | Broadcast messages to multiple queues and filter by headers | Event broadcasting with metadata filtering | |
| Headers + Direct | Route based on message headers and specific routing keys | Complex filtering with custom properties | |
Example: Combining Direct and Topic Exchanges
const amqp = require('amqplib');
async function setupRouting() {
const conn = await amqp.connect('amqp://localhost');
const ch = await conn.createChannel();
// Setup direct exchange
await ch.assertExchange('direct', 'direct');
await ch.bindExchange('topic', 'direct', 'user.*');
// Setup topic exchange
await ch.assertExchange('topic', 'topic');
await ch.bindExchange('direct', 'topic', 'user.*');
// Bind queues to exchanges
await ch.assertQueue('user.queue');
await ch.bindQueue('user.queue', 'direct', 'user.login');
await ch.assertQueue('notification.queue');
await ch.bindQueue('notification.queue', 'topic', 'user.*');
console.log('Routing setup complete');
}
Example: Fanout and Headers Exchange Combination
const amqp = require('amqplib');
async function setupFanoutHeaders() {
const conn = await amqp.connect('amqp://localhost');
const ch = await conn.createChannel();
// Setup fanout exchange
await ch.assertExchange('fanout', 'fanout');
await ch.assertQueue('broadcast.queue');
await ch.bindQueue('broadcast.queue', 'fanout', '');
// Setup headers exchange
await ch.assertExchange('headers', 'headers');
await ch.assertQueue('filtered.queue');
await ch.bindQueue('filtered.queue', 'headers', '', {
'x-match': 'all',
'priority': 'high'
});
console.log('Fanout + Headers setup complete');
}
8. Implementing Conditional Routing
Conditional routing in RabbitMQ allows messages to be routed based on specific conditions or criteria, often using headers or message properties. This is typically achieved through the use of headers exchanges.
| Exchange Type | Description | Use Case |
|---|---|---|
| headers | Routes messages based on message headers matching specific criteria. | Advanced routing logic where routing depends on message metadata. |
Example: Setting up a Headers Exchange with Conditional Routing
const amqp = require('amqplib');
async function setupHeadersExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Declare a headers exchange
await channel.assertExchange('my-headers-exchange', 'headers', { durable: true });
// Bind queue with conditional header matching
await channel.bindQueue('my-queue', 'my-headers-exchange', '', {
'x-match': 'all', // or 'any'
priority: 'high',
type: 'notification'
});
console.log('Headers exchange and binding set up');
}
setupHeadersExchange().catch(console.error);
Note: The
x-match header can be set to 'all' or 'any' to control whether all or any of the header conditions must match for routing.
Example: Publishing a Message with Headers for Conditional Routing
const amqp = require('amqplib');
async function publishWithHeaders() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const message = {
text: 'This is a notification',
priority: 'high',
type: 'notification'
};
// Publish with headers
channel.publish('my-headers-exchange', '', Buffer.from(JSON.stringify(message)), {
headers: {
priority: 'high',
type: 'notification'
}
});
console.log('Message published with headers');
}
publishWithHeaders().catch(console.error);
9. Using Default Exchange
| Property | Description | Default Value |
|---|---|---|
| exchange.name | Name of the default exchange | "" (empty string) |
| exchange.type | Type of the default exchange | direct |
| exchange.durable | Whether the exchange is durable | false |
| exchange.auto_delete | Whether the exchange auto-deletes when no longer in use | true |
Example: Publishing to Default Exchange
const amqp = require('amqplib');
async function publishToDefaultExchange() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Publishing to the default exchange
const message = 'Hello, RabbitMQ!';
channel.publish('', 'queue-name', Buffer.from(message));
console.log('Message published to default exchange');
await channel.close();
await connection.close();
}
publishToDefaultExchange().catch(console.error);
Note: The default exchange is a special direct exchange that has no name and is automatically bound to every queue with the routing key equal to the queue name.
10. Monitoring Routing Metrics
| Metric | Description | Unit | Collection Method |
|---|---|---|---|
| messages_published | Total number of messages published to the broker | count | Broker statistics |
| messages_delivered | Number of messages delivered to consumers | count | Broker statistics |
| messages_acknowledged | Number of messages acknowledged by consumers | count | Broker statistics |
| messages_unroutable | Number of messages that could not be routed to any queue | count | Broker statistics |
| channel_count | Number of active channels | count | Broker statistics |
| connection_count | Number of active connections | count | Broker statistics |