package com.ankurm.rabbit; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.HeadersExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; /** * The whole topology as beans. {@code RabbitAdmin} declares every {@code Exchange}, * {@code Queue} and {@code Binding} bean when the connection is first opened. * *

Two things about that are worth knowing before you rely on it: *

* * @see docs/02-exchanges.md */ @Configuration(proxyBeanMethods = false) public class Topology { public static final String DIRECT = "orders.direct"; public static final String FANOUT = "orders.fanout"; public static final String TOPIC = "orders.topic"; public static final String HEADERS = "orders.headers"; public static final String DLX = "orders.dlx"; public static final String Q_NEW = "orders.new"; public static final String Q_CANCEL = "orders.cancel"; public static final String Q_AUDIT = "audit.all"; public static final String Q_ANALYTICS = "analytics.all"; public static final String Q_EU = "orders.eu"; public static final String Q_HIGH = "orders.high"; public static final String Q_PRIORITY = "orders.priority"; public static final String Q_ANY = "orders.any"; public static final String Q_WORK = "orders.work"; public static final String Q_TTL = "orders.ttl"; public static final String Q_DLQ = "orders.dlq"; // --- direct: routing key must match the binding key exactly ----------------------------- @Bean DirectExchange directExchange() { return new DirectExchange(DIRECT); } @Bean Queue newQueue() { return QueueBuilder.durable(Q_NEW).build(); } @Bean Queue cancelQueue() { return QueueBuilder.durable(Q_CANCEL).build(); } @Bean Binding bindNew(DirectExchange directExchange, Queue newQueue) { return BindingBuilder.bind(newQueue).to(directExchange).with("new"); } @Bean Binding bindCancel(DirectExchange directExchange, Queue cancelQueue) { return BindingBuilder.bind(cancelQueue).to(directExchange).with("cancel"); } // --- fanout: routing key ignored entirely ----------------------------------------------- @Bean FanoutExchange fanoutExchange() { return new FanoutExchange(FANOUT); } @Bean Queue auditQueue() { return QueueBuilder.durable(Q_AUDIT).build(); } @Bean Queue analyticsQueue() { return QueueBuilder.durable(Q_ANALYTICS).build(); } @Bean Binding bindAudit(FanoutExchange fanoutExchange, Queue auditQueue) { return BindingBuilder.bind(auditQueue).to(fanoutExchange); } @Bean Binding bindAnalytics(FanoutExchange fanoutExchange, Queue analyticsQueue) { return BindingBuilder.bind(analyticsQueue).to(fanoutExchange); } // --- topic: '*' is exactly one word, '#' is zero or more ------------------------------- @Bean TopicExchange topicExchange() { return new TopicExchange(TOPIC); } @Bean Queue euQueue() { return QueueBuilder.durable(Q_EU).build(); } @Bean Queue highQueue() { return QueueBuilder.durable(Q_HIGH).build(); } @Bean Binding bindEu(TopicExchange topicExchange, Queue euQueue) { return BindingBuilder.bind(euQueue).to(topicExchange).with("order.eu.*"); } @Bean Binding bindHigh(TopicExchange topicExchange, Queue highQueue) { return BindingBuilder.bind(highQueue).to(topicExchange).with("order.#.high"); } // --- headers: routing key ignored, header map matched ------------------------------------ @Bean HeadersExchange headersExchange() { return new HeadersExchange(HEADERS); } @Bean Queue priorityQueue() { return QueueBuilder.durable(Q_PRIORITY).build(); } @Bean Queue anyQueue() { return QueueBuilder.durable(Q_ANY).build(); } /** x-match=all: every header must match. */ @Bean Binding bindPriority(HeadersExchange headersExchange, Queue priorityQueue) { return BindingBuilder.bind(priorityQueue).to(headersExchange) .whereAll(Map.of("priority", "high", "region", "eu")).match(); } /** x-match=any: one is enough. */ @Bean Binding bindAny(HeadersExchange headersExchange, Queue anyQueue) { return BindingBuilder.bind(anyQueue).to(headersExchange) .whereAny(Map.of("priority", "high", "region", "eu")).match(); } // --- the dead-letter path ---------------------------------------------------------------- @Bean DirectExchange deadLetterExchange() { return new DirectExchange(DLX); } @Bean Queue deadLetterQueue() { // Deliberately NO dead-letter-exchange on the DLQ itself. Pointing a DLQ's DLX back at // the exchange that feeds it is the classic infinite loop. return QueueBuilder.durable(Q_DLQ).build(); } @Bean Binding bindDlq(DirectExchange deadLetterExchange, Queue deadLetterQueue) { // The routing key used when dead-lettering is the message's ORIGINAL routing key unless // deadLetterRoutingKey() overrides it. Both work queues below set it explicitly to // "failed", so this one binding catches everything. return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange).with("failed"); } @Bean Queue workQueue() { return QueueBuilder.durable(Q_WORK) .deadLetterExchange(DLX) .deadLetterRoutingKey("failed") .build(); } @Bean Queue ttlQueue() { return QueueBuilder.durable(Q_TTL) .ttl(1500) .deadLetterExchange(DLX) .deadLetterRoutingKey("failed") .build(); } }