Spring Boot Tomcat Session Replication using Hazelcast

See something wrong? Edit this page.

You can see the whole project here.

What You’ll Learn

Spring Boot is a framework that helps to build microservices easily. In the Java ecosystem, it’s one of the most preferred ways of building microservices as of now. In a microservice architecture, there are different options to share data among the running services: one of them is caching, which Spring Boot leverages. It also provides session scope data access using built-in Tomcat HTTP sessions. But when running multiple microservices, it quickly becomes a hassle to replicate sessions and share their data across microservices.

Imagine that you have a microservice which uses Tomcat HTTP sessions, and you would like to scale it horizontally. You can create multiple microservice instances, and use a load balancer to access them. However, each microservice instance will not have the same session data and the session won’t be consistent through microservices when the requests reach different instances. One option is to use sticky sessions but even if you use sticky sessions, you will lose some session data when a microservice instance is crashed/shut down. To prevent any data loss and provide consistency, session data should be replicated through microservice instances.

Multiple solutions exist for different environments and setups but in this blog post, we will find out how we can replicate sessions through Spring Boot microservices using Hazelcast with only minimal configuration settings.

Prerequisites

  • ~15 minutes

  • A text editor or IDE

  • JDK 1.8+

  • Apache Maven 3.2+

The Spring Boot Application Structure

The application is a basic Spring Boot application having 3 endpoints:

  • / is the homepage returning “Homepage” string only

  • /put is the page where key and value are saved to the current session as an attribute

  • /get is the page where the values in the current session can be obtained by keys

Session Replication using Hazelcast Tomcat Session Manager

To configure session replication, let’s first add some dependencies to the pom.xml file:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-tomcat9-sessionmanager</artifactId>
    <version>${hazelcast-tomcat-sessionmanager.version}</version>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <version>${hazelcast.version}</version>
</dependency>

The first dependency is for Hazelcast Tomcat Session Manager, the second one is for Hazelcast IMDG itself.

At this point, only some configuration beans are necessary to enable session replication:

// Application.java

@Bean
public Config hazelcastConfig() {
    Config config = new Config();
    config.setProperty("hazelcast.logging.type", "slf4j");
    config.setInstanceName("hazelcastInstance");
    JoinConfig joinConfig = config.getNetworkConfig().getJoin();
    joinConfig.getMulticastConfig().setEnabled(false);
    joinConfig.getTcpIpConfig().setEnabled(true).addMember("localhost");
    return config;
}

@Bean
public HazelcastInstance hazelcastInstance(Config hazelcastConfig) {
    return Hazelcast.getOrCreateHazelcastInstance(hazelcastConfig);
}

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> customizeTomcat(HazelcastInstance hazelcastInstance) {
    return (factory) -> {
        factory.addContextCustomizers(context -> {
            HazelcastSessionManager manager = new HazelcastSessionManager();
            manager.setSticky(false);
            manager.setHazelcastInstanceName("hazelcastInstance");
            context.setManager(manager);
        });
    };
}
  • The first bean creates a Hazelcast Config object to configure Hazelcast members. We enable the TCP/IP configuration.

  • The second bean creates a Hazelcast member using the hazelcastConfig bean.

  • The third one customizes Tomcat instance in Spring Boot to use Hazelcast Tomcat Session Manager.

Please note that the Hazelcast instance name for HazelcastSessionManager object and the instance name for Hazelcast config bean should be the same. Otherwise, HazelcastSessionManager wouldn’t access the running Hazelcast instance.

Running the Sample Application

In the project directory, build the application:

mvn clean package

Run the first instance of the application:

java -Dserver.port=8080 -jar target/springboot-tomcat-session-replication-0.1.0.jar

Open another terminal session and start the second application:

java -Dserver.port=8081 -jar target/springboot-tomcat-session-replication-0.1.0.jar

Now, you can issue HTTP calls to application endpoints to put data and get it back. Open another terminal session and send a request to /put endpoint. This will set the key-value pair as a session attribute.

Since session data is stored in Hazelcast distributed map, we can read this attribute from the other application instance using the same cookie:

curl --cookie cookies.txt --cookie-jar cookies.txt -s -L "localhost:8080/put?key=myKey&value=hazelcast"
curl --cookie cookies.txt --cookie-jar cookies.txt -s -L "localhost:8081/get?key=myKey"

For each command the output should be like the following:

{"value":"hazelcast"}

Notice we have to use cookies when testing the application since the data is saved in HTTP sessions.

Summary

We created a Spring Boot application that accesses session scope data. To be able to share the session data between multiple SpringBoot applications we used Hazelcast Tomcat Session Manager. When we put the key-value pair to our current session as an attribute at port 8080, we could get the attributes from the other application at port 8081.