Vert.x
Vert.x is an open source Eclipse toolkit that's used to build distributed and reactive systems.
Eclipse Vert.x provides a flexible way to write applications that are lightweight and responsive due to its implementation of Reactive Stream principles.
It is designed to be cloud-native—it allows many processes to run with very few resources (threads, CPU, etc.). In this way, Vert.x applications can better use their CPU quotas in cloud environments. There isn't any unnecessary overhead caused by the creation of a great number of new threads.
It defines an asynchronous and non-blocking development model based on an event loop that handles the requests and avoids long waiting on the client side while the server side is stressed by a high number of invocations.
Since it's a toolkit and not a framework, Vert.x can be used as a typical third-party library and you are free to choose the component that is needed for you to target.
It does not provide an all-in-one solution, but it gives you the scaffolds that you can assemble to build your own architecture. You can decide to use a Vert.x component inside your existing application, for example, a Spring application, maybe refactoring a particular section to be reactive and better respond to a heavy load. It's also a good choice for implementing HTTP REST microservices thanks to its high volume event processing.
Vert.x runs on top of JVM but it doesn't work in Java alone. You can use all the programming language ecosystem around JVM. You can also use Vert.x with Groovy, Ceylon, JavaScript (maybe using a Java-provided JavaScript engine such as Nashorn) and JRuby.
A diagram having a list of Vert.x modules can be seen here: https://www.eclipse.org/community/eclipse_newsletter/2016/october/article4.php. As you can see, there are some components that can help you build a reactive microservice.
The main component of Vert.x is the verticle. The verticle is the programmable unit where you implement the business logic of your application. Although it's optional in the Vert.x toolkit, it's highly recommended to use it to obtain a clear separation of duty in your architecture. It provides an actor-like deployment and concurrency model, similar to what you can obtain using Akka—a toolkit for building resilient message-driven applications— and it's always executed on the same thread.
A single thread may execute several verticles, using the event loop mechanism, and a single verticle instance usually starts one thread/event loop per core.
A Vert.x application can be composed of multiple verticles; each one implements a specific section of the domain business logic to follow the principles of single responsibility and the separation of duties. Each verticle should communicate to the others in a loosely coupled way—for this purpose, Vert.x uses an event bus, that is, lightweight distributed messaging. When a verticle wants to listen for messages from the event bus, it listens on a certain virtual address, as defined in Reactive Stream specifications.
Now, it's time to create our first simple microservice application. As I mentioned earlier, for Spring Boot and Spring WebFlux, as well as for Vert.x, there is a project generator utility that helps you create a scaffold for the project. The generator is available at http://start.vertx.io/.
Set the fields with the following suggested values (feel free to substitute them with whatever you want):
- Version: 3.5.0
- Language: Java
- Build: Maven
- GroupId: com.microservice.vertx
- ArtifactId: demoVertx
- Selected dependencies: Vert.x Web
The following is a simple screenshot of what I described previously:
Once the ZIP file is downloaded, unzip the context into a directory, open the project with your favorite IDE and, finally, launch the first build from your IDE or by using the following Maven command:
$ mvn clean compile
In this way, you have created a simple runnable Vert.x application.
You will notice that there is a simple verticle class, MainVerticle.java, in the com.microservice.vertx.demoVertx package that creates an HTTP server, listening on port 8080, and returns a simple plain text message, "Hello from Vert.x!":
package com.microservice.vertx.demoVertx;
import io.vertx.core.AbstractVerticle;
public class MainVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
vertx.createHttpServer().requestHandler(req -> {
req.response().putHeader("content-type", "text/plain").end("Hello from Vert.x!");
}).listen(8080);
System.out.println("HTTP server started on port 8080");
}
}
To run the application, you can do the following:
- Launch the $ mvn compile exec:java command
- Build a fat JAR, with the $ mvn package command, and then launch the executable JAR with the java -jar target/demoVertx-1.0.0-SNAPSHOT-fat.jar command
The final result will be the same—an up-and-running HTTP server:
....
HTTP server started on port 8080
Jun 20, 2018 11:09:16 AM io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer
INFO: Succeeded in deploying verticle
....
You will also have a service exposed via HTTP that you can invoke at the URL http://localhost:8080:
Now, we are ready to update our source code so that we can implement a simple microservice.
First of all, we create the classes that are needed for the RESTful web service. We must build an easy Java Plain Old Java Object (POJO) to store the author's information:
package com.microservice.vertx.demoVertx;
import java.util.concurrent.atomic.AtomicInteger;
public class Author {
private static final AtomicInteger COUNTER = new AtomicInteger();
private final int id;
private final String name;
private final String surname;
public Author(String name, String surname) {
this.id = COUNTER.getAndIncrement();
this.name = name;
this.surname = surname;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public int getId() {
return id;
}
}
Then, we make the AuthorVerticle, which has the purpose of exposing the data; in our example, the list of the book's authors. There is a static initialization of the data, as well as the usual creation of the Router and of the HTTP server to serve the requests:
package com.microservice.vertx.demoVertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import java.util.LinkedHashMap;
import java.util.Map;
public class AuthorVerticle extends AbstractVerticle {
private final Map<Integer, Author> authors = new LinkedHashMap<>();
private void populateAuthorsData() {
Author mauro = new Author("Mauro", "Vocale");
authors.put(mauro.getId(), mauro);
Author luigi = new Author("Luigi", "Fugaro");
authors.put(luigi.getId(), luigi);
}
private void getAuthors(RoutingContext routingContext) {
routingContext.response().putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(authors.values()));
}
@Override
public void start(Future<Void> future) {
populateAuthorsData();
Router router = Router.router(vertx);
router.get("/authors").handler(this::getAuthors);
vertx.createHttpServer().requestHandler(router::accept).listen(8080,
result -> {
if (result.succeeded()) {
future.complete();
} else {
future.fail(result.cause());
}
});
}
}
Finally, we refactor the MainVerticle class, which will be responsible for deploying the verticle we created previously:
package com.microservice.vertx.demoVertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
public class MainVerticle extends AbstractVerticle {
@Override
public void start(Future<Void> future) throws Exception {
vertx.deployVerticle("com.microservice.vertx.demoVertx.AuthorVerticle", res -> {
if (res.succeeded()) {
System.out.println("Deployment id is: " + res.result());
} else {
System.out.println("Deployment failed!");
}
});
}
}
Now, we can launch the application:
$ mvn compile exec:java
Then, we can invoke the RESTful service using the URL http://localhost:8080/authors. You will obtain the following result:
You can test your service, following TDD, and also implement a JUnit test:
package com.microservice.vertx.demoVertx;
import io.vertx.core.Vertx;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VertxUnitRunner.class)
public class AuthorVerticleTest {
private Vertx vertx;
public AuthorVerticleTest() {
}
@Before
public void setUp(TestContext context) {
vertx = Vertx.vertx();
vertx.deployVerticle(AuthorVerticle.class.getName(), context.asyncAssertSuccess());
}
@After
public void tearDown(TestContext context) {
if (vertx != null) {
vertx.close(context.asyncAssertSuccess());
}
}
/**
* Test of start method, of class AuthorVerticle.
* @param context
*/
@Test
public void testStart(TestContext context) {
final Async async = context.async();
vertx.createHttpClient().getNow(8080, "localhost", "/authors", response -> {
response.handler(body -> {
context.assertTrue(body.toString().contains(""name" : "Mauro""));
async.complete();
});
});
}
}
Check the result by running the $ mvn test command.
Here, again, we implemented an easy example that shows you how to use Vert.x to build reactive microservices. You can find more details in the Vert.x documentation at https://vertx.io/docs/.