Skip to main content

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
const directExchange = ch.assertExchange('direct', 'direct');
const topicExchange = ch.assertExchange('topic', 'topic');
Fanout + Headers Broadcast messages to multiple queues and filter by headers Event broadcasting with metadata filtering
const fanoutExchange = ch.assertExchange('fanout', 'fanout');
const headersExchange = ch.assertExchange('headers', 'headers');
Headers + Direct Route based on message headers and specific routing keys Complex filtering with custom properties
const headersExchange = ch.assertExchange('headers', 'headers');
const directExchange = ch.assertExchange('direct', 'direct');

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

Example: Collecting Metrics via Management Plugin

curl -u guest:guest http://localhost:15672/api/overview

Example: Using Prometheus with RabbitMQ Exporter

scrape_configs:
  - job_name: 'rabbitmq'
    static_configs:
      - targets: ['localhost:9090']

Was this article helpful?