Get Started with Hazelcast and Quarkus

Set up a native image-ready Quarkus application, using a Hazelcast client.

Context

Hazelcast is the fastest cloud-native distributed cache solution in the OSS world. It’s elastic and a natural fit for cloud-ready architectures. Quarkus tailors your application for GraalVM and HotSpot. It has amazingly fast boot time, incredibly low RSS memory (not just heap size!) offering near-instant scale-up.

Before you Begin

  • Docker and Docker Compose

  • Apache Maven 3.2+

The Quarkus Application Structure

This guide showcases how to set up a basic Quarkus application to work with Hazelcast client/server topology.

The put operation places a key-value pair to Hazelcast and get operation returns the value along with the Container Name.

The Container Name is present to make it clear from which instance the value is returned from.

Use Hazelcast Client in the Application

In pom.xml we add quarkus-hazelcast-client dependency as follows:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>quarkus-hazelcast-client</artifactId>
</dependency>

And in the code we need to configure the client as follows:

@ApplicationScoped
public class HazelcastClientConfig {
    @Produces
    HazelcastInstance createInstance() {
        ClientConfig clientConfig = new ClientConfig();
        String[] members = System.getenv("HAZELCAST_IP").split(",");
        clientConfig.getNetworkConfig().addAddress(members);
        return HazelcastClient.newHazelcastClient(clientConfig);
    }
}

The environment variable HAZELCAST_IP in the above code is set in the docker-compose.yml file. This file creates a Hazelcast cluster with 2 members and 2 Quarkus applications.

...
  quarkus-service1:
    container_name: quarkus-service1
    image: hazelcast-guides/hazelcast-quarkus
    environment:
      - CONTAINER_NAME=hazelcast-quarkus_1
      - HAZELCAST_IP=member1,member2
    ports:
...

Build the Quarkus Application

To build a standalone jar, you can use a standard Maven command:

mvn package

Now, we’re ready to build the Docker image:

docker build . -f Dockerfile -t hazelcast-guides/hazelcast-quarkus

Build the Quarkus Native Executable

To build a native executable, you can use the dedicated native Maven profile:

mvn clean package -Dnative -Dquarkus.native.container-build=true

The -Dnative-image.docker-build=true build parameter runs the native compilation inside a special GraalVM-enabled Docker container provided by Quarkus. However, if you wish, you can use your local GraalVM setup.

Now, we’re ready to build the Docker image (based on a dedicated native-friendly Dockerfile):

docker build . -f Dockerfile.native -t hazelcast-guides/hazelcast-quarkus

However, keep in mind that native mode doesn’t support all features out-of-the-box (https://github.com/oracle/graal/blob/master/substratevm/Limitations.md).

Run the Application

We’re ready to launch the application consisting of a Hazelcast cluster and two Quarkus applications:

docker-compose up -d

We can verify that two members joined the Hazelcast cluster:

$ docker logs member1 -f
...
 Members {size:2, ver:2} [
	Member [172.30.0.2]:5701 - b123eadd-e3e6-4605-867d-1f1f40a2eaf2 this
	Member [172.30.0.3]:5701 - c31026b6-e01d-45f7-b3b2-3d0338d67ef2
]
...

We can verify that Quarkus applications started properly (notice the startup time of 14ms!):

$ docker logs quarkus-service1 -f
2019-12-13 23:40:27,298 INFO  [io.quarkus] (main) hazelcast-quarkus-guide 1.0-SNAPSHOT (running on Quarkus 1.0.0.Final) started in 0.014s. Listening on: http://0.0.0.0:8080
2019-12-13 23:40:27,299 INFO  [io.quarkus] (main) Profile prod activated.
2019-12-13 23:40:27,299 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, resteasy-jackson, resteasy-jsonb]...

Send a put operation to the first application running on port 8081:

$ curl -X POST "localhost:8081/hazelcast/put?key=key_1&value=value_1";echo;
{"containerName":"quarkus-service2","value":"value_1"}

Get the value from application running on 8080 and verify that it’s the same value as put operation:

$ curl "localhost:8080/hazelcast/get?key=key_1";echo;
{"containerName":"quarkus-service1","value":"value_1"}

Clean Up

$ docker-compose down

Summary

In this tutorial, you bootstrapped a native-image-ready Quarkus application which uses a Hazelcast client.

You started two application instances, and they formed a Hazelcast cluster. Since the data was shared among all Hazelcast cluster instances, you could access it from both application instances.