Spring MultiRabbit is a highly opinionated library that does a multitude of things that relieve you from many burdens:
- It lets you configuratively connect to multiple RabbitMQ brokers, as opposed to the one connection Spring's
RabbitAutoConfiguration
enables. - For each broker it will auto-configure a separate:
- a
ConnectionFactory
- a
RabbitTemplate
- a
RabbitAdmin
- a
RabbitHealthIndicator
- a
RetryTemplate
- a
RabbitListenerContainerFactory
- a
RetryOperationsInterceptor
- a
In addition to that you have the option to configure consumers and producers that are either set up on your RabbitMQ, or that you want to set up there. This lets you automatically create or provide access to:
- a
TopicExchange
- a
HeadersExchange
- a
Queue
- a
Binding
All of the above will be configured with:
- a
MessageConverter
- a
RetryOperationsInterceptor
- and an
ErrorHandler
Everything is declared (or provided to you) without a single line of code. Everything will be wired together, using the routing keys specified by you. This also goes for their deadletter equivalents.
Every one of them will be made available as a Spring @Bean
under a specific name that you can autowire or override, if you need a custom setup.
When you're done you'll be relieved of writing unit or integration tests that check for the existence or proper wiring of Exchanges, Queues and Bindings. The Exchanges, Queues and Bindings will just be available, all due to the configuration you've specified, with no code written from your side.
Add the following line to your build.gradle
:
implementation "com.kuehnenagel.messaging:spring-multirabbit:<CURRENT-VERSION>"
Your Maven pom.xml
will need to say:
<dependency>
<groupId>com.kuehnenagel.messaging</groupId>
<artifactId>spring-multirabbit</artifactId>
<version><CURRENT-VERSION></version>
</dependency>
As initially mentioned Spring MultiRabbit is highly opinionated and catered towards the needs of the environment at Kühne + Nagel AG & Co. KG it was originally conceived in, so some things may not yet be included, such as support for DirectExchange
and FanoutExchange
. It does not claim to be best-of-breed or follow best practices, so do your due diligence and make sure it does what you need. For instance it does not exactly replicate the auto-configuration mechanisms found in RabbitAutoConfiguration
, which we didn't need in our specific environment. Also, as of now only all Queues that are created from the configuration are configured as durable
.
If there's anything you need please create an issue and explain what you think is missing and should be added. Also feel free to contribute and create pull requests.
Add @EnableMultiRabbit
to your @Configuration
and Spring MultiRabbit will kick in. It will read the .yml
configuration, connect to the brokers and (optionally, if specified) create Exchanges, Queues and Bindings and provide access to them via autowireable Spring Beans:
@Configuration
@EnableMultiRabbit
public class MyConfiguration {
...
}
Please be aware that the RabbitAutoConfiguration
is incompatible with this library, as it's expecting single instances of certain Bean types. Hence @EnableMultiRabbit
excludes it via @ImportAutoConfiguration(exclude = RabbitAutoConfiguration.class)
.
So, again, you should be aware that the auto-configured Beans you're familiar with are not created anymore, so there is no rabbitListenerContainerFactory
anymore, but a specific one for each broker, whose name is prefixed with the configured broker name. You now need to take into account, when you're annotating a method with @RabbitListener
, that you will also need to set the containerFactory
parameter to the specific one. You'll find more information on the topic of "specific beans" in the sections below.
The basis for the configuration is a key named amqp
in your .yml
configuration. Every key directly underneath it is considered a RabbitMQ broker, and underneath that comes its configuration.
Here is an example of how to specify multiple brokers to connect to. Please note that the keys starting at rabbitmq:
represent the exact configuration spring-boot-autoconfigure
expects, so you may look into Spring RabbitMQ for more information on the keys and values set there.
amqp:
my-broker-1:
prefetch-count: 250
rabbitmq:
host: some-host-somewhere
port: 5672
virtual-host: vhost1
username: user1
password: secret
ssl:
enabled: false
validate-server-certificate: false
connection-timeout: 5000ms
listener:
simple:
retry:
enabled: true
initial-interval: 1s
max-attempts: 3
max-interval: 10s
multiplier: 2
template:
retry:
enabled: true
initial-interval: 1s
max-attempts: 3
max-interval: 10s
multiplier: 2
my-broker-2:
prefetch-count: 250
rabbitmq:
host: some-other-host-somewhere
port: 5672
virtual-host: vhost2
username: user2
password: secret
ssl:
enabled: false
validate-server-certificate: false
connection-timeout: 5000ms
listener:
simple:
retry:
enabled: true
initial-interval: 3s
max-attempts: 6
max-interval: 10s
multiplier: 2
This configuration specifies two RabbitMQ brokers to connect to: my-broker-1
and my-broker-2
. These are arbitrary names that you may choose yourself, and it's not necessary to number them or follow some form of naming pattern.
Each one of the brokers is configured separately, according to the RabbitProperties
that you're familiar with from Spring RabbitMQ.
The listener
and template
properties will be used for all RetryOperationsInterceptor
and RabbitTemplate
instances that are being created. If you leave them out, and are still in need of retry handling in case of an Exception, you need to specify your own Spring Beans of type RetryOperationsInterceptor
(for RabbitListener
) and RetryTemplate
(for RabbitTemplate
). See below for more information on that.
Once the configuration is read Spring MultiRabbit will register the following Spring Beans that you may inject, or override by using the same (!) name as found in the log files on start up:
For the configuration based on the broker key name my-broker-1
the following Spring Beans will be configured:
- a
myBroker1RabbitConnectionFactory
- a
myBroker1RabbitTemplate
- a
myBroker1RabbitAdmin
- a
myBroker1RabbitHealthIndicator
- a
myBroker1RabbitRetryTemplate
- a
myBroker1RabbitListenerContainerFactory
For the configuration based on the broker key name my-broker-2
:
- a
myBroker2RabbitConnectionFactory
- a
myBroker2RabbitTemplate
- a
myBroker2RabbitAdmin
- a
myBroker2RabbitHealthIndicator
- a
myBroker2RabbitRetryTemplate
- a
myBroker2RabbitListenerContainerFactory
These are regular Spring Beans that you may inject into your code, like this:
@Autowired
private CachingConnectionFactory myBroker1RabbitConnectionFactory;
Please note, that all generated Spring Bean names are sanitized and camelCased, so a broker name of my-broker-1
will be turned into a prefix of myBroker1
, yielding myBroker1RabbitConnectionFactory
, myBroker1RabbitTemplate
etc. as the Spring Bean names.
As with the regular RabbitAutoConfiguration
these Spring Beans are autowired into each other where needed so that e.g. myBroker1RabbitTemplate
and myBroker1RabbitAdmin
are created using the myBroker1RabbitConnectionFactory
, or the myBroker1RabbitHealthIndicator
is created using the myBroker1RabbitTemplate
. There's nothing you need to do here, if this auto-configuration fulfills your requirements.
When the application boots up it will print a bunch of INFO
log messages from the MultiRabbitConfigurer
class that explain the configuration that was created. It will print out Bean names, what was created, and if not why not etc..
You should configure Spring MultiRabbit and run your tests, to see what gets printed out. The log messages make it easier for you to make adjustments to your configuration where needed, then rinse and repeat.
Here is an example of such an output that is extremely helpful when trying to understand what has been created and what's missing.
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitProperties'
...MultiRabbitConfigurer - Did not register bean 'myBroker1RabbitConnectionFactory' as it already exists
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitRetryTemplate'
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitTemplate'
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitAdmin'
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitHealthIndicator'
...MultiRabbitConfigurer - Registered bean 'myBroker1RabbitListenerContainerFactory'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1ExchangeRabbitTemplate'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1Exchange'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1Queue'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1Binding'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1DeadletterExchangeRabbitTemplate'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1DeadletterExchange'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1DeadletterQueue'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1DeadletterBinding'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer1Metadata'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer2ExchangeRabbitTemplate'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer2Exchange'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer2Queue'
...MultiRabbitConfigurer - Registered bean 'myBroker1Consumer2Binding'
...MultiRabbitConfigurer - No deadletter-exchange configured for RabbitMQ configuration 'my-broker-1' and consumer or producer 'consumer2'
...MultiRabbitConfigurer - No deadletter-queue configured for RabbitMQ configuration 'my-broker-1' and consumer or producer 'consumer2'
...MultiRabbitConfigurer - No deadletter-binding created for RabbitMQ configuration 'my-broker-1' and consumer or producer 'consumer2' because of missing exchange configuration
...
...MultiRabbitConfigurer - Found a specific MessageConverter named 'myBroker1MessageConverter'
...MultiRabbitConfigurer - No unique or specific ErrorHandler found, creating a default ErrorHandler of type ConditionalRejectingErrorHandler for connection name 'my-broker-1'
...MultiRabbitConfigurer - Found a specific
10000
RetryOperationsInterceptor named 'myBroker1RetryOperationsInterceptor'
...
...MultiRabbitConfigurer - Configuring RabbitAdmin 'myBroker1RabbitAdmin' with ConnectionFactory 'myBroker1RabbitConnectionFactory' and virtual-host 'vhost1'
...MultiRabbitConfigurer - Declaring queue 'my-first.queue' with RabbitAdmin 'myBroker1RabbitAdmin' and ConnectionFactory 'CachingConnectionFactory [channelCacheSize=25, host=localhost, port=56128, active=true myBroker1RabbitConnectionFactory]'
...MultiRabbitConfigurer - Declaring queue 'my-first.queue.deadletter' with RabbitAdmin 'myBroker1RabbitAdmin' and ConnectionFactory 'CachingConnectionFactory [channelCacheSize=25, host=localhost, port=56128, active=true myBroker1RabbitConnectionFactory]'
On startup the auto-configuration will try to find certain beans that are specific to the broker, in order to inject them into its RabbitListenerContainerFactory
. In the above example, for the broker named my-broker-1
, it will create a myBroker1RabbitListenerContainerFactory
and during configuration it will look for:
- a Bean named
myBroker1ErrorHandler
- if there is none it will try to find exactly one (1) bean of type
ErrorHandler.class
- if none or more than one is found it will create a
ConditionalRejectingErrorHandler
- if none or more than one is found it will create a
- this is the bean you want to override, if you need special error handling on incoming messages
- note: this is not the
errorHandler
you may set on your@RabbitListener
- if there is none it will try to find exactly one (1) bean of type
- a Bean named
myBroker1MessageConverter
- if there is none it will try to find exactly one (1) bean of type
MessageConverter
- if none or more than one is found it will create a
SimpleMessageConverter
- if none or more than one is found it will create a
- this is the bean you want to override, e.g. if you need to convert an incoming JSON message to an object using a
Jackson2JsonMessageConverter
- if there is none it will try to find exactly one (1) bean of type
- a Bean named
myBroker1RetryOperationsInterceptor
- if there is none it will try to find exactly one (1) bean of type
RetryOperationsInterceptor
- if none or more than one is found it will create a stateless
RetryOperationsInterceptor
with aninitialInterval
of1000
, amultiplier
of2.0
, amaxInterval
of10000
andmaxAttempts
set to3
- if none or more than one is found it will create a stateless
- this is the bean you want to override, so when your
RabbitListener
fails during message processing it should try again after a certain amount of time and for a certain number of times, e.g. try again after 1 second, then double the wait time until a maximum of 10 seconds and a maximum pause between each try of 10 seconds (important: make sure you throw anAmqpRejectAndDontRequeueException
if the@RabbitListener
fails for theRetryOperationsInterceptor
to kick in)
- if there is none it will try to find exactly one (1) bean of type
Have a look at the integration tests for examples of the above.
If one of the auto-created Spring Beans does not meet your requirements you may override it and specify your own version of it, by giving the Spring Bean the exact same name the library would have given it.
The following is an excerpt from MultiRabbitTestConfiguration.java
, where we boot up a Docker container and rewire the myBroker1RabbitConnectionFactory
Spring Bean to point at the host and port of the Docker container that was started along with the test. The overriding here is necessary because the Docker port is randomly created and can't be put in the test's .yml
file beforehand.
@Bean
public CachingConnectionFactory myBroker1RabbitConnectionFactory(
final ObjectProvider<ConnectionNameStrategy> connectionNameStrategy,
final RabbitMQContainer rabbitMQContainer) {
final RabbitProperties rabbitProperties = amqpProperties.getAmqp().get("myBroker1").getRabbitmq();
rabbitProperties.setHost(rabbitMQContainer.getContainerIpAddress());
rabbitProperties.setPort(rabbitMQContainer.getMappedPort(rabbitProperties.getPort()));
return rabbitConnectionFactoryCreator.rabbitConnectionFactory(rabbitProperties, connectionNameStrategy);
}
So this example will lead to our very own hand-written myBroker1RabbitConnectionFactory
Spring Bean and not the one Spring MultiRabbit creates for us. At the same time Spring MultiRabbit will use our custom myBroker1RabbitConnectionFactory
for all other Spring Beans it creates, such as the myBroker1RabbitTemplate
or the myBroker1RabbitAdmin
.
Here's a MessageConverter
that is not specific and will therefore be used for all brokers that don't have a specific MessageConverter
bean configured:
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter(new ObjectMapper());
}
Here's a MessageConverter
that is specific for the broker named my-broker-1
, and it will not be used anywhere else but for this broker only:
@Bean
public MessageConverter myBroker1MessageConverter() {
return new Jackson2JsonMessageConverter(new MyMyBroker1SpecificObjectMapper());
}
Here's a MessageConverter
that is specific for the broker named my-broker-1
and the RabbitTemplate
of the exchange named customer-update
, and will not be used anywhere else but for this specific RabbitTemplate
only:
@Bean
public MessageConverter myBroker1CustomerUpdateExchangeMessageConverter() {
return new Jackson2JsonMessageConverter(new MyMyBroker1CustomerUpdateSpecificObjectMapper());
}
Here's a specific RetryOperationsInterceptor
that will only be used on the broker named my-broker-1
:
@Bean
public RetryOperationsInterceptor myBroker1RetryOperationsInterceptor() {
// Specific one for this broker, has precedence over all others
return RetryInterceptorBuilder.stateless().backOffOptions(100, 1.0, 10000).maxAttempts(3).build();
}
Here's an ErrorHandler
used only on the broker named my-broker-2
that will re-throw the exception wrapped in a RuntimeException
:
@Bean
public ErrorHandler myBroker2ErrorHandler() {
return t -> {
throw new RuntimeException(t);
};
}
As mentioned earlier, please keep in mind that this is a Spring ErrorHandler
and not a Spring RabbitMQ RabbitListenerErrorHandler
that you may specify as the errorHandler
parameter of a @RabbitListener
.
While the above handles the connection part you may instruct Spring MultiRabbit to either declare (create) new or "connect" to existing Exchanges and Queues. This is purely optional, and you may choose not to use that feature, if you prefer to configure the Exchanges, Queues and Bindings using the good old Spring ways.
The following configuration layout declares a list of consumers and producers to create:
amqp:
my-broker-1:
rabbitmq:
...
consumers:
consumer1:
...
arbitrary-consumer-name:
...
some-other-consumer-name:
...
producers:
some-producer-name:
...
another-producer:
...
Here is an example of such a configuration, done on the RabbitMQ broker named my-broker-1
:
amqp:
my-broker-1:
rabbitmq:
host: localhost
port: 5672
virtual-host: vhost1
username: user1
password: secret
...
consumers:
consumer1:
exchange:
type: topic # The default type if this attribute is omitted
name: my-first.exchange # Bean name: myBroker1Consumer1Exchange
should-declare: true
routing-key: consumer1.routing-key # Bean name: myBroker1Consumer1ExchangeRabbitTemplate, pre-configured with Exchange name and Routing Key
queue:
name: my-first.queue # Bean name: myBroker1Consumer1Queue
should-declare: true
declare-binding: true # Bean name: myBroker1Consumer1Binding
max-length: 42
max-length-bytes: 16777216
overflow: reject-publish
deadletter-exchange:
type: topic # The default type if this attribute is omitted
name: my-first.exchange.deadletter # Bean name: myBroker1Consumer1DeadletterExchange
should-declare: true
routing-key: consumer1.routing-key.deadletter # Bean name: myBroker1Consumer1DeadletterExchangeRabbitTemplate, pre-configured with Exchange name and Routing Key
deadletter-queue:
name: my-first.queue.deadletter # Bean name: myBroker1Consumer1DeadletterQueue
should-declare: true
declare-binding: true # Bean name: myBroker1Consumer1DeadletterBinding
metadata:
arbitrary-key: arbitrary-value
another-arbitrary-key: another-arbitrary-value
consumer2:
...
This will create a TopicExchange
named my-first.exchange
and store it in the ApplicationContext
as a Spring Bean myBroker1Consumer1Exchange
. If you set should-declare
to false
it will assume the broker already contains an Exchange
named my-first.exchange
and not declare (read: create) it. The TopicExchange
Spring Bean will still end up under the name of myBroker1Consumer1Exchange
in the ApplicationContext
.
Please note, that the type
parameter has been specified as topic
, but could have been omitted. Spring MultiRabbit will fall back to the default, which is topic
.
It will then do the same for the deadletter exchange (created as a TopicExchange
Spring Bean named myBroker1Consumer1DeadletterExchange
), the queue named my-first.queue
(created as a Queue
Spring Bean named myBroker1Consumer1Queue
) and the deadletter queue named my-first.queue.deadletter
(created as a Queue
Spring Bean named myBroker1Consumer1DeadletterQueue
).
All of these will be wired appropriately, and at the end a Binding
will be created that binds the TopicExchange
to the Queue
(same for the deadletter declarations), using the routing key consumer1.routing-key
and consumer1.routing-key.deadletter
. They will be stored as Binding
Spring Beans named myBroker1Consumer1Binding
and myBroker1Consumer1DeadletterBinding
.
The values for max-length
, max-length-bytes
and overflow
will become the Queue
arguments x-max-length
, x-max-length-bytes
and x-overflow
.
As a final step a RabbitTemplate
for each Exchange will be created, named myBroker1Consumer1ExchangeRabbitTemplate
(and myBroker1Consumer1DeadletterExchangeRabbitTemplate
for the deadletter exchange). It will have the Exchange name and Routing Key wired into it, so you can make a simple call of myBroker1Consumer1ExchangeRabbitTemplate.send(message)
and it will end up in the right Exchange with the right Routing Key attached to it.
While creating this Spring Bean Spring MultiRabbit will look for a specific MessageConverter
named myBroker1Consumer1ExchangeMessageConverter
and wire it into myBroker1Consumer1ExchangeRabbitTemplate
. If there is none it will try to find a MessageConverter
specific for the broker that is named myBroker1MessageConverter
, if that one is also not found it will look for exactly one (1) bean of type MessageConverter
and if none, or more than one, is found it will create a SimpleMessageConverter
.
The RetryPolicy
that the RabbitTemplate
is configured with is a SimpleRetryPolicy
for AmqpException
, with maxAttempts
taken from the retry
configuration, and a NeverRetryPolicy
for Exception
. As of now this RetryPolicy
can not be overridden with your own Spring Bean.
As mentioned previously, please refer to the log messages to understand what setup your configuration yields. This makes it easy to track errors and recognize missing keys in the configuration.
In the above example we could have omitted any of the values. Only what is complete will be created, so if there's an exchange:
specified but no queue:
then a TopicExchange
Spring Bean will be created, but no Queue
Spring Bean and therefore also no Binding
.
As of now only exchanges of type TopicExchange
and HeadersExchange
may be created. The library does not support DirectExchange
or FanoutExchange
yet, but you are free to create them manually and, e.g. bind them to an auto-configured Queue
. Spring MultiRabbit does not hinder you from adding anything else you need. It supports you where it can and stays out of your way for any custom configuration you prefer to make in your code.
Here's what the configuration would look like if you needed a HeadersExchange
:
amqp:
my-broker-1:
rabbitmq:
host: localhost
port: 5672
virtual-host: vhost1
username: user1
password: secret
...
consumers:
consumer1:
exchange:
type: headers
name: my-first.exchange # Bean names: myBroker1Consumer1Exchange and myBroker1Consumer1ExchangeRabbitTemplate
should-declare: true
queue:
name: my-first.queue # Bean name: myBroker1Consumer1Queue
should-declare: true
declare-binding: true
max-length: 42
max-length-bytes: 16777216
overflow: reject-publish
bindings: # Create two Bindings between this exchange and queue, one with two arguments and one with just one
- arguments: # Bean name: myBroker1Consumer1Binding0
SomeHeaderKey1: SomeHeaderValue1
SomeHeaderKey2: SomeHeaderValue2
- arguments: # Bean name: myBroker1Consumer1Binding1
AnotherHeaderKey: AnotherHeaderValue
deadletter-exchange:
type: headers
name: my-first.exchange.deadletter # Bean names: myBroker1Consumer1DeadletterExchange and myBroker1Consumer1DeadletterExchangeRabbitTemplate
should-declare: true
deadletter-queue:
name: my-first.queue.deadletter # Bean name: myBroker1Consumer1DeadletterQueue
should-declare: true
declare-binding: true
bindings: # Create two Bindings between this exchange and queue, one with two arguments and one with just one
- arguments: # Bean name: myBroker1Consumer1DeadletterBinding0
SomeHeaderKey1: SomeHeaderValue1
SomeHeaderKey2: SomeHeaderValue2
- arguments: # Bean name: myBroker1Consumer1DeadletterBinding1
AnotherHeaderKey: AnotherHeaderValue
metadata:
arbitrary-key: arbitrary-value
another-arbitrary-key: another-arbitrary-value
consumer2:
...
There is none.
As of now the consumers
and producers
keys are solely defined to configuratively differentiate between consuming and producing parts of the application, but the keys underneath them are identical. This means that both of them may contain specifications for Exchanges, Queues, Routing Keys etc., and technically there is no difference between what is generated from the consumers
configuration values and the producers
configuration values.
Just make sure the key names right underneath consumers
and producers
are unique if (theoretically) put into one list.
Check out the application-rabbitmq_docker.yml
to see a more full-fledged example of such a configuration. It also contains more examples and comments on generated Spring Bean names and what happens in which case, e.g. if a queue name is missing or if a routing key has not been specified.
The key named metadata
may be used if you want to add additional information to the consumer or producer, in order to make use of it in your custom code. What is entered here is provided as a Spring bean named myBroker1Consumer1Metadata
and may be injected as a Map
, using @Qualifier("myBroker1Consumer1Metadata")
(which is kind of redundant, but apparently the only way to inject a Map
, or as a HashMap
without the @Qualifier
(if your Checkstyle configuration permits direct usage of HashMap
as class or instance variables).
The metadata
has no effect whatsoever on the creation of Exchanges, Queues or Bindings and is solely available for your personal usage.
This library is in production at Kühne + Nagel AG & Co. KG (Hamburg) in multiple applications and is processing millions of messages per day in a high performance environment. But as with any library found on the Internet you should run your own tests and make sure your technical requirements are met before putting it into production.
Specify the configuration as in the examples above, but set should-declare
and declare-binding
to false
, so the Exchange, Queue and Binding don't get created.
If you provide all information on an Exchange it will be created, same goes for a Queue (as long as you also specify should-declare: true
on it). If you omit one of them it will not be created, but the other will.
Same for Bindings: they will only be created if you specify an Exchange and a Queue. Obviously, if any one of them is missing a Binding can not be created.
Just skip the configuration sections for consumers
and producers
and all that will be created is a CachingConnectionFactory
for each RabbitMQ instance. You may then proceed as before and create your Exchanges, Queues and Bindings yourself, via your Java code.
As mentioned above Spring MultiRabbit does not yet support a couple of features as there was no need for them at Kühne + Nagel.
Here's a list of things that could be added in the future:
- support for
DirectExchange
- support for
FanoutExchange
- support for message/header properties for
RabbitTemplate
'ssend
operation - non-durable Queues, could be made configurable at the
queue
parameter level - additional parameters on a Queue, such as
ttl
,exclusive
orexpires
- make the
RetryPolicy
configurable as well, like theMessageConverter
Feel free to fork and create a pull request to add your desired features.
You only need a JDK 11, everything else comes with the build.
Build your code with: ./gradlew build
.
Please note that we're expecting 100% code coverage, hence we've specified the additional brokers named code-coverage-rabbitmq-1
and code-coverage-rabbitmq-2
in the application-rabbitmq_docker.yml
. They only make sure every part of the code is run.
You should also run ./gradlew spotlessApply
every now and then as otherwise the build may fail. This will format the code according to our coding style conventions.
In IntelliJ IDEA install the "Eclipse Code Formatter" plugin and import the eclipse_formatter.xml
to have your code formatted according to our code style conventions. While you're there you should also import the project.importorder
settings, so the imports also get formatted appropriately.
In Eclipse there's native support for importing the eclipse_formatter.xml
.
Apache License 2.0
This library was developed by Kühne + Nagel (AG & Co.) KG.
Original Author:
Rias A. Sherzad | @Riyadh