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

Vert.x and Fiber: Perfect Synergy!

DZone's Guide to

Vert.x and Fiber: Perfect Synergy!

Vert.x might be your go-to tool suite for Reactive apps, but take a look at how Fibers can allow you to receive events synchronously.

· Java Zone
Free Resource

Just released, a free O’Reilly book on Reactive Microsystems: The Evolution of Microservices at Scale. Brought to you in partnership with Lightbend.

Presumably, you have already heard of Vert.x, a toolkit for building Reactive applications on the JVM. Vert.x is completely asynchronous, lightweight, incredibly fast, event-driven, and definitely a very promising project. Some of you may have heard about Fibers — if you're not familiar with them, check here and here. In addition to Vert.x's non-blocking and event-driven toolkit, we can take advantage of Vertx-Sync as a component through which we can easily escape from well-known callback hell while using non-blocking APIs. Vertx-Sync allows you to perform asynchronous operations and receive events in a synchronous way, but without blocking kernel threads. As a result, even if  it’s synchronous, it is negligibly less performant. Vertx-Sync uses Quasar to provide its Fibers.

In this article, I’ll show you how to:

  1. Create a simple Vert.x project.
  2. Write simple and concise code via Vertx-Sync.  
  3. Create a simple REST API via Vertx-Web.
  4. Get some web content using an HTTP client.
  5. Use MongoDB as an example for using a database with Vert.x.

You can head to GitHub to follow along here. For the sake of this blog, we need:

  1. JDK 1.8
  2. Maven
  3. MongoDB

Install and set up MongoDB

Download the latest version of MongoDB and unzip it to your local directory. You may download it from here. Then, make the following directory /data/db inside the directory where MongoDB is unzipped.

To start the server go to {mongo_directory}/bin and start mongod.

Now it is time to run the mongo client. Go to {mongo_directory}/bin and start mongo .

Create a testDB database and an Entities collection:

use testDB;
db.createCollection('Entities');


Create a Simple Vert.x Project

It’s time to create our Vert.x project. Let’s do it! Our starting point of the project is:

import io.vertx.core.Vertx;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

public class Launcher {
    private static final Logger logger = LoggerFactory.getLogger(MainVerticle.class);

