This tutorial helps you integrate Vert.x with Hazelcast and use Hazelcast for distributed session management and other distributed data structures.
Overview
In this tutorial, you will learn how to:
- 
Start with a simple Vert.x Hello World application. 
- 
Add the vertx-hazelcast module and enable distributed session management. 
- 
Use the io.vertx.core.shareddata.Counterdata structure to implement a unique ID generator.
Prerequisites
- 
Java 17 or newer 
- 
Maven 3.9 
- 
httpie client 
Create a new project
- 
Go to start.vertx.io, change the artifact id to messages, the version to 5.0.0, and generate a new project.
- 
Extract the project and build it using: 
$ mvn clean packageThen start the application using:
java -jar target/messages-1.0.0-SNAPSHOT-fat.jarYou should see output similar to the following:
HTTP server started on port 8888
Aug 29, 2024 2:22:38 PM io.vertx.launcher.application.VertxApplication
INFO: Succeeded in deploying verticleStore data in session
Go to the MainVerticle.java file and replace the contents of the start method with the following:
| This tutorial uses 2-space indentation, which is customary for Vert.x projects due to the high number of nested callbacks. | 
// include::ROOT:example$/vertx/vertxMainVerticleStart.java[] Save for later; keep code sample inline for now
  public void start() {
    // Create a Router
    Router router = router(vertx);
    // Create local SessionStore
    SessionStore store = LocalSessionStore.create(vertx);
    // Use the SessionStore to handle all requests
    router.route()
      .handler(SessionHandler.create(store));
    router.route(HttpMethod.PUT, "/").handler(context -> {
      context.request().bodyHandler(body -> {
        List<String> messages = getMessagesFromSession(context);
        JsonObject json = body.toJsonObject();
        String message = json.getString("message");
        messages.add(message);
        putMessagesToSession(context, messages);
        context.json(
          new JsonObject()
            .put("messages", messages)
        );
      });
    });
    // Create the HTTP server
    vertx.createHttpServer()
      // Handle every request using the router
      .requestHandler(router)
      // Start listening
      .listen(8888)
      // Print the port
      .onSuccess(server ->
        System.out.println(
          "HTTP server started on port " + server.actualPort()
        )
      );
  }
  private static List<String> getMessagesFromSession(RoutingContext context) {
    String messages = context.session().get("messages");
    if (messages == null) {
      return new ArrayList<>();
    } else {
      return new ArrayList<>(Arrays.asList(messages.split(",")));
    }
  }
  private void putMessagesToSession(RoutingContext context, List<String> messages) {
    context.session().put("messages", String.join(",", messages));
  }Next, test the server response:
$ http put localhost:8888 message=Hello\ World!
HTTP/1.1 200 OK
content-length: 29
content-type: application/json
set-cookie: vertx-web.session=ed22f77473a7f613c9305431a62832a6; Path=/
{
    "messages": [
        "Hello World!"
    ]
}Execute another request with the cookie:
$ http put localhost:8888 'Cookie:vertx-web.session=ed22f77473a7f613c9305431a62832a6' message=Hello\ World\ 2!
HTTP/1.1 200 OK
content-length: 46
content-type: application/json
{
    "messages": [
        "Hello World!",
        "Hello World 2!"
    ]
}Distributed sessions
Let’s modify the code, so we can start multiple instances easily — the application will start on the defined port and, if the port is unavailable, it will search for another port:
Add the following method to the MainVerticle.java class:
private int findFreePort(int from) {
    for (int port = from; port < from + 100; port++) {
      try {
        new ServerSocket(port).close();
        return port;
      } catch (IOException e) {
        // port not available, try next
      }
    }
    throw new RuntimeException("Could not find an available port");
  }And then use it in the start method:
...
int port = findFreePort(8888);
// Create the HTTP server
vertx.createHttpServer()
  // Handle every request using the router
  .requestHandler(router)
  // Start listening
  .listen(port)
...Now, we can start two instances:
$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar
HTTP server started on port 8888
Aug 30, 2024 9:09:44 AM io.vertx.launcher.application.VertxApplication
INFO: Succeeded in deploying verticle
...
$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar
HTTP server started on port 8889
Aug 30, 2024 9:09:47 AM io.vertx.launcher.application.VertxApplication
INFO: Succeeded in deploying verticleWe can see the session is not shared between the instances. Here is the request to the first instance:
$ http PUT localhost:8888 message="Hello world"
HTTP/1.1 200 OK
content-length: 28
content-type: application/json
set-cookie: vertx-web.session=00f219c166ca50727d23eaaf9fe54229; Path=/
{
    "messages": [
        "Hello world"
    ]
}And here is the request to the 2nd instance. Notice the different port and that we use the cookie we received, but the data does not contain the previous message.
$ http PUT localhost:8889 message="Hello world 2" 'Cookie: vertx-web.session=00f219c166ca50727d23eaaf9fe54229'
HTTP/1.1 200 OK
content-length: 30
content-type: application/json
set-cookie: vertx-web.session=a1486c5ed6416972fdc356e4d91d2397; Path=/
{
    "messages": [
        "Hello world 2"
    ]
}We will fix that by using a Hazelcast Cluster Manager. This resides in the io.vertx:vertx-hazelcast module, which is maintained by the Vert.x team with contributions from Hazelcast and is based on Hazelcast Community Edition.
Change the following part of the start method:
// Create local SessionStore
SessionStore store = LocalSessionStore.create(vertx);to the following:
// Create clustered SessionStore
SessionStore store = ClusteredSessionStore.create(vertx);From now on, we will start the application with the -server parameter, which tells Vert.x to look for a cluster manager implementation.
We also need to provide a Hazelcast configuration file, and create a file cluster.xml in the src/main/resources directory:
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.hazelcast.com/schema/config
           https://www.hazelcast.com/schema/config/hazelcast-config-5.6.xsd">
  <network>
    <join>
      <multicast enabled="true"/>
    </join>
  </network>
  <multimap name="__vertx.subs">
    <backup-count>1</backup-count>
    <value-collection-type>SET</value-collection-type>
  </multimap>
  <map name="__vertx.haInfo">
    <backup-count>1</backup-count>
  </map>
  <map name="__vertx.nodeInfo">
    <backup-count>1</backup-count>
  </map>Now rebuild the project and start the application. You will see more verbose output as Hazelcast prints its own startup logs:
$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar -cluster
...
HTTP server started on port 8888
...
Members {size:2, ver:2} [
	Member [192.168.0.10]:5701 - e29f0362-f9a9-4708-b6e5-1a6067b5aa39 this
	Member [192.168.0.10]:5702 - 74014573-a18a-44f2-9ca7-fd90b70dcb43
]
...And:
$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar -cluster
...
HTTP server started on port 8889
...
Members {size:2, ver:2} [
	Member [192.168.0.10]:5701 - e29f0362-f9a9-4708-b6e5-1a6067b5aa39
	Member [192.168.0.10]:5702 - 74014573-a18a-44f2-9ca7-fd90b70dcb43 this
]
...Putting two messages into different instances while using the same cookie, we see that the session is shared between the instances.
$ http PUT localhost:8888 message="Hello world"
HTTP/1.1 200 OK
content-length: 31
content-type: application/json
set-cookie: vertx-web.session=1ab47cb96731123135f25ec7b67efd64; Path=/
{
    "messages": [
        "",
        "Hello world"
    ]
}$ http PUT localhost:8889 message="Hello world 2" 'Cookie: vertx-web.session=674806546c690674962f279670abefcf'
HTTP/1.1 200 OK
content-length: 44
content-type: application/json
{
    "messages": [
        "Hello world",
        "Hello world 2"
    ]
}Using Counter
Next, we’ll use Hazelcast’s distributed counter functionality to generate unique, incrementing IDs across a distributed system. The counter is maintained across all nodes in the Hazelcast cluster, ensuring each request gets a unique ID even when multiple servers are handling requests.
Replace this part of the code at the end of the start() method:
context.json(
  new JsonObject()
    .put("messages", messages)
);with the following:
context.vertx()
  .sharedData()
  .getCounter("requestId")
  .onSuccess(counter -> {
    counter.incrementAndGet()
      .onSuccess(requestId -> {
        context.json(
          new JsonObject()
            .put("requestId", requestId)
            .put("messages", messages)
        );
      });
  });Now, when you try the application, you can see the response contains an additional field named requestId and its value increments for every request.
$ http PUT localhost:8888 message="Hello world"
HTTP/1.1 200 OK
content-length: 42
content-type: application/json
set-cookie: vertx-web.session=d9fb4cada5c0fc625089a38f3de13e3c; Path=/
{
    "messages": [
        "Hello world"
    ],
    "requestId": 1
}Summary
In this tutorial, you learned how to add the vertx-hazelcast module and enable distributed session management, as well as how to use the io.vertx.core.shareddata.Counter data structure to implement a unique id generator.