ARTICLE

Verticles: the basic processing units of Vert.x

From Vert.x in Action by Julien Ponge

This article talks about what verticles are and delves a bit into how they work and how to write them.

_________________________________________________________________

Take 37% off Vert.x in Action. Just enter fccponge into the discount code box at checkout at manning.com.
_________________________________________________________________

Relevant source code for this article can be found here: https://github.com/jponge/vertx-in-action/tree/master/chapter2

A verticle is the fundamental processing unit in Vert.x. The role of a verticle is to encapsulate a technical functional unit for processing events such as exposing an HTTP API and responding to requests, providing a repository interface on top of a database, or issuing requests to a third-party system. Much like components in other technologies such as Enterprise Java Beans, verticles can be deployed and they have a life-cycle.

Readers familiar with the actor concurrency model will find similarities between Vert.x verticles and actors. Actors are a model where autonomous entities (the actors) exclusively communicate with other entities by sending and responding to messages. The similarities between Vert.x verticles and actors isn’t fortuitous coincidence: verticles have a private state that may be updated when receiving events, they can deploy other verticles, and they can communicate via message-passing. Verticles don’t necessarily follow the orthodox modern definition of actors, but it’s fair to consider Vert.x at least as being inspired by actors.

Let’s now dive into writing verticles.

Writing a verticle

Because verticles are a key concept in Vert.x, we’ll look into how they work. Before that, let’s write a small verticle that processes two types of events: periodic timers and HTTP requests.

Preparing the project

We’ll use a common project for all samples in this article, using the Gradle project descriptor of listing 1.

Listing 1. Gradle build.gradle.kts for the samples

plugins {
java
}

repositories {
mavenCentral()
}

dependencies {
implementation("io.vertx:vertx-core:3.8.0") ❶

implementation("ch.qos.logback:logback-classic:1.2.3") ❷
}

tasks.create<JavaExec>("run") { ❸

main = project.properties.getOrDefault("mainClass", "chapter2.hello.HelloVerticle") as String
classpath = sourceSets["main"].runtimeClasspath
systemProperties["vertx.logger-delegate-factory-class-name"] = "io.vertx.core.logging.SLF4JLogDelegateFactory" ❹

}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
}

❶ This is the Vert.x core library dependency.

❷ The logback-classic dependency provides the SLF4J logger API and the logback implementation.

❸ This allows running samples with Gradle from the command line.

❹ This ensures that Vert.x itself also uses SLF4J logging.

The Gradle build is a simple one for a Java project. Because we have several examples to run, we don’t rely on the Gradle application plugin but define our own custom run task where we can pass the name of the class to execute. We also take advantage of it to ensure that logging is properly configured and unified to SLF4J.

Listing 2. Logback configuration to reduce Netty verbosity

<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%level [%thread] %logger{0} - %msg%n</pattern> ❷
</encoder>
</appender>

<logger name="io.netty" level="warn"/> ❸

<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

❶ This defines an appender to send events to the console.

❷ The pattern defines how the log events look like.

❸ We drop Netty log events which are more verbose than warnings.

Vert.x uses Netty and logging in Netty is quite verbose with the default Logback configuration. We can reduce the amount of log entries by creating a src/main/resources/logback.xml file and adding the configuration as in listing 2. To make the log samples shorter in this article we also removed event dates and shortened logger class names ($logger{0}). Please refer to the Logback documentation to understand how to configure it [LogbackDoc].

The verticle class

The whole verticle and application fits in the Java class of listing 3.

Listing 3. A sample verticle

package chapter2.hello;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloVerticle extends AbstractVerticle {
private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);
private long counter = 1;

@Override
public void start() {
vertx.setPeriodic(5000, id -> { ❶
logger.info("tick");
});

vertx.createHttpServer()
.requestHandler(req -> { ❷
logger.info("Request #{} from {}", counter++, req.remoteAddress().host());
req.response().end("Hello!");
})
.listen(8080);
logger.info("Open http://localhost:8080/");
}

public static void main(String[] args) {
Vertx vertx = Vertx.vertx(); ❸
vertx.deployVerticle(new HelloVerticle()); ❹
}
}