    public static void main(String[] args) {
        Vertx.vertx().deployVerticle(MainVerticle.class.getName(), h -> {
            if (h.succeeded()) {
                logger.info("Success: {0}", h.result());
            } else {
                logger.error("Something went wrong: {0}", h.cause());
            }
        });


And our Vertical:

import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.ErrorHandler;

import java.util.List;


public class MainVerticle extends SyncVerticle {

    private static final Logger 
    logger = LoggerFactory.getLogger(MainVerticle.class);
    private static final String COLLECTION_NAME= "Entities";
    private WebClient webClient;
    private MongoClient mongoClient;

    @Override
    @Suspendable
    public void start(Future<Void> startFuture) throws Exception {
        super.start(startFuture);
        HttpServer server = vertx.createHttpServer();
        Router router = Router.router(vertx);
        // enable BodyHandler globally for easiness of body accessing
        router.route().handler(BodyHandler.create()).failureHandler(ErrorHandler.create());
        router.route(HttpMethod.GET, 
        "/getWebContent").handler(Sync.fiberHandler(this::getWebContent));
        router.route(HttpMethod.GET, 
        "/entities").handler(Sync.fiberHandler(this::getAllEntities));
        router.route(HttpMethod.GET, 
        "/entities/:id").handler(Sync.fiberHandler(this::getEntityById));
        router.route(HttpMethod.PUT, 
        "/entities").handler(Sync.fiberHandler(this::saveNewEntity));
        // HttpServer will be automatically shared if port matches
        server.requestHandler(router::accept).listen(8089);
        webClient = WebClient.create(vertx, new WebClientOptions().setSsl(true));
        mongoClient = MongoClient.createShared
       (vertx, new JsonObject().put("connection_string", "mongodb://127.0.0.1:27017/testDb"));
    }

    @Suspendable
    private void saveNewEntity(RoutingContext routingContext){
        final String response = Sync.awaitResult
        (h ->  mongoClient.save(COLLECTION_NAME, routingContext.getBodyAsJson(), h));
        routingContext.response().end(response);
    }

    @Suspendable
    private void getAllEntities(RoutingContext routingContext){
        final List<JsonObject> entities = Sync.awaitResult
        (h ->  mongoClient.find(COLLECTION_NAME, new JsonObject(), h));
        routingContext.response().end(Json.encodePrettily(entities));
    }

    @Suspendable
    private void getEntityById(RoutingContext routingContext){
        final JsonObject query = new JsonObject()
                .put("_id", routingContext.pathParam("id"));
        final List<JsonObject> entity = Sync.awaitResult
        (h ->  mongoClient.find(COLLECTION_NAME, query, h));
        routingContext.response()
                .end(Json.encodePrettily(entity));
    }

    @Suspendable
    private void getWebContent(RoutingContext routingContext){
        final HttpResponse<Buffer> response = Sync.awaitResult
        (h -> webClient.getAbs("https://www.google.com").send(h));
        final String responseContent = response.bodyAsString("UTF-8");
        logger.info("Result of Http request: {0}", responseContent);
        routingContext.response()
                .putHeader(HttpHeaderNames.CONTENT_TYPE, "text/html")
                .end(responseContent);


  1. The Launcher class is just for starting our program and deploying our MainVerticle.
  2. It has to be considered that our MainVerticle extends io.vertx.ext.sync.SyncVerticle. If it is not deployed as a SyncVerticle instance, the Fibers will not work.
  3. Create HttpServer and a router for handling our REST API (following the next three steps):
    1. GET http://localhost:8089/getWebContent — request to “https://www.google.com” just for testing the async HTTP client.
    2. GET http://localhost:8089/entities
    3. GET http://localhost:8089/entities/:id  — :id is the path parameter for finding some entity
    4. PUT http://localhost:8089/entities — create a new entity record. Apply JSON to the payload, for example: {“name”: “Charlie Harper”}
  4. Create WebClient and MongoClient and use their async APIs synchronously via Fibers.
  5. It is very important to use Sync.fiberHandler() when we need to use any handlers. For usage of Fibers in a normal handler, for example in the request handler of an HTTP server, we must first convert the normal handler to a fiber handler.
  6. Getting a response from async APIs that return AsyncResult<T>  as a callback is very easy with Sync.awaitResult(). Below is some copied documentation from Vertx about it:

"Many async operations in Vert.x-land take a Handler<AsyncResult<T>> as the last argument. An example would be executing a find using the Vert.x Mongo client or sending an event bus message and getting a reply. Vertx-sync allows you to get the result of a one-shot asynchronous operation in a synchronous way. This is done by using the Sync.awaitResult method."

The method is executed specifying the asynchronous operation that you want to execute in the form of a Consumer, the consumer is passed the handler at run-time. Using Lambda expressions makes it even easier:

final List<JsonObject> entity = Sync.awaitResult
(h ->  mongoClient.find(COLLECTION_NAME, query, h));
final HttpResponse<Buffer> 
response = Sync.awaitResult
(h -> webClient.getAbs("https://www.google.com").send(h));


The most important thing is to instrument our bytecode. Vert.x uses Quasar and their Fiber implementation use bytecode instrumentation. We don’t need to do anything special, but just have to start the JVM with javaagent as the VM option. For example:

javaagent:/path/to/quasar/core/quasar-core.jar.

For that, we use “exec-maven-plugin” . That is the content of 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>org.idle.fibers</groupId>
    <artifactId>vertx-sync-fibers</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <vertx.version>3.4.1</vertx.version>
        <main.verticle>org.idle.easy.fibers.MainVerticle</main.verticle>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.vertx</groupId>
                <artifactId>vertx-dependencies</artifactId>
                <version>${vertx.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
            <version>${vertx.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web-client</artifactId>
            <version>${vertx.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-auth-oauth2</artifactId>
            <version>${vertx.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-sync</artifactId>
            <version>${vertx.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-mongo-client</artifactId>
            <version>${vertx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>${project.basedir}/src/main/generated</directory>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.8.2</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <configuration>
                    <mainClass>org.idle.easy.fibers.Launcher</mainClass>
                    <workingDirectory>target/classes</workingDirectory>
                    <executable>java</executable>
                    <arguments>
                        <argument>-javaagent:${co.paralleluniverse:quasar-core:jar}</argument>
                    </arguments>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


Finally, to start our program, just type: mvn clean package exec:java

And that is all!

Strategies and techniques for building scalable and resilient microservices to refactor a monolithic application step-by-step, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
java ,vert.x ,fiber ,reactive ,tutorial ,async

Published at DZone with permission of Nikolay Petkov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}