RabbitMQ with Spring MVC Micro Service Architecture

Hasitha Amarathunga
5 min readSep 20, 2019

Rabbitmq

RabbitMQ is the most widely deployed open source message broker that can use to communicate data between services of micro service architecture. It’s a queue type message broker and exchange the data using routing key and queue. First you need to download the RabbitMQ server and configure the environment Home and path.

I will show the environment configuration of Windows. RabbitMQ requires a 64-bit supported version of Erlang for Windows to be installed. Erlang releases include a Windows installer. Erlang Solutions provide binary 64-bit builds of Erlang as well.

First you need to configure Erlang. Add erlang installed location bin file (C:\Program Files\erl10.4\bin) to path variable.

Then you need to install RabbitMq server and setup environment Home and path as below.

Rabbitmq Home path
Rabbitmq sbin path

Then you need to start rabbitmq server. Go to task manager services and start rabbitmq as below.

right click and click start

Then get the terminal and run the rabbitmq in localhost 15672 port using following command.

rabbitmq-plugins enable rabbitmq_management

Then go to http://localhost:15672 in browser and login using username = guest and password = guest

Rabbit Overview page

You can create queue with exchange and routing key. But I will create that queue, routing key and exchange using Spring bean configuration.

Spring MVC Project

First you need to create Two Spring MVC project.In my example I will create Customer and Product applications and communicate data between these two application using RabbitMQ. You can get the source code from here. I will use maven to build my Spring MVC applications and add every dependency to pom.xml as below and use mariaDB as my database connection and use hibernate, jacksonJson, RabbiMQ and few more libraries.

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.11.Final</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>5.2.11.Final</version>
</dependency>

<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>1.5.7</version>
</dependency>

<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.7.1.RELEASE</version>
</dependency>

<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.8.0</version>
</dependency>

<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>1.7.10.RELEASE</version>
</dependency>
</dependencies>

This is my property file and configuration

# MariaDB properties
mariadb.url=jdbc:mariadb://localhost:3306/customer
mariadb.username=root
mariadb.password=toor
mariadb.driver=org.mariadb.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create-drop

# Hibernate properties
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

#C3P0 properties
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.acquire_increment=1
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=150

#Rabbitmq properties
rabbitmq.hostName=localhost
rabbitmq.portNumber=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtualHost=/
rabbitmq.exchange=customer.direct
rabbitmq.routingkey=customer.routingkey
rabbitmq.queue=customer.queue

In my application there are few packages like config, controller, service, model and dao.

Following show the project structure.

project structure

AppConfig.java

In here I configure the database connection and hibernate properties and transaction management as below.

@Configuration
@PropertySource("classpath:db.properties")
@EnableTransactionManagement
@ComponentScans(value = { @ComponentScan("com.customer.spring.dao"),
@ComponentScan("com.customer.spring.service")})
public class AppConfig {

@Autowired
Environment env;

@Bean
public LocalSessionFactoryBean getSessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();

Properties props = new Properties();

// Setting JDBC properties
props.put(DRIVER, env.getProperty("mariadb.driver"));
props.put(URL, env.getProperty("mariadb.url"));
props.put(USER, env.getProperty("mariadb.username"));
props.put(PASS, env.getProperty("mariadb.password"));

// Setting Hibernate properties
props.put(SHOW_SQL, env.getProperty("hibernate.show_sql"));
props.put(HBM2DDL_AUTO, env.getProperty("hibernate.hbm2ddl.auto"));
props.put(DIALECT, env.getProperty("hibernate.dialect"));

// Setting C3P0 properties
props.put(C3P0_MIN_SIZE, env.getProperty("hibernate.c3p0.min_size"));
props.put(C3P0_MAX_SIZE, env.getProperty("hibernate.c3p0.max_size"));
props.put(C3P0_ACQUIRE_INCREMENT,
env.getProperty("hibernate.c3p0.acquire_increment"));
props.put(C3P0_TIMEOUT, env.getProperty("hibernate.c3p0.timeout"));
props.put(C3P0_MAX_STATEMENTS, env.getProperty("hibernate.c3p0.max_statements"));

factoryBean.setHibernateProperties(props);
factoryBean.setPackagesToScan("com.customer.spring.model");

return factoryBean;
}

@Bean
public HibernateTransactionManager getTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory().getObject());
return transactionManager;
}
}

MqConfig.java