❶ This defines a periodic task every five seconds.

❷ The HTTP server calls this handler on every request.

❸ We need a global Vert.x instance.

❹ This is the simplest way to deploy a verticle.

This verticle defines two event handlers: one for periodic tasks every five seconds, and one for processing HTTP requests in a HTTP server. The main method instantiates a global Vert.x instance, and deploys an instance of the verticle.

Defining a verticle code in Java is typically done by specializing the AbstractVerticle class. There exists a Verticle interface that one could in theory implement, but AbstractVerticle provides all the event processing, configuration and execution plumbing that Vert.x users need.

Because Vert.x is a library and not a framework, you can create a Vert.x instance from a main method, or from any other class and then deploy verticles.

The life-cycle of a verticle is made of start and stop events. The AbstractVerticle class provides start and stop methods that can be overridden. By default, these methods do nothing.

  1. The start method typically contains setup and handlers’ initialization, like setting a periodic task handler and starting a HTTP server in listing 3.

Running and first observations

The application can be launched as a regular Java application by running the main method either from an integrated development environment or from the command-line. To run on the command line using Gradle, you can use the following command:

gradle run -PmainClass=chapter2.hello.HelloVerticle

In some of the remaining samples we shortened class definitions. We removed package definitions, imports, and main methods which are similar to that of listing 3. When in doubt, please consult the full source code of the samples.

Once the application runs, we can perform a few HTTP requests at http://localhost:8080/ with a web browser or by using command-line tools such as cUrl and HTTPie. The logs shall be similar to that of listing 4.

Listing 4. Sample log output when running HelloVerticle

INFO [vert.x-eventloop-thread-0] HelloVerticle - Open http://localhost:8080/
INFO [vert.x-eventloop-thread-0] HelloVerticle – tick ❷
INFO [vert.x-eventloop-thread-0] HelloVerticle - Request #1 from 0:0:0:0:0:0:0:1 ❸
INFO [vert.x-eventloop-thread-0] HelloVerticle - Request #2 from 0:0:0:0:0:0:0:1
INFO [vert.x-eventloop-thread-0] HelloVerticle - Request #3 from 0:0:0:0:0:0:0:1
INFO [vert.x-eventloop-thread-0] HelloVerticle - Request #4 from 0:0:0:0:0:0:0:1
INFO [vert.x-eventloop-thread-0] HelloVerticle – tick

❶ The HTTP server is now ready.

❷ A periodic task event log.

❸ A HTTP request event log.

The Logback configuration that we use shows the name of the thread associated with an event. We can already check an important property of Vert.x verticles in log entries: event processing happens on a single event-loop thread. Both the periodic tasks and HTTP requests processing happen on a thread that appears as vert.x-eventloop-thread-0 in the logs.

An obvious benefit with this design is that a verticle instance is always executing event processing on the same thread, and there’s no need for using thread synchronization primitives. In a multi-threaded design updating the counter field requires either a synchronized block or the usage of java.util.concurrent.AtomicLong. No such issues here and a plain long field can be safely used.

Preparation methods such as createHttpServer or setTimer may be called from a non-Vert.x thread. This may happen when directly using a Vertx object without a verticle, or when writing unit tests. This isn’t a problem as usage of the Vertx class methods is thread-safe.

Image for post
Image for post
Figure 1. Execution of listing 3

Figure 1 shows the (simplified) interactions between the verticle, the handlers, Vert.x and the event sources. Each arrow represents a method call between the participants. For instance, HelloVerticle creates a periodic task handler by calling setPeriodic on the Vertx object, which in turns creates a periodic task using a Vert.x-internal timer. In turn, the timer periodically calls back the timerHandler handler in HelloVerticle.

Note that we represented the calls to requestHandler and listen as being to the Vertx object as a shortcut, but in reality they’re on an object that implements the HttpServer interface. The class is internal to Vert.x, and because it doesn’t serve the diagram to add another participant we merged it into Vertx.

That’s all for this article.

If you want to learn more about the book, check it out on our browser-based liveBook reader here and see this slide deck.

Written by

Follow Manning Publications on Medium for free content and exclusive discounts.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store