Spring Boot Messaging with RabbitMQ
RabbitMQ is an open-source message broker that allows communication between different services of enterprise applications. It is typically used in microservices to send and receive messages for inter-service communication.
Messaging In RabbitMQ involves:
This image shows how messages are communicated in RabbitMQ.
The Application
We will create a Spring Boot multi-module project in order to perform messaging with RabbitMQ.
The application consists of two services:
To use RabbitMQ in your application, you first need to add the dependency of RabbitMQ in your parent?
pom.xml file.The code to add the RabbitMQ dependency is this:
<dependency
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>>
The application that I will create will contain two Spring Boot services: Producer Service and Consumer Service.
The Producer Service
The Producer Service is a Spring Boot RESTFul service that produces messages to RabbitMQ. In the Producer Service, we will create:
The Domain Object
In the Producer Service application, let’s start by writing a?
User domain class whose objects we will exchange as messages. The code of the?
User class is this.
@Component
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id", scope = User.class)
public class User implements Serializable {
private String userId;
private String userName;
public User(String userId, String userName) {
this.userId = userId;
this.userName = userName;
}
public User() {
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\'' +
", userName='" + userName + '\'' +
'}';
}
}
The?User class implements the Serializable interface as it will be transmitted over a messaging system. It is also annotated with the @Component annotation so that Spring treats it as a Spring-managed object. The?@JsonIdentityInfo annotation indicates the object identity during the serialization and deserialization process.
The application.yml File
Next, we will write the?application.yml file for the Producer Service. Here we will specify the configurations required by an application to connect with RabbitMQ.
app
message: Message has been sent Successfully..
spring:
rabbitmq:
host: localhost
password: guest
port: 15672
username: guest
exchange: user.exchange
queue: user.queue
routingkey: user.routingkey
server:
port: 9091:
The Service Class
We will create a service class named?RabbitMQSender to send messages to RabbitMQ.The code of the?RabbitMQSender class is this.
@Service
public class RabbitMqSender {
private RabbitTemplate rabbitTemplate;
@Autowired
public RabbitMqSender(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
@Value("${spring.rabbitmq.exchange}")
private String exchange;
@Value("${spring.rabbitmq.routingkey}")
private String routingkey;
public void send(User user){
rabbitTemplate.convertAndSend(exchange,routingkey, user);
}
}
This class defines two properties:?exchange?and?routingkey. The?exchange?property defines the RabbitMQ exchange that is responsible for routing the messages to different queues. Then the?routingkey?property defines how to route the messages to the queue depending on the exchange type
Next, line 5-7 autowires an?rabbitTemplate object of the RabbitTemplate class. The RabbitTemplate class allows sending and receiving messages with RabbitMQ.
Then, line 10-14?sets the?exchange and routingkey fields with the values from the application.yml file.
Finally, line 16-17 defines the?send() method that calls the convertAndSend() method of the RabbitTemplate class and sets exchange routing user to it. This convertAndSend() method then pushes the message to exchange with the specified routingkey.
The Controller
Next, we will write the controller named?ProducerController.
The code of the?ProducerController class is this.
@RestController
@RequestMapping(value = "/api/v1/")
public class ProducerController {
private RabbitMqSender rabbitMqSender;
@Autowired
public ProducerController(RabbitMqSender rabbitMqSender) {
this.rabbitMqSender = rabbitMqSender;
}
@Value("${app.message}")
private String message;
@PostMapping(value = "user")
public String publishUserDetails(@RequestBody User user) {
rabbitMqSender.send(user);
return message;
}
}
As you can see that this code uses the?@RestController annotation that makes this class a RESTful web service.
In this code, line 2 adds the?@RequestMapping annotation to map the HTTP requests to handler methods.
Then, line 6-9 autowires a?RabbitMqSender object to send a message to RabbitMQ.
Next, line 12-13 creates a property named?message and then annotated with the @Value annotation to get the values defined in our application.yml file.
Next, line 15-16 creates a?publishUserDetails() method annotated with the @PostMapping annotation to map this handler method to a URL named user with the HTTP POST method .
Finally, line 17?calls the?send() method of the RabbitMqSender class accepts a User object to send it to RabbitMQ.
Refactoring the ProducerServiceApplication Class
Finally, we will refactor the?ProducerServiceApplication class that contains the main() method of the application.
The code of the?ProducerServiceApplication class after refactoring is this.
领英推荐
@SpringBootApplication
public class ProducerServiceApplication {
@Value("${spring.rabbitmq.host}")
String host;
@Value("${spring.rabbitmq.username}")
String username;
@Value("${spring.rabbitmq.password}")
String password;
public static void main(String[] args) {
SpringApplication.run(ProducerServiceApplication.class, args);
}
@Bean
CachingConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host);
cachingConnectionFactory.setUsername(username);
cachingConnectionFactory.setPassword(password);
return cachingConnectionFactory;
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
}
This class defines three properties :?
host, username and password and annotated with the @Value annotation to read the values defined in the application.yml file.
In this code, you can see that line 17-22 creates a?CashingConnectionFactory bean initialized with localhost, username, and password.
Next, line 25-27 creates a?Jackson2JsonMessageConverter bean of type MeaasgeConverter to send the message in the JSON format.
Finally, line 30-34 finally creates a?RabbitTemplate bean initialized with ConnectionFactory. It then calls the setMessageConverter() method and setsJsonMessageConverter() to set the MessageConverter.
The Consumer Service
The Consumer Service is a Spring Boot RESTful service that receives messages from RabbitMQ. Here, in the Consumer Service we will create:
The Configuration Class
In the Consumer Service application, let’s start by writing a configuration class that retrieves values set in the?application.yml file and then configures a message listener, declare the queue, exchange, and then binds them together.
The code of the configuration class named?RabbitMQConfigis this.
@Configuration
public class RabbitMQConfig {
@Value("${spring.rabbitmq.queue}")
private String queue;
@Value("${spring.rabbitmq.exchange}")
private String exchange;
@Value("${spring.rabbitmq.routingkey}")
private String routingKey;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.host}")
private String host;
@Bean
Queue queue() {
return new Queue(queue, true);
}
@Bean
Exchange myExchange() {
return ExchangeBuilder.directExchange(exchange).durable(true).build();
}
@Bean
Binding binding() {
return BindingBuilder
.bind(queue())
.to(myExchange())
.with(routingKey)
.noargs();
}
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host);
cachingConnectionFactory.setUsername(username);
cachingConnectionFactory.setPassword(password);
return cachingConnectionFactory;
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
}
This code defines six properties:?queue, exchange, routingKey, username, password and host. It?also uses the @Value annotation to read the values from the application.yml file.
This code also creates a?Queue bean that is durable.
In this code,?line 27-29 creates an?ExchangeBuilder bean to send a message to the queue whose binding key matches the routing key of the message.
Line 32-38 then creates a?Binding bean by binding the queue, exchange and routingkey.
Next, line 41-46 creates a?CashingConnectionFactory bean of type ConnectionFactory initialized with localhost, username, and password.
Then line 49-51 creates a?Jackson2JsonMessageConverter bean of type MeaasgeConverter to send the message in the JSON format.
Finally, line 54-58 creates a?RabbitTemplate bean initialized with ConnectionFactory. It then calls the setMessageConverter() method and sets JsonMessageConverter() to set the MessageConverter.
The application.yml File
Next, I will write the?application.yml file for the Consumer Service.
The code of the?application.yml file is this.
server:
port: 8080
spring:
rabbitmq:
host: localhost
password: guest
port: 15672
username: guest
exchange: user.exchange
queue: user.queue
routingkey: user.routingkey
The Message Receiver Class
Let’s now write the?RabbitMqService class to receive messages as a Spring component.
The code of the?RabbitMqService class is this.
@Component
public class RabbitMqReceiver implements RabbitListenerConfigurer {
private static final Logger logger = LoggerFactory.getLogger(RabbitMqReceiver.class);
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar rabbitListenerEndpointRegistrar) {
}
@RabbitListener(queues = "${spring.rabbitmq.queue}")
public void receivedMessage(User user) {
logger.info("User Details Received is.. " + user);
}
}
This class implements the?RabbitListenerConfigurer interface that allows defining how listener endpoints are configured.
This class also overrides the configureRabbitListeners() method but without any implementation. Then creates a?receivedMessage() method that receives a User object from RabbitMQ. This method is annotated with the @RabbitListener annotation that defines the queue to consume messages.
Testing Messaging with RabbitMQ
Now, before you run the Producer Service and Consumer Service microservices, you need to start the RabbitMQ server.
To start the RabbitMQ server, in the terminal window type:
service rabbitmq-server start
This command starts the RabbitMQ server and you can also check the status of the server with the following command:
service rabbitmq-server status
The terminal window displays that the RabbitMQ server is already running, as you can see.
Let’s go back to the microservices and run the Producer Service and Consumer Service respectively.
To test the flow of messaging with RabbitMQ, you can use?Postman.
Postman displays a message indicating that the message has been successfully sent, as you can see in the figure.
Now, you can check the Console of the Consumer Service.
In the Console window, you can see the user details that have been received by the Consumer Service.