Simple Pub-Sub implementation with Spring Boot, Docker, and RabbitMQ
Note: In order to follow this tutorial, you should have some basic knowledge in setting up a Spring Boot application and know how to run a Docker container.
First, let’s start with some quick fundamentals about the Publish-Subscribe-Pattern. The pattern is very simple but powerful. The following image shows the main concept:
We have two actors in this pattern:
- Publisher: Asynchronously sends messages to the queue without worrying about who exactly processes the respective message.
- Subscriber: Subscribes to specific topics or a specific queue and consumes incoming messages.
We don’t care who exactly processes the message but that somebody does it. Therefore we make use of acknowledgment. If you have a use-case where you don’t need every message to be processed, you can skip this and go for a fire-and-forget approach.
Acknowledgment in this case means that a subscriber tells the incoming channel that he received the message and processed it. The providing queue therefore no longer needs to send it to other subscribers.
Make sure you have Docker installed. If that is the case just open your terminal and type in the following command:
After the image has been successfully started you will be able to communicate with the RabbitMQ via standard port 5672. To access the RabbitMQ management console visit port 15672. The default user and password is guest for user and also guest as the password.
Publisher Spring Boot Application
To set up our project we will be using spring initializr. Just go for the same setting as I did (see the screenshot below) or use your custom own to fulfill your personal requirements. For this tutorial, we will only need the Spring for RabbitMQ and Spring Web dependency.
Manually adding the dependencies
If you want to use RabbitMQ in an already existing Spring Boot application, for Gradle open up your build.gradle and add the following to your dependencies:
AMQP (Advanced Messaging Query Protocol):
If you are using Maven, add the following to your pom.xml instead:
AMQP (Advanced Messaging Query Protocol):
Note: At the point in time I am writing this tutorial, 2.4.2 is the latest version. There will be probably a newer one available if you are reading this later in time. Just check it.
Adding the configuration
As a real first step for the project, we want to add a decent configuration. Therefore just create a new folder and call it config. Inside of this folder create a new Java file and call it RabbitMQConfig:
As you can see, the configuration is quite simple. We have the identifier QK_EXAMPLE_QUEUE which is not more than a key for referencing the respective queue we are setting up starting from line 28. Next, the Bean for the
RabbitRemplategets defined. It uses the
Jackson2JsonMessageConverterwhich provides basic serialization / deserialization of Java objects.
Like already mentioned, the
Queueobject is initialized with a respective key. We can use this key as a reference to publish or subscribe to this specific queue.
Add a new folder and call it event. Next, add a new Java class and call it DemoEventController. Add the following code:
The controller implements only one REST endpoint “/event”. This endpoints just calculates the current time as a String by the
getTimeNowRepresentation method and combines it with a prefix text to our message to be delivered. We then log this text message to be able to see that the endpoint has been called from our terminal and then call the
RabbitTemplate. The RabbitTemplate provides us various methods to send messages to our queue. In this case, we want to send out
convertAndSendmessage internally converts our transmitted object to an
Messageobject which is then delivered to our queue, referenced by the key we previously defined in our RabbitMQConfig. Instead of a String we therefore also could have used another class with multiple variables for example.
Run the application
Execute the bootRun task. Your application should startup. Assuming you are running the Spring Boot application on port 8080, post the following command into your terminal:
Now open your RabbitMQ management console. Go to the Queues tab and click on the exampleQueue. If the publishing succeeded you now should see the following:
As you can see there is one message ready to be consumed and one in total. But there are zero unacknowledged messages. That’s why be default a “fire and forget” mechanism is implemented where RabbitMQ acknowledges messages as soon as a message gets published into a queue.
To make sure that a message gets processed by a consumer we want to set the queue to manual acknowledgment mode.
Therefore open your application.properties file and add the following setting:
Delete the queue (delete -> purge) in the RabbitMQ management console and restart your spring application.
Subscriber Spring Boot Application
Just clone the Publisher project we just set up. Delete the DemoEventController and add a new class DemoEventConsumer:
We define a method to listen for our queue with the
@RabbitListener tag. Within this tag, we declare our queue identifier. Besides the message that we receive when the method gets invoked, we also get the respective channel and a delivery tag.
Both parameters in combination can be used to manually acknowledge the message. As you can see we first print out the received message. Next, we try to acknowledge the message by using the delivery tag. If the
basicAckcall succeeds the message is set to acknowledged in the RabbitMQ management console. If the method fails and throws an exception the
basicNack method gets called which rejects the message. Nack is the abbreviation for negative acknowledgment.
If you’d set the second parameter of both these methods to true, not only the current but all messages up to and including the delivery tag would get acknowledged.
If you’d comment out the try-catch block, you go into your management console and could see that the sent message is unacknowledged:
If everything works as expected, after calling our endpoint from the Publisher endpoint, you now should see an output in the following format in your Consumer application log:
In this tutorial, I showed you how to set up a simple publisher-subscriber pattern with RabbitMQ, Spring Boot, and Docker.
I hope you learned something new! If you like, write in the comments what kind of applications you already built with this pattern.