Basics of Vert.x
A toolkit for building asynchronous and reactive app
Vert.x is a toolkit for building an asynchronous and reactive application on the JVM. Initially inspired and influenced by node.js and truly those that know node.js will probably find many common elements, but since its creation Vert.x has taken a course of its own toward providing easy-to-use, resource-efficient, asynchronous, and reactive development tools. Important to know is that Vert.x is polyglot since it supports most of popular JVM languages: Java, Scala, Groovy, Kotlin and more. Some of the most basic concepts in Vert.x that requires some explanation at the beginning are: Event loop, Verticle, Event bus.
Event loop concept is not specific to Vert.x in fact it’s common in the asynchronous programming model. Maybe you are already familiar with the concept but if you are not, here is a short explanation. Imagine you have one thread that is continuously running one infinite while loop executing the following steps:
In the first step of that loop we are taking the next element from the queue, often this is called event queue. Event can be anything like receiving network buffers, timing events, messages sent from different sources, etc.
In the second step, we are finding handler for that event.
In the third step, event is dispatched to the interested handler.
The important thing to realize is that with this approach we don’t need to worry about concurrency, only one thread at a time can access event. Event while it is in the queue no other thread is accessing it since it’s waiting to be processed, when it gets to event loop again only one thread is assigned to one event loop so no concurrency issue. However, there is one important constraint that we have to respect with this kind of programming approach. We should never run blocking code in the event loop. What does that mean?
Blocking code in this context means calls to some long-running operations that are blocking. For example when using a simple JDBC connection to execute some query, when we execute the query we are waiting (blocking) in the current thread for a response from DB. This is a “BIG NO” when using this kind of asynchronous programming model. The reason is simple if we block with this kind of operation, our event loop is blocked, it can’t dispatch events to handlers for further processing, it can’t take the next event from the queue. Of course, we need to run some long-running operations that are blocking, and for that Vert.x has special worker threads, which when are done running blocking operation send an event to some event loop, in a sense notifying us when the operation is done and we can continue processing results on the event loop, maybe even giving results to some other worker thread, depending on business logic. With this kind of nonblocking approach, the event loop can deliver a huge number of events in a short amount of time.
Verticle is essentially a building block provided by Vert.x to help us with writing simple and scalable applications.
Verticle has a lot in common with actor (from the actor concurrency model). In the actor model, the actor is some unit of computation, actors communicate by sending and receiving messages, and the main point is that they are completely isolated from each other and never share a memory, one actor can not directly change the state of another actor.
Verticles are deployed and run by Vert.x, we can deploy multiple instances of the same Verticle if the incoming load to that kind of Verticle is larger than one verticle instance can handle. Often you will have a verticle that starts some server that will route the incoming request to different handlers, maybe another verticle for interacting with a database, another for some information coming from some message broker, etc. (see img 2 and 3).
There are two types of verticles: regular & worker. Regular verticles are executed on the event loop and thus should never block. Worker verticles are not executed on the event loop, instead, they use worker thread taken from the worker pool. Regular verticles always have the same thread assigned to them, while worker verticles don’t always have the same thread assigned to them.
The Event bus is meant for sending and receiving messages in an asynchronous fashion. The most common usage is communication between verticles. Messages are sent to and retrieved from destinations. Really it’s a way to decouple verticles from each other, since verticle is inspired by actor model and actor model communicates via messages, we need verticles to communicate between each other in a similar fashion. The event bus provides three patterns of communication: point-to-point, request-reply & publish/subscribe.
Writing asynchronous code in Vert.x
As explained we can not block, so we have to write asynchronous code. Vert.x has several options how we can write async code.
Reactive Extensions (RxJava)
Kotlin coroutines (won’t be covered in this text)
Let’s take a look at simple example (listing 1) of callback style coding in Vert.x:
As we can see, we register requestHandler callback to out httpServer, in the requestHandler callback we call to DB verticle, and again provide a callback for the reply from that DB verticle, depending on the result in http response we return either results or error. As you can imagine, we can have maybe a few other operations that are asynchronous which would require us to put one callback in another ending up in callback hell. For this reason, Vert.x provides other styles to write asynchronous code.
In short, Promise is used to write an eventual value, and Future is used to read that value when it is available. The promise is created by a piece of code that wants to perform the async operation. From the promise object, we create a future object and register handlers to that future (listing 2). Once the async operation is done we complete the promise which triggers proper handler registered to future. Maybe it does not sound like much of an improvement compared to the callback, but the benefit comes when we have multiple chained async operations as well as error handling.
In this simple example (listing 2) we have the following steps:
In the beginning, we create the Promise, start async operation setTimer that will run after 2 seconds
After that, we get Future from that Promise.
If the promise is finished in error, we try to recover by using Future#recover functionality
After we do some sync operation using map operator
Then we chain our future with another future using flatMap
And in the end, handle the final result in onSuccess.
As we can see this kind of coding style enables us to write more concise and clearer code, and of course, combine different async operations.
Reactive Extensions (RxJava)
Before we take a look at an example, maybe a short explanation of what ReactiveX is.
ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences. Much like Vert.x, ReactiveX is cross-platform available for many languages. In this text, we are focused on Java implementation (RxJava).
In ReactiveX we have 3 basic concepts:
- Observable (source of event streams)
- Operator(s) (we chain operators to transform events coming from source)
- Observer (end subscriber to event stream)
Let’s take a look at a simple example:
Observable emits “events”, in this example it’s just pushing elements from the list one by one,
The first map operator subscribes to Observable and does some transformation, but what is worth mentioning is that for the next map operator, the first map operator becomes Observable, and so on, until we reach the final subscriber.
What is important to know is that unlike iteration over some list where we are effectively polling for data, here we have push from the source (Observable) to the next subscriber.
It’s worth mentioning a few different types of Observable:
Observable<T> – stream of events
Flowable<T> – stream of events with back-pressure support
Single<T> – emits exactly one event
Maybe<T> – may emit one event or none
For usage in Vert.x, it is similar to the usage of futures. It allows easier composition of asynchronous operations. For example:
It’s a similar example to the one given for Futures, again the point is how we can chain and combine different async operations.
Here in the output, we would either get :
event: from another source->GOOD
event: from another source->FAIL BUT RETURN THAT WE ACKNOWLEDGED IT FAILED
depending on the condition in the first map.
This was just a short introduction of some basic concepts related to Vert.x, truly each one of mentioned subjects deserves a blog for itself. But like you can see Vert.x gives us the ability to easily create asynchronous and potentially reactive applications while keeping complicated details like management of event loop, communication between verticles, etc. out of our code so we can focus on implementing business logic. Vert.x has a big ecosystem of modules that can be added to your project as needed, for example, communication with SQL databases via JDBC, interacting with mongo database, interacting with different message brokers, and so on. It’s recommended to use these modules whenever you can since they already provide asynchronous API, so you don’t have to write your own switch from synchronous code to asynchronous non-blocking code. If you are writing an application where you would benefit from writing your code in a reactive manner, Vert.x might be a good tool to start with, also if you write your application using reactive programming, it’s much easier to integrate that app into the reactive system. Designing reactive systems is more of a dev-ops topic, but maybe just to mention that Vert.x has a few modules to help us with realizing some of the “reactive manifesto” principles, for example it provides module for circuit breaker, service discovery. Of course, creating reactive systems is much more that than just this, but in many aspects, Vert.x can make things easier.