This project is a sample Spring Boot application that demonstrates the message redrive pattern (also known as a retry pattern) using RabbitMQ. It includes a mechanism to automatically retry processing a message a few times before moving it to a failed queue for manual inspection.
The application is designed with several key components to achieve the redrive mechanism:
The RabbitMQ setup is configured in RabbitConfig.java and consists of the following:
- Main Exchange (
main-exchange): ADirectExchangethat routes messages to the appropriate queues. - Main Queue (
main-queue): The primary queue where new messages are sent for processing. - Retry Queue (
retry-queue): When processing a message from themain-queuefails, it is sent here. This queue has two important properties:- Time-To-Live (TTL): Messages in this queue have a TTL (e.g., 5 seconds). After the TTL expires, RabbitMQ will automatically move the message.
- Dead-Letter Exchange (DLX): The dead-letter exchange is set to our
main-exchange. When a message's TTL expires, it is "dead-lettered" and sent back to themain-exchangewith its original routing key (main-queue), effectively re-queuing it for another processing attempt.
- Failed Queue (
failed-queue): If a message fails processing more than the maximum number of retry attempts, it is sent to this queue. This isolates problematic messages for later analysis without blocking the main processing flow.
MessageListenerService: A Spring@Servicethat contains a@RabbitListenerfor themain-queue.- It simulates a processing failure for every message it receives.
- It uses a custom message header (
x-retry-count) to track the number of processing attempts. - If the retry count is below the maximum, it increments the count and forwards the message to the
retry-queue. - If the maximum retry count is reached, it moves the message to the
failed-queue.
QueueHealthIndicator: A custom Spring Boot ActuatorHealthIndicator.- It exposes the current message counts for the
main,retry, andfailedqueues via the/actuator/healthendpoint. - If the
failed-queuecontains one or more messages, it reports the application's health status asDOWN.
- It exposes the current message counts for the
TestController: A simple REST controller with an endpoint (POST /test/send) to easily publish a message to themain-queuefor manual testing.
- Java 11 or later
- Maven 3.6 or later
- Docker
Run a RabbitMQ instance with the management plugin enabled using Docker.
docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-managementThis will start RabbitMQ and expose the standard AMQP port on 5672 and the management UI on 15672.
Navigate to the project root and run the application using the Spring Boot Maven plugin:
mvn spring-boot:runThe application will connect to the local RabbitMQ instance.
Open a new terminal and use curl to send a sample JSON message to the TestController's endpoint.
curl -X POST -H "Content-Type: application/json" -d '{"id": "123", "payload": "test-data"}' http://localhost:8080/test/sendWatch the console where the Spring Boot application is running. You will see logs indicating:
- The message is received from the
main-queue. - Processing fails, and the message is sent to the
retry-queue. - After 5 seconds (the TTL), the message is dead-lettered back to the
main-queueand processing is attempted again. - This cycle repeats until the maximum retry count (3) is reached.
- Finally, the message is sent to the
failed-queue.
While the application is running, you can check the custom health status:
curl http://localhost:8080/actuator/healthInitially, the status will be UP. After the message has been moved to the failed-queue, the status will change to DOWN, and the response will show the message counts in all queues.
Open your web browser and navigate to http://localhost:15672.
- Username:
guest - Password:
guest
In the "Queues" tab, you can observe the messages as they move from main-queue to retry-queue and finally land in failed-queue.