Vert.x Verticles
In this tutorial, we are going to discuss about the Vert.x Verticles. In Vert.x, Verticles are the fundamental units of deployment and execution. They represent isolated, lightweight components of your application that can be deployed, scaled, and managed by the Vert.x runtime. A Vert.x application typically consists of multiple verticles that can communicate with each other via the event bus.
Vert.x comes with a scalable actor like deployment and concurrency model out of the box in the vert.x world. These components are called Verticles. The verticle model is not 100% the same as the actor model, but very similar.
What is actor model?
The Actor Model is a conceptual model for building concurrent and distributed systems, introduced by Carl Hewitt in 1973. It’s a way of designing systems where the primary components, called actors, are the fundamental units of computation. Actors encapsulate state and behavior, and they communicate with each other solely by passing messages. The actor model is widely used in building highly scalable, concurrent systems because it avoids shared mutable state and complex locking mechanisms.
Actor Model Example:
Here’s a simplified illustration of how actors work:
- Actor A sends a message to Actor B.
- Actor B, when it receives the message, processes it (based on its internal logic) and sends a message to Actor C.
- Actor A and Actor B never directly interact or access each other’s state.
Actor A -----> Actor B -----> Actor C
"msg" "response" "final msg"
Actor Model in Practice
Many modern programming frameworks and systems use the actor model, especially in distributed computing and high-concurrency environments:
- Akka (Java/Scala): A popular actor-based framework for the JVM.
- Erlang/Elixir: Erlang’s concurrency model is based on actors, and it has been used for building fault-tolerant distributed systems.
- Microsoft Orleans (C#): Implements the actor model for building distributed systems in .NET.
- Project Loom: Java is also introducing structured concurrency, which is inspired by actor-like models.
Coming to the Vert.x Verticles, To use this model, it is possible to define multiple verticles that are deployed by the Vert.x instance we have seen in the previous section. Vert.x Verticles are running on the event loop thread which are used for non-blocking operations and the verticle can be deployed multiple times for scalability.
As an example, we could have a main verticle that deploys verticle A and verticle B, and these two verticles are then children.
So how would a more complex verticle structure look like? Here we have a more complex example structure.
So the main verticle would deploy verticlelly A, B and N as child verticles. And each of these children can also have children. So verticlelly A can have a verticle AA and verticle AB as children. So you could imagine this is a tree with zero or multiple leafs. It doesn’t matter how deep the structure goes, but I would recommend to not make it too complicated. One big benefit of this structure is the concurrency model and scalability.
Scaling Verticles
Each child verticle can be deployed multiple times and as each verticle has its own thread, multiple threads can be used without concurrent access issues. In the above example we can see verticle N is deployed 4 times.
To summarize, verticle code is executed on a non-blocking event loop thread. So inside a verticle thread safety is guaranteed. Vertx uses multiple event loops for an application that’s called multi reactor pattern. It is important that inside a verticle only Non-blocking code is executed to guarantee fast processing of events. Otherwise, worker verticles should be used. We are going to learn more details later on.
Verticles typically communicate over the Vert.x event bus. There is a separate tutorial on the event bus as well. Typically, all verticles use the same vertx instance, so don’t create your own ones. Vertx is handling this for you and the whole verticle model is optional, but it makes sense to use it to avoid concurrency issues and to make it easier to scale your application.
Lifecycle of Verticles
A verticle has a simple lifecycle consisting of three stages:
- start(): Called when the verticle is deployed. You put your initialization code here.
- stop(): Called when the verticle is undeployed. You put your cleanup code here.
We have heard the theoretical part about verticles, so let’s look in our code, how this looks like and how we can use Vert.x to our favor. For this, I’m going to create a new package called verticles
in our vert.x starter project, and there I’m creating a new main verticle. We also need to extend from AbstractVerticle.
package com.ashok.vertx.vertx_starter.verticles;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
/**
*
* @author ashok.mariyala
*
*/
public class MainVerticle extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(MainVerticle.class);
public static void main(String[] args) {
final Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new MainVerticle());
}
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
}
}
This main verticle has a public static void main class. So basically a standard Java main class. And here we are creating a new vertx instance. As you remember, there should only be one vertx instance. So this is our main entry point.
And here we are deploying the main verticle with vertx.deployVerticle(new MainVerticle());
. We are deploying our current class.
When we have extend AbstractVerticle
, we can override the start method. So when the verticle is deployed, the start method is called. When we have this, we are going to start to create the structure we have seen previously.
So we have a main verticle with a child verticle A and a child verticle B. So let’s create a new class verticle A and extend it from abstract verticle. And in here we are overriding the start method again. So it’s basically the same as in the main verticle.
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleA extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
}
And we are printing the name. Also make sure to complete the promise with startPromise.complete();
. After that we can go into the main verticle (this is now the parent).
The parent must make sure to deploy their children as verticle A should be a child of the MainVerticle
. We need to call vertx.deployVerticle(new VerticleA());
. We need to signal as well when the verticle was started. So we need to add a new line with startPromise.complete();
and this will complete the start up of the main verticle.
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
vertx.deployVerticle(new VerticleA());
startPromise.complete();
}
Above snippet should be present in MainVerticle
class.
Also verticle B should be a child of the main verticle. So let’s create a new class verticle B
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleB extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
}
Now we need to call vertx.deployVerticle(new VerticleA());
in main verticle.
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
vertx.deployVerticle(new VerticleA());
vertx.deployVerticle(new VerticleB());
startPromise.complete();
}
So our main verticle looks like
package com.ashok.vertx.vertx_starter.verticles;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
/**
*
* @author ashok.mariyala
*
*/
public class MainVerticle extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(MainVerticle.class);
public static void main(String[] args) {
final Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new MainVerticle());
}
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
vertx.deployVerticle(new VerticleA());
vertx.deployVerticle(new VerticleB());
startPromise.complete();
}
}
Now we can run the main method and start our application in the log output.
3:29:27 pm: Executing ':MainVerticle.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :MainVerticle.main()
Start com.ashok.vertx.vertx_starter.verticles.MainVerticle
Start com.ashok.vertx.vertx_starter.verticles.VerticleA
Start com.ashok.vertx.vertx_starter.verticles.VerticleB
In the above output, We see start main verticle, start verticle A and start verticle B that shows all start methods of the different verticles have been called, whereas the parent has been started first. Great. That’s a good start.
Now let’s also see how to define a more complex structure and add some children to verticle A. So I’m now copying verticle A and name it verticle AA Then let’s add another one with verticle AB.
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleAA extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleAA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
}
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleAB extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleAB.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
}
Now we can go inside verticle A and there we are simply defining vertx.deployVerticle(new VerticleAA());
and vertx.deployVerticle(new VerticleAB());
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleA extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
vertx.deployVerticle(new VerticleAA());
vertx.deployVerticle(new VerticleAB());
startPromise.complete();
}
}
That’s it. This is pretty similar to how we have it in the main verticle. When we have this, we can go back to our main verticle and start the main method again to see some log output.
Now we can run the main method and start our application in the log output.
3:54:31 pm: Executing ':MainVerticle.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :MainVerticle.main()
Start com.ashok.vertx.vertx_starter.verticles.MainVerticle
Start com.ashok.vertx.vertx_starter.verticles.VerticleA
Start com.ashok.vertx.vertx_starter.verticles.VerticleB
Start com.ashok.vertx.vertx_starter.verticles.VerticleAA
Start com.ashok.vertx.vertx_starter.verticles.VerticleAB
So it’s really going down the tree and the parents are always deployed first. We have learned a lot about the structure of verticles already. And the last part missing is the deployment of verticles. Additionally to the Start method, there is also a stop method that can be used to clean up resources properly when an application shuts down.
To showcase the behavior, we are going to deploy verticle AA after it was deployed with vertx.deployVerticle()
method second parameter.
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleA extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
vertx.deployVerticle(new VerticleAA(), whenDeployed -> {
LOG.debug("Deployed {}", VerticleAA.class.getName());
vertx.undeploy(whenDeployed.result());
});
vertx.deployVerticle(new VerticleAB(), whenDeployed -> {
LOG.debug("Deployed {}", VerticleAB.class.getName());
// Do not undeploy
});
startPromise.complete();
}
}
We know that we are deploying it as second parameter. We can define a callback. This is called when the deployment of the verticle is done. Here we are in an asynchronous callback and when this one completes we can manually undeploy verticle using vertx.undeploy(whenDeployed.result())
. Here whenDeployed.result()
will return deployment id.
Then we are overriding the stop method in the verticle AA and also make sure to complete the stop promise. So we signaled Vertx when the verticle was undeployed. We also adding this code to verticle AB but we are not deploying AB so we should not see a message there. So a second parameter we are defining the when deployment callback again with a message deployed. Here we don’t undeploy it. So we should see the behavior that verticle AA is undeployed and verticle AB not.
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleAA extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleAA.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
@Override
public void stop(final Promise<Void> stopPromise) throws Exception {
LOG.debug("Stop {}", getClass().getName());
stopPromise.complete();
}
}
package com.ashok.vertx.vertx_starter.verticles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
/**
*
* @author ashok.mariyala
*
*/
public class VerticleAB extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(VerticleAB.class);
@Override
public void start(final Promise<Void> startPromise) throws Exception {
LOG.debug("Start {}", getClass().getName());
startPromise.complete();
}
@Override
public void stop(final Promise<Void> stopPromise) throws Exception {
LOG.debug("Stop {}", getClass().getName());
stopPromise.complete();
}
}
So let’s start the main verticle and see the output.
Now we can run the main method and start our application in the log output.
4:21:16 pm: Executing ':MainVerticle.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :MainVerticle.main()
Start com.ashok.vertx.vertx_starter.verticles.MainVerticle
Start com.ashok.vertx.vertx_starter.verticles.VerticleA
Start com.ashok.vertx.vertx_starter.verticles.VerticleB
Start com.ashok.vertx.vertx_starter.verticles.VerticleAA
Start com.ashok.vertx.vertx_starter.verticles.VerticleAB
Deployed com.ashok.vertx.vertx_starter.verticles.VerticleAA
Stop com.ashok.vertx.vertx_starter.verticles.VerticleAA
Deployed com.ashok.vertx.vertx_starter.verticles.VerticleAB
So we can see the main verticle was started, verticle A, verticle B, verticle AA, verticle AB and then when the verticle a deployment was completed, we see a output of stop because we undeployed it immediately. As we are not undeployed verticle AB, verticle AB stop method not invoked.
In a real application. You should not have the need to undeploy verticles manually, but often there is the need to clean up resources like database connections or others before the application shuts down. Now you know how this works and that you can do this in the stop method of a verticle.
Key Features of Vert.x Verticles
- Asynchronous: Verticles handle events asynchronously, meaning they don’t block the event loop.
- Lightweight: Verticles are very lightweight, making them easy to deploy and scale.
- Isolation: Verticles are designed to be isolated, with their own execution context.
- Reactive: Verticles react to events (like HTTP requests, messages, timers) and execute based on these events.
Types of Verticles
There are two main types of verticles in Vert.x:
- Standard (Event-Loop) Verticles:
- They run on the event loop and are designed for handling asynchronous tasks like I/O operations, event handling, and HTTP requests.
- They should never block the event loop by performing long or blocking operations like heavy computation or database access.
- Worker Verticles:
- Designed to handle blocking operations (e.g., CPU-bound tasks or blocking I/O).
- They are executed on a separate thread pool, so they don’t interfere with the event loop.
- They are perfect for interacting with blocking APIs (e.g., databases that do not have asynchronous drivers).
Summary of Vert.x Verticles
- Standard Verticles: Run on the event loop, handle asynchronous operations.
- Worker Verticles: Handle blocking operations on a separate thread pool.
- Event Bus: Facilitates communication between verticles.
- Deployment Options: Used to control how verticles are deployed and scaled.
Verticles provide the building blocks of a Vert.x application, allowing you to create modular, reactive, and scalable systems that handle asynchronous events efficiently.
That’s all about the Vert.x Verticles with example. If you have any queries or feedback, please write us email at contact@waytoeasylearn.com. Enjoy learning, Enjoy Vert.x tutorials..!!