I added RabbitMq bean Configuration in this file. You need to Add configuration and EnableRabbit annotations as below.

@Configuration
@EnableRabbit
public class MqConfig {

This is RabbitMQ Connection configurations.

@Autowired
private Environment environment;
@Bean
public ConnectionFactory connectionFactory()
{
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setUsername( environment.getProperty( "rabbitmq.username" ) );
connectionFactory.setPassword( environment.getProperty( "rabbitmq.password" ) );
connectionFactory.setVirtualHost( environment.getProperty( "rabbitmq.virtualHost" ) );
connectionFactory.setHost( environment.getProperty( "rabbitmq.hostName" ) );
connectionFactory.setPort( Integer.parseInt( environment.getProperty( "rabbitmq.portNumber" ) ) );
return connectionFactory;
}
@Bean
public AmqpAdmin amqpAdmin()
{
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory() );
rabbitAdmin.setIgnoreDeclarationExceptions(true);
return rabbitAdmin;
}

@Bean
public RabbitTemplate rabbitTemplate()
{
RabbitTemplate rabbitTemplate = new RabbitTemplate( connectionFactory() );
rabbitTemplate.setMessageConverter( jsonMessageConverter() );
return rabbitTemplate;
}

@Bean(name = "rabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory listenerFactory()
{
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory( connectionFactory() );
factory.setMessageConverter( jsonMessageConverter() );


factory.setConcurrentConsumers(3);
factory.setMaxConcurrentConsumers(10);
return factory;
}
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter()
{
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
return new Jackson2JsonMessageConverter( mapper );
}

Here is Queue, exchange and routing key creation using bean

@Bean
public Queue queue()
{
return new Queue( environment.getProperty( "rabbitmq.queue" ) );
}

@Bean
public DirectExchange directExchange()
{
return new DirectExchange(environment.getProperty( "rabbitmq.exchange" ));
}

@Bean
public Binding binding()
{
return BindingBuilder.bind(queue()).to(directExchange())
.with(environment.getProperty( "rabbitmq.routingkey" ));
}

MyWebAppInitializer.java

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class , MqConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { com.customer.spring.config.WebConfig.class };
}

@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

WebConfig.java

This is the webmvc configuration using EnableWebMvc annotation.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.customer.spring.controller"})
public class WebConfig extends WebMvcConfigurerAdapter {

}

I have implemented all the CRUD operations for that Customer Application. You can see it in source code. I will show the inter-module communication using RabbiMQ.

The concept is simple. Customer app send the Customer’s productId to product app using RabbiMQ routing key and exchange as below. RabbitMQ provide AmqpTemplate to do that. Get the routingKey and exchange from property file. You can define any names for that in your property file. It will automatically create that queue, routing key and exchange using the above mentioned bean configurations.

@Service
@Transactional(readOnly = true)
public class ProducerServiceImp implements ProducerService {
@Autowired
private AmqpTemplate amqpTemplate;

@Value( "${rabbitmq.exchange}" )
private String exchange;

@Value( "${rabbitmq.routingkey}" )
private String routingkey;

@Override
public Object sendMsg(Long proId) throws Exception{
Object response = amqpTemplate.convertSendAndReceive(exchange,routingkey,proId);
System.out.println("============== Response ==================");
System.out.println(response);
System.out.println("==========================================");
return response;
}
}

In here convertSendAndRecieve() method can send the request and wait to the response. The RabbitListner of other application(Product application) should return some data in that listener function. You can see the RabbitListner using queue of Product application below.

@Override
@RabbitListener(queues = "${rabbitmq.queue}")
public Object consumerMessage(Long proId) throws AmqpIOException {
System.out.println("=============== Message ==================");
System.out.println(proId);
System.out.println("==========================================");
Product product=productService.getProduct(proId);
if(product==null){
return null;
}
else{
ObjectMapper obj = new ObjectMapper();
try {
String pro = obj.writeValueAsString(product);
return pro;
}catch(JsonProcessingException e){
return null;
}
}
}

Notes : You need to configure all the dependencies and properties also to the product application.

Then the producerService response object assign the response of consumerMessage service returned.

So this is about the inter-module communication using Sprint MVC + RabbitMQ in micro service architecture.

You can get the source code from here.

--

--

Hasitha Amarathunga

Graduated Computer Science Student at University Of Colombo School of Computing & Still working as Specialist - Senior Software Engineer at Scicom Msc Bhd.