Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Event-Driven Microservices With Spring Boot and ActiveMQ

DZone 's Guide to

Event-Driven Microservices With Spring Boot and ActiveMQ

A guide to using Spring Boot and ActiveMQ for event-driven microservices.

· Microservices Zone ·
Free Resource

Most communications between microservices are either via HTTP request-response APIs or asynchronous messaging. While these two mechanisms are most commonly used, yet they’re quite different. It is important to know when to use which mechanism.

Event-driven communication is important when propagating changes across several microservices and their related domain models. This means that when changes occur, we need some way to coordinate changes across the different models. This ensures reliable communication as well as loose coupling between microservices.

There are multiple patterns to achieve event-driven architecture. One of the common and popular patterns is the messaging pattern. It is extremely scalable, flexible, and guarantees delivery of messages. There are several tools that can be used for messaging pattern such as RabbitMQ, ActiveMQ, Apache Kafka and so on.

Messaging Pattern

In this article, we are going to build microservices using Spring Boot and we will set up the ActiveMQ message broker to communicate between microservices asynchronously.

Building Microservices

Let us create two Spring Boot projects ‘ activemq-sender ’ and ‘ activemq-receiver ’. Here is the sample project structure.

                                                                     Sample Project Structure

We need to add maven dependency spring-boot-starter-activemq to enable ActiveMQ. Here is a sample pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.techshard.activemqsender</groupId>
    <artifactId>activemq-sender</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>
    </dependencies>

</project>

Configuring Publisher

In the project activemq-sender, we will first configure a queue. Create a JmsConfig class as follows.

package com.techshard.activemq.configuration;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;

import javax.jms.Queue;

@Configuration
public class JmsConfig {

    @Bean
    public Queue queue(){
        return new ActiveMQQueue("test-queue");
    }
}

The above class just declares a bean Queue and our queue name would be test-queue. Note: queue names can also be read from application properties. This is just an example.

Now, let’s create a REST API which will be used to publish the message to the queue.

package com.techshard.activemq.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.jms.Queue;

@RestController
@RequestMapping("/api")
public class MessageController {

    @Autowired
    private Queue queue;

    @Autowired
    private JmsTemplate jmsTemplate;

    @GetMapping("message/{message}")
    public ResponseEntity<String> publish(@PathVariable("message") final String message){
        jmsTemplate.convertAndSend(queue, message);
        return new ResponseEntity(message, HttpStatus.OK);
    }

}

In the controller, we will inject the bean Queue which we declared before and we will also inject  JmsTemplate.

To send or receive messages through JMS, we need to establish a connection to the JMS provider and obtain a session.  JmsTemplate is a helper class that simplifies sending and receiving of messages through JMS and gets rid of boilerplate code.

We have now created a simple API endpoint that will accept a string as a parameter and puts it in the queue.

Configuring Consumer

In the project  activemq-receiver, create a component class as follows:

package com.techshard.activemq.consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
@EnableJms
public class MessageConsumer {

    private final Logger logger = LoggerFactory.getLogger(MessageConsumer.class);

    @JmsListener(destination = "test-queue")
    public void listener(String message){
        logger.info("Message received {} ", message);
    }
}

In this class, we have an annotated method with  @JmsListenerand we have passed the queue name test-queue which we configured in the publisher. @JmsListener is used for listening to any messages that are put on the queue test-queue.

Notice that we have an annotated class with @EnableJms. As Spring documentation says @EnableJms enables detection of JmsListener annotations on any Spring-managed bean in the container.”

The interesting point here is that Spring Boot detects the methods even without @EnableJms annotation. This issue has been reported on Stackoverflow.

Creating Spring Boot Applications

In both the projects, create an "Application" class annotated with @SpringBootApplication as below.

package com.techshard.activemq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}


Installing ActiveMQ

I have installed ActiveMQ by downloading here. We can also use Spring Boot’s embedded ActiveMQ for testing purposes. Once you have installed, the ActiveMQ server should be available at http://localhost:8161/admin and we will see the following welcome page.

                                                                    ActiveMQ Home Page

Configuring ActiveMQ

In both the projects, create application.properties file and add the following properties.

spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin

ActiveMQ supports multiple protocols to connect to the message broker. In this example, we will be using the OpenWire protocol.

That’s it!

Testing ActiveMQ

Before running the applications, make sure to change the server port for one of the projects. The embedded tomcat server runs on the port 8080 by default.

Run both the applications and run the URL http://localhost:8080/api/message/Welcome to ActiveMQ! in browser or any REST API testing tool.

In the consumer application, we will see the following log in console.

2019-08-06 22:29:57.667 INFO 17608 — [enerContainer-2] c.t.activemq.consumer.MessageConsumer : Message received Welcome to ActiveMQ!

´What just happened is that the message was put on the queue. The consumer application that was listening to the queue read the message from the queue.

In the ActiveMQ dashboard, navigate to the Queue tab. We can see the details such as a number of consumers to a queue, the number is messages pending, queued and dequeued.

At the beginning of this article, I mentioned that message brokers guarantee delivery of messages. Imagine that the consumer service is down, and the message was put on the queue by publisher service.

Stop the application  activemq-receiver . Run this URL again http://localhost:8080/api/message/Welcome to ActiveMQ! In browser.

Navigate to the ActiveMQ dashboard and notice the queue state.

We can see that one message is pending and enqueued. Start the application activemq-receiver again.

As soon as the application is started, we will the following message in console.

2019-08-06 22:54:32.667 INFO 17608 — [enerContainer-2] c.t.activemq.consumer.MessageConsumer : Message received Welcome to ActiveMQ!

The number of pending messages is now set to zero and the number of dequeued messages is set to two. The message broker guarantees the delivery of messages.

Conclusion

We just saw a simple example of a messaging pattern in this article. The messaging system takes the responsibility of transferring data from one service to another, so the services can focus on what data they need to share but not worry about how to share it.

I hope you enjoyed this article. Feel free to let me know if you have any comments or suggestions.

Topics:
spring boot ,activemq ,microservices

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}