Vert.x – a node-based framework

Egmont-Petersen

Vert.x

In 2011, the framework Vert.x was launched. After its infancy at VMPlayer, Vert.x was developed into a full-fledged web library, targeted specifically at node-based computing using non-blocking event processing.

Vert.x is basically a Java-library, but with large capabilities. It supports deployment of web endpoints, JDBC-endpoints, inter-node communication via the EventBus according to different schemens, and it is compatible with several programming languages including Java, JavaScript, Scala, Kotlin and Groovy. The following contains only Java examples.

Central concepts in Vert.x are:

  • nodes – the Java-work horses that generate, process or receive events (can be an http-endpoint). The nodes are called ventricles. A vertex is a node, in a graph.
  • eventbus – the central message highway, through which the nodes communicate (contained in the class Vertx).
  • communication schemes
    • producer-consumer,
    • subscription-scheme,
    • broadcast scheme,
  • non-blocking events – an event that passes the EventBus will not block another event for processing.

 

Relation to frameworks (Spring)

With Vert.x, you can configure and deploy a Java web-application completely with web-content, without using Tomcat and Spring. In essence, the thread-pool session management offered by Tomcat (and utilized by a Spring Boot web application) – that user-thread management task is handed over to the Vert.x core. This enables the software developer to control the user session-management at a direct level, by adapting the node configuration and the deployment scheme to cope with the required session load.

 

Step 1 – Creating an HttpEndPoint

Based on these compact examples, the configuration of an HttpEndPoint can look as follows:

public class HttpServer extends AbstractVerticle {

  private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerVerticle.class);

  private FreeMarkerTemplateEngine templateEngine;

 
  private static final String EMPTY_PAGE_MARKDOWN =
    "# A new page\n" +
      "\n" +
      "Feel-free to write in Markdown!\n";

  @Override
  public void start(Promise<Void> promise) throws Exception {
   
    HttpServer server = vertx.createHttpServer();

    Router router = Router.router(vertx);
    router.get("/").handler(this::indexHandler);
    
    templateEngine = FreeMarkerTemplateEngine.create(vertx);

    int portNumber = config().getInteger(CONFIG_HTTP_SERVER_PORT, 8080);
    server
      .requestHandler(router)
      .listen(portNumber, ar -> {
        if (ar.succeeded()) {
          LOGGER.info("HTTP server running on port " + portNumber);
          promise.complete();
        } else {
          LOGGER.error("Could not start a HTTP server", ar.cause());
          promise.fail(ar.cause());
        }
      });
  }
}

The vertx object is available from the parent class AbstractVerticle. Using vertx.createHttpServer(), the application obtains a ready-to-use http endpoint. The http-router is also obtained by means of the vertx object.

Usage of the Vert.x components happens in Java via the dot-connected builder-pattern (that is, server.requestHandler(…).listen(…)… ).

 

Step 2 – Client-Server via HttpEndPoint

The basic client-server example is in Vert.x programmed with a few lines of Java code.

Server.java:

public class Server extends AbstractVerticle {

  // To run it in your IDE
  public static void main(String[] args) {
    Runner.runExample(Server.class);
  }

  @Override
  public void start() throws Exception {

    vertx.createHttpServer().requestHandler(req -> {

      req.response().end("Vert.x server - From Michael");

    }).listen(8080, listenResult -> {
      if (listenResult.failed()) {
        System.out.println("Could not start HTTP server");
        listenResult.cause().printStackTrace();
      } else {
        System.out.println("Server started");
      }
    });
  }
}

which can be called by an http client, as follows.

Client.java:

public class Client extends AbstractVerticle {

  // To run it in your IDE
  public static void main(String[] args) {
    Runner.runExample(Client.class);
  }

  @Override
  public void start() throws Exception {

    WebClient client = WebClient.create(vertx);

    client.get(8080, "localhost", "/").send(ar -> {
      if (ar.succeeded()) {
        HttpResponse<Buffer> response = ar.result();
        System.out.println("Got HTTP response with status " + response.statusCode() + " with data " +
          response.body().toString("ISO-8859-1"));
      } else {
        ar.cause().printStackTrace();
      }
    });
  }
}

 

In your favorite IDE, you can call both main methods (‘run’ them), and the client call the server, via port 8080.

The instance object vertx is central in the Vert.x Java library, as it is used to construct both server nodes, client nodes and routers.

 

Step-3 – Mongo Client within Vert.x

The Vert.x library comes with a built-in integration with MongoDB.

Simply configure Mongo using a Json-object.

Call the Vert.X MongoClient with this configuration, and MongoDB is ready to receive data in the form of Json-objects.

 

Configuring and setting up the MongoClient:

JsonObject config = new JsonObject()
  .put("connection_string", "mongodb://localhost:27018")
  .put("db_name", "my_DB");

// Create the Mongo client
MongoClient mongo = MongoClient.createShared(vertx, config);

Inserting Json objects looks as follows:

JsonObject product = new JsonObject().put("Id", "12345").put("name", "CPU").put("price", "500.0");

mongo.save("products", product, id -> 

{ System.out.println("Inserted id: " + id.result());

});

which is really compact code.

 

Some essential Vert.x Java classes and method calls

A few classes and method calls form the cornerstone of Vert.x.

 

Future

The Future class is used as a parameter to an asynchronous Verticle.

Future<Void> beginFuture

When is an asynchronous verticle useful? You can declare an asynchronous verticle, of which start(.) takes a Future as only parameter, when this verticle should not be deployed before some initialization code has been executed.

You achieve this by implementing the asynchronous start method, of a Verticle:

public class AsyncVerticle extends AbstractVerticle {
   ...
   @Override 
   public void start(Future<Void> beginFuture) {
   ...

The start(.) method takes a Future as a parameter. When the method returns AsyncVerticle will not be considered deployed.

When the initialization code has eventually finalized, you can call complete on the Future (or fail) to indicate that deployment has now taken place:

startFuture.complete();
             // OR
startFuture.fail("This verticle failed to initialize");

 

Router

Essential to all multi-linked web-servers is the routing to different (sub)URLs. Add a webpath to the Vert.x router, using .route(…)

Router router = Router.router(vertx);
router.route("/weblink/*").handle(this::handlerMethod);

where the handle-call specifies which method needs be called, when the http-caller requests the url with the prefix ‘weblink’.

 

EventBus

Vert.x comes with an eventbus, which is the central vehicle for inter-node communication. Within a Java-class derived from AbstractVerticle, the eventBus is obtained with the method call:

EventBus eventBus = vertx.eventBus();

Sending a message from one verticle to another one (point-to-point communication) takes place via the eventBus.

String message = "Hello receiver"; 

eventBus.send(ADDRESS, message, reply -> {
  if (reply.succeeded()) {
     System.out.println("Received reply: " + reply.result().body());
  } else {
     System.out.println("No reply");
  }
});

which is received by the verticle addressed in the first parameter to .send(.

In-depth analysis of the capacity and capabilities of the EventBus in Vert.X, see technicalities of Vert.x EventBus.

 

The EventBus – front-end/back-end communication

The eventbus is used for efficient communcation between a Java/Vert.x backend and an Angular or Javascript backend. In that sense, the eventbus (and not http-requests) is used for exchanging Json objects between the front-end client and the Java/Vert.x backend.

An example of this can be found in the blog:

Angular-TypeScript with Java-Vert.x backend

In short, a web-socket is defined for handling incoming messages (a ‘consumer’)  in the Java-backend. This takes but a few lines of Java-code:

...
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
BridgeOptions bo = new BridgeOptions()
       .addInboundPermitted(new PermittedOptions().setAddress(EB_WS_SERVER_ADDRESS))
       .addOutboundPermitted(new PermittedOptions().setAddress(EB_WS_CLIENT_ADDRESS));
sockJSHandler.bridge(bo, Sync.fiberHandler(this::handleEBrequests));

router.route("/ws/*").handler(sockJSHandler);
vertx.eventBus().consumer(EB_WS_SERVER_ADDRESS, Sync.fiberHandler(this::handleClientMessage));
server.requestHandler(router::accept).listen(9090);
...

The vert.x eventbus is defined with one line of code in the TypeScript main class. Subsequently, the messages can be pushed to the backend with one line of front-end code.

 

The general documentation of Vert.x can be found here.