@@ -636,12 +636,12 @@ The following examples shows how to configure a `SimpleRoutingConnectionFactory`
636
636
----
637
637
<bean id="connectionFactory"
638
638
class="org.springframework.amqp.rabbit.connection.SimpleRoutingConnectionFactory">
639
- <property name="targetConnectionFactories">
640
- <map>
641
- <entry key="#{connectionFactory1.virtualHost}" ref="connectionFactory1"/>
642
- <entry key="#{connectionFactory2.virtualHost}" ref="connectionFactory2"/>
643
- </map>
644
- </property>
639
+ <property name="targetConnectionFactories">
640
+ <map>
641
+ <entry key="#{connectionFactory1.virtualHost}" ref="connectionFactory1"/>
642
+ <entry key="#{connectionFactory2.virtualHost}" ref="connectionFactory2"/>
643
+ </map>
644
+ </property>
645
645
</bean>
646
646
647
647
<rabbit:template id="template" connection-factory="connectionFactory" />
@@ -1476,7 +1476,7 @@ public static MessageBuilder fromClonedMessage(Message message) <5>
1476
1476
1477
1477
<1> The message created by the builder has a body that is a direct reference to the argument.
1478
1478
<2> The message created by the builder has a body that is a new array containing a copy of bytes in the argument.
1479
- <3> The message created by the builder has a body that is a new array containing the range of bytes from the argument.
1479
+ <3> The message created by the builder has a body that is a new array containing the range of bytes from the argument.
1480
1480
See https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html[`Arrays.copyOfRange()`] for more details.
1481
1481
<4> The message created by the builder has a body that is a direct reference to the body of the argument.
1482
1482
The argument's properties are copied to a new `MessageProperties` object.
@@ -1554,11 +1554,11 @@ The following listing shows the `BatchingStrategy` interface definition:
1554
1554
----
1555
1555
public interface BatchingStrategy {
1556
1556
1557
- MessageBatch addToBatch(String exchange, String routingKey, Message message);
1557
+ MessageBatch addToBatch(String exchange, String routingKey, Message message);
1558
1558
1559
- Date nextRelease();
1559
+ Date nextRelease();
1560
1560
1561
- Collection<MessageBatch> releaseBatches();
1561
+ Collection<MessageBatch> releaseBatches();
1562
1562
1563
1563
}
1564
1564
----
@@ -1652,22 +1652,22 @@ The following listing shows those method definitions:
1652
1652
[source,java]
1653
1653
----
1654
1654
<R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> callback)
1655
- throws AmqpException;
1655
+ throws AmqpException;
1656
1656
1657
1657
<R, S> boolean receiveAndReply(String queueName, ReceiveAndReplyCallback<R, S> callback)
1658
- throws AmqpException;
1658
+ throws AmqpException;
1659
1659
1660
1660
<R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> callback,
1661
- String replyExchange, String replyRoutingKey) throws AmqpException;
1661
+ String replyExchange, String replyRoutingKey) throws AmqpException;
1662
1662
1663
1663
<R, S> boolean receiveAndReply(String queueName, ReceiveAndReplyCallback<R, S> callback,
1664
- String replyExchange, String replyRoutingKey) throws AmqpException;
1664
+ String replyExchange, String replyRoutingKey) throws AmqpException;
1665
1665
1666
1666
<R, S> boolean receiveAndReply(ReceiveAndReplyCallback<R, S> callback,
1667
- ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException;
1667
+ ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException;
1668
1668
1669
1669
<R, S> boolean receiveAndReply(String queueName, ReceiveAndReplyCallback<R, S> callback,
1670
- ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException;
1670
+ ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException;
1671
1671
----
1672
1672
====
1673
1673
@@ -1801,7 +1801,7 @@ The following listing shows the definition of `FunctionalInterface`:
1801
1801
@FunctionalInterface
1802
1802
public interface ReplyingMessageListener<T, R> {
1803
1803
1804
- R handleMessage(T t);
1804
+ R handleMessage(T t);
1805
1805
1806
1806
}
1807
1807
----
@@ -4284,34 +4284,34 @@ The following listing shows sample client and server configurations:
4284
4284
[source,xml]
4285
4285
----
4286
4286
<bean id="client"
4287
- class="org.springframework.amqp.remoting.client.AmqpProxyFactoryBean">
4288
- <property name="amqpTemplate" ref="template" />
4289
- <property name="serviceInterface" value="foo.ServiceInterface" />
4287
+ class="org.springframework.amqp.remoting.client.AmqpProxyFactoryBean">
4288
+ <property name="amqpTemplate" ref="template" />
4289
+ <property name="serviceInterface" value="foo.ServiceInterface" />
4290
4290
</bean>
4291
4291
4292
4292
<rabbit:connection-factory id="connectionFactory" />
4293
4293
4294
4294
<rabbit:template id="template" connection-factory="connectionFactory" reply-timeout="2000"
4295
- routing-key="remoting.binding" exchange="remoting.exchange" />
4295
+ routing-key="remoting.binding" exchange="remoting.exchange" />
4296
4296
4297
4297
<rabbit:admin connection-factory="connectionFactory" />
4298
4298
4299
4299
<rabbit:queue name="remoting.queue" />
4300
4300
4301
4301
<rabbit:direct-exchange name="remoting.exchange">
4302
- <rabbit:bindings>
4303
- <rabbit:binding queue="remoting.queue" key="remoting.binding" />
4304
- </rabbit:bindings>
4302
+ <rabbit:bindings>
4303
+ <rabbit:binding queue="remoting.queue" key="remoting.binding" />
4304
+ </rabbit:bindings>
4305
4305
</rabbit:direct-exchange>
4306
4306
----
4307
4307
4308
4308
[source,xml]
4309
4309
----
4310
4310
<bean id="listener"
4311
- class="org.springframework.amqp.remoting.service.AmqpInvokerServiceExporter">
4312
- <property name="serviceInterface" value="foo.ServiceInterface" />
4313
- <property name="service" ref="service" />
4314
- <property name="amqpTemplate" ref="template" />
4311
+ class="org.springframework.amqp.remoting.service.AmqpInvokerServiceExporter">
4312
+ <property name="serviceInterface" value="foo.ServiceInterface" />
4313
+ <property name="service" ref="service" />
4314
+ <property name="amqpTemplate" ref="template" />
4315
4315
</bean>
4316
4316
4317
4317
<bean id="service" class="foo.ServiceImpl" />
@@ -4323,7 +4323,7 @@ The following listing shows sample client and server configurations:
4323
4323
<rabbit:queue name="remoting.queue" />
4324
4324
4325
4325
<rabbit:listener-container connection-factory="connectionFactory">
4326
- <rabbit:listener ref="listener" queue-names="remoting.queue" />
4326
+ <rabbit:listener ref="listener" queue-names="remoting.queue" />
4327
4327
</rabbit:listener-container>
4328
4328
----
4329
4329
====
@@ -4790,17 +4790,17 @@ public static class Config {
4790
4790
4791
4791
@Bean
4792
4792
public DirectExchange e1() {
4793
- return new DirectExchange("e1", false, true);
4793
+ return new DirectExchange("e1", false, true);
4794
4794
}
4795
4795
4796
4796
@Bean
4797
4797
public Queue q1() {
4798
- return new Queue("q1", false, false, true);
4798
+ return new Queue("q1", false, false, true);
4799
4799
}
4800
4800
4801
4801
@Bean
4802
4802
public Binding b1() {
4803
- return BindingBuilder.bind(q1()).to(e1()).with("k1");
4803
+ return BindingBuilder.bind(q1()).to(e1()).with("k1");
4804
4804
}
4805
4805
4806
4806
@Bean
@@ -4896,9 +4896,9 @@ The properties are available as attributes in the namespace, as shown in the fol
4896
4896
<rabbit:queue id="notDeclaredByAllExceptAdmin3" auto-declare="false" />
4897
4897
4898
4898
<rabbit:direct-exchange name="direct" declared-by="admin1, admin2">
4899
- <rabbit:bindings>
4900
- <rabbit:binding key="foo" queue="bar"/>
4901
- </rabbit:bindings>
4899
+ <rabbit:bindings>
4900
+ <rabbit:binding key="foo" queue="bar"/>
4901
+ </rabbit:bindings>
4902
4902
</rabbit:direct-exchange>
4903
4903
----
4904
4904
====
@@ -6149,10 +6149,10 @@ The following example shows how to do so:
6149
6149
----
6150
6150
@Bean
6151
6151
public StatefulRetryOperationsInterceptor interceptor() {
6152
- return RetryInterceptorBuilder.stateful()
6153
- .maxAttempts(5)
6154
- .backOffOptions(1000, 2.0, 10000) // initialInterval, multiplier, maxInterval
6155
- .build();
6152
+ return RetryInterceptorBuilder.stateful()
6153
+ .maxAttempts(5)
6154
+ .backOffOptions(1000, 2.0, 10000) // initialInterval, multiplier, maxInterval
6155
+ .build();
6156
6156
}
6157
6157
----
6158
6158
====
@@ -6220,10 +6220,10 @@ The following example shows how to set a `RepublishMessageRecoverer` as the reco
6220
6220
----
6221
6221
@Bean
6222
6222
RetryOperationsInterceptor interceptor() {
6223
- return RetryInterceptorBuilder.stateless()
6224
- .maxAttempts(5)
6225
- .recoverer(new RepublishMessageRecoverer(amqpTemplate(), "something", "somethingelse"))
6226
- .build();
6223
+ return RetryInterceptorBuilder.stateless()
6224
+ .maxAttempts(5)
6225
+ .recoverer(new RepublishMessageRecoverer(amqpTemplate(), "something", "somethingelse"))
6226
+ .build();
6227
6227
}
6228
6228
----
6229
6229
====
@@ -6271,6 +6271,159 @@ When `true`, it travers exception causes until it finds a match or there is no c
6271
6271
6272
6272
To use this classifier for retry, you can use a `SimpleRetryPolicy` created with the constructor that takes the max attempts, the `Map` of `Exception` instances, and the boolean (`traverseCauses`) and inject this policy into the `RetryTemplate`.
6273
6273
6274
+ [[multi-rabbit]]
6275
+ ==== Multiple Broker (or Cluster) Support
6276
+
6277
+ Version 2.3 added more convenience when communicating between a single application and multiple brokers or broker clusters.
6278
+ The main benefit, on the consumer side, is that the infrastructure can automatically associate auto-declared queues with the appropriate broker.
6279
+
6280
+ This is best illustrated with an example:
6281
+
6282
+ ====
6283
+ [source, java]
6284
+ ----
6285
+ @SpringBootApplication(exclude = RabbitAutoConfiguration.class)
6286
+ public class Application {
6287
+
6288
+ public static void main(String[] args) {
6289
+ SpringApplication.run(Application.class, args);
6290
+ }
6291
+
6292
+ @Bean
6293
+ CachingConnectionFactory cf1() {
6294
+ return new CachingConnectionFactory("localhost");
6295
+ }
6296
+
6297
+ @Bean
6298
+ CachingConnectionFactory cf2() {
6299
+ return new CachingConnectionFactory("otherHost");
6300
+ }
6301
+
6302
+ @Bean
6303
+ CachingConnectionFactory cf3() {
6304
+ return new CachingConnectionFactory("thirdHost");
6305
+ }
6306
+
6307
+ @Bean
6308
+ SimpleRoutingConnectionFactory rcf(CachingConnectionFactory cf1,
6309
+ CachingConnectionFactory cf2, CachingConnectionFactory cf3) {
6310
+
6311
+ SimpleRoutingConnectionFactory rcf = new SimpleRoutingConnectionFactory();
6312
+ rcf.setDefaultTargetConnectionFactory(cf1);
6313
+ rcf.setTargetConnectionFactories(Map.of("one", cf1, "two", cf2, "three", cf3));
6314
+ return rcf;
6315
+ }
6316
+
6317
+ @Bean("factory1-admin")
6318
+ RabbitAdmin admin1(CachingConnectionFactory cf1) {
6319
+ return new RabbitAdmin(cf1);
6320
+ }
6321
+
6322
+ @Bean("factory2-admin")
6323
+ RabbitAdmin admin2(CachingConnectionFactory cf2) {
6324
+ return new RabbitAdmin(cf2);
6325
+ }
6326
+
6327
+ @Bean("factory3-admin")
6328
+ RabbitAdmin admin3(CachingConnectionFactory cf3) {
6329
+ return new RabbitAdmin(cf3);
6330
+ }
6331
+
6332
+ @Bean
6333
+ public RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry() {
6334
+ return new RabbitListenerEndpointRegistry();
6335
+ }
6336
+
6337
+ @Bean
6338
+ public RabbitListenerAnnotationBeanPostProcessor postProcessor(RabbitListenerEndpointRegistry registry) {
6339
+ MultiRabbitListenerAnnotationBeanPostProcessor postProcessor
6340
+ = new MultiRabbitListenerAnnotationBeanPostProcessor();
6341
+ postProcessor.setEndpointRegistry(registry);
6342
+ postProcessor.setContainerFactoryBeanName("defaultContainerFactory");
6343
+ return postProcessor;
6344
+ }
6345
+
6346
+ @Bean
6347
+ public SimpleRabbitListenerContainerFactory factory1(CachingConnectionFactory cf1) {
6348
+ SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
6349
+ factory.setConnectionFactory(cf1);
6350
+ return factory;
6351
+ }
6352
+
6353
+ @Bean
6354
+ public SimpleRabbitListenerContainerFactory factory2(CachingConnectionFactory cf2) {
6355
+ SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
6356
+ factory.setConnectionFactory(cf2);
6357
+ return factory;
6358
+ }
6359
+
6360
+ @Bean
6361
+ public SimpleRabbitListenerContainerFactory factory3(CachingConnectionFactory cf3) {
6362
+ SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
6363
+ factory.setConnectionFactory(cf3);
6364
+ return factory;
6365
+ }
6366
+
6367
+ @Bean
6368
+ RabbitTemplate template(RoutingConnectionFactory rcf) {
6369
+ return new RabbitTemplate(rcf);
6370
+ }
6371
+
6372
+ @Bean
6373
+ ConnectionFactoryContextWrapper wrapper(SimpleRoutingConnectionFactory rcf) {
6374
+ return new ConnectionFactoryContextWrapper(rcf);
6375
+ }
6376
+
6377
+ }
6378
+
6379
+ @Component
6380
+ class Listeners {
6381
+
6382
+ @RabbitListener(queuesToDeclare = @Queue("q1"), containerFactory = "factory1")
6383
+ public void listen1(String in) {
6384
+
6385
+ }
6386
+
6387
+ @RabbitListener(queuesToDeclare = @Queue("q2"), containerFactory = "factory2")
6388
+ public void listen2(String in) {
6389
+
6390
+ }
6391
+
6392
+ @RabbitListener(queuesToDeclare = @Queue("q3"), containerFactory = "factory3")
6393
+ public void listen3(String in) {
6394
+
6395
+ }
6396
+
6397
+ }
6398
+ ----
6399
+ ====
6400
+
6401
+ As you can see, we have declared 3 sets of infrastructure (connection factories, admins, container factories).
6402
+ As discussed earlier, `@RabbitListener` can define which container factory to use; in this case, they also use `queuesToDeclare` which causes the queue(s) to be declared on the broker, if it doesn't exist.
6403
+ By naming the `RabbitAdmin` beans with the convention `<container-factory-name>-admin`, the infrastructure is able to determine which admin should declare the queue.
6404
+ This will also work with `bindings = @QueueBinding(...)` whereby the exchange and binding will also be declared.
6405
+ It will NOT work with `queues`, since that expects the queue(s) to already exist.
6406
+
6407
+ On the producer side, a convenient `ConnectionFactoryContextWrapper` class is provided, to make using the `RoutingConnectionFactory` (see <<routing-connection-factory>>) simpler.
6408
+
6409
+ As you can see above, a `SimpleRoutingConnectionFactory` bean has been added with routing keys `one`, `two` and `three`.
6410
+ There is also a `RabbitTemplate` that uses that factory.
6411
+ Here is an example of using that template with the wrapper to route to one of the broker clusters.
6412
+
6413
+ ====
6414
+ [source, java]
6415
+ ----
6416
+ @Bean
6417
+ public ApplicationRunner runner(RabbitTemplate template, ConnectionFactoryContextWrapper wrapper) {
6418
+ return args -> {
6419
+ wrapper.run("one", () -> template.convertAndSend("q1", "toCluster1"));
6420
+ wrapper.run("two", () -> template.convertAndSend("q2", "toCluster2"));
6421
+ wrapper.run("three", () -> template.convertAndSend("q3", "toCluster3"));
6422
+ };
6423
+ }
6424
+ ----
6425
+ ====
6426
+
6274
6427
==== Debugging
6275
6428
6276
6429
Spring AMQP provides extensive logging, especially at the `DEBUG` level.
0 commit comments