RabbitMQ routing is defined by exchange type, bindings, and queue properties. Understanding these is essential for designing reliable messaging topologies.

Direct Exchange

Routes messages to queues whose binding key exactly matches the routing key.

  channel.exchangeDeclare("orders.direct", BuiltinExchangeType.DIRECT, true);
channel.queueDeclare("orders.created", true, false, false, null);
channel.queueDeclare("orders.shipped", true, false, false, null);
channel.queueBind("orders.created", "orders.direct", "order.created");
channel.queueBind("orders.shipped", "orders.direct", "order.shipped");

channel.basicPublish("orders.direct", "order.created", null, message.getBytes());
  

Fanout Exchange

Broadcasts to all bound queues, ignoring routing keys.

  channel.exchangeDeclare("notifications.fanout", BuiltinExchangeType.FANOUT, true);
channel.queueBind("email.queue", "notifications.fanout", "");
channel.queueBind("sms.queue", "notifications.fanout", "");
channel.queueBind("push.queue", "notifications.fanout", "");

// All three queues receive the message
channel.basicPublish("notifications.fanout", "", null, message.getBytes());
  

Topic Exchange

Routes based on wildcard pattern matching.

  Routing key pattern:
  *  — matches exactly one word
  #  — matches zero or more words

Binding key "order.*"     matches "order.created", "order.shipped"
Binding key "order.#"     matches "order.created", "order.us.created"
Binding key "#.error"     matches "payment.error", "order.us.error"
  
  channel.exchangeDeclare("events.topic", BuiltinExchangeType.TOPIC, true);
channel.queueBind("us.orders", "events.topic", "order.us.*");
channel.queueBind("all.errors", "events.topic", "*.error");
channel.queueBind("everything", "events.topic", "#");
  

Queue Properties

  Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000);           // message TTL: 60s
args.put("x-max-length", 10000);             // max queue size
args.put("x-dead-letter-exchange", "dlx");   // dead letter exchange
args.put("x-dead-letter-routing-key", "failed");

channel.queueDeclare("orders.queue", true, false, false, args);
  
Property Description
durable Survive broker restart
exclusive Only this connection can use
autoDelete Delete when no consumers
x-message-ttl Message expiration
x-max-length Max messages in queue

Dead Letter Exchange (DLX)

Handle failed or expired messages:

  // Main queue with DLX
Map<String, Object> args = Map.of(
    "x-dead-letter-exchange", "orders.dlx",
    "x-dead-letter-routing-key", "order.failed"
);
channel.queueDeclare("orders.queue", true, false, false, args);
channel.queueDeclare("orders.failed", true, false, false, null);
channel.queueBind("orders.failed", "orders.dlx", "order.failed");
  

Spring Configuration

  @Configuration
public class RabbitConfig {
    @Bean
    public TopicExchange ordersExchange() {
        return new TopicExchange("orders.topic", true, false);
    }

    @Bean
    public Queue ordersCreatedQueue() {
        return QueueBuilder.durable("orders.created")
            .withArgument("x-dead-letter-exchange", "orders.dlx")
            .build();
    }

    @Bean
    public Binding binding(Queue ordersCreatedQueue, TopicExchange ordersExchange) {
        return BindingBuilder.bind(ordersCreatedQueue)
            .to(ordersExchange).with("order.created");
    }
}
  

Best Practices

  • Use topic exchanges for flexible routing in event-driven systems
  • Always configure dead letter exchanges for production queues
  • Set message TTL to prevent stale messages accumulating
  • Use durable exchanges and queues unless ephemeral data is intended
  • Design queue names to reflect their purpose (orders.created, payments.failed)