Messaging is often implemented with Spring using outdated/deprecated patterns.
This repo attempts to show an easy way to use RabbitMQ using latest best practices of Spring Boot & Spring Cloud Stream.
The Java code is not RabbitMQ specific. It is bound to a RabbitMQ service through configuration (resources/application.yml
) and by declaring the dependency spring-cloud-stream-binder-rabbit
.
This app includes:
- Message consumer
- Message processor (consumes message -> publishes new message)
- Message publisher (using StreamBridge)
- Dead letter queue
- REST endpoint: publishes request parameter value as new message
- Unit testing with BDD Mockito
- Integration testing with Stream test binder
- Use of @Component and constructor based dependency injection
- No XML needed to configure beans!
- No use of
@Autowired
!
- Spring Boot Actuator
- A running RabbitMQ server. (The app will automatically connect to it)
./mvnw clean install
mvnw
is a Maven wrapper included with Spring Initializr.
./mvnw spring-boot:run
To use the message publisher, POST to /text
with the parameter text
.
Example: curl -d "text=hi" -X POST http://localhost:8080/text -v
The POST publishes a message to the exchange consumed by the Function
bean textToTextWrapperProcessor
. This bean is configured for its return value to be published as a message to the exchange which the Consumer
bean wrappedTextConsumer
consumes from.
See readme-images/example-app-log.txt
for an example of running the app with a successful POST receive and messaging, followed by a failed message consumption attempt & dead-lettering.
If an exception is thrown during consumption of a message, and autoBindDlq
is enabled, the message will be consumed and a copy will be published to a dead-letter queue.
This could happen if the bound function throws an exception or Spring fails to marshall the payload into the object type defined in the signature. The following message is not JSON and will thus fail:
This results in the DLQ receiving the failure as a message:
The message:
Enable with: spring.cloud.stream.rabbit.bindings.<channelName>.consumer.autoBindDlq=true
(see resources/application.yml
for example)
- Spring Initializr
- Very convenient tool to quickly create a Spring Boot project.
- Add dependencies
spring-cloud-stream
and a binder such asspring-cloud-stream-binder-rabbit
- Add a Function bean and include it in the configured definitions:
spring.cloud.stream.function.definition: beanName
To bind to a particular exchange & queue:
spring:
cloud:
stream:
function:
definition: beanName
bindings:
beanName-in-0:
# Creates a queue named `text.scsExample` bound to `text` exchange
# (queue name format is "[destination].[group]")
destination: text # name of the AMQP exchange to bind to
group: scsExample
beanName-out-0:
# name of the AMQP exchange to publish to
# Does not create a queue. If no queues exist but you wish to view published
# messages, you can manually create a queue bound to
# this exchange in the RabbitMQ UI.
destination: text.wrapped
- Name exchanges after the type of data that is published to them.
- Avoid the use of environment names in queue or exchange names.
- Environments should be separated by using multiple virtual hosts or multiple actual RabbitMQ servers.
- Consider naming queues by combining the exchange name and the name of the app that declared the queue.
- Example: a
device
exchange consumed by a Device Service app would be nameddevice.device-service
- Example: a
- Official:
- Articles:
- Event Driven Microservices with Spring Cloud Stream and RabbitMQ by Medium (2021)
Any corrections or suggestions are welcome. Please raise a Github issue or contact the owner directly.