Vert.x is based around lightweight actors, called Verticles.
Verticle is an isolated unit of work, that can scale independently.
Usually, actor model requires actors to have a concept called “incoming mailbox” which is usually a queue (more specifically blocking buffered queue).
So, if one actor wants some work to be done by another actor, it will push a message into its mailbox.
What work this may be? Let’s take an animal shelter as an example. We’ll have one service that gets requests for registration of new animals in the shelter, and another, that actually stores them into some kind of a database.
Ok, that’s easy. What happens if we now want to fetch this cat information from the database?
Then DB Actor will need to send another message to HTTP Actor:
This is nice and all, but what if you decide to scale your DB actors, because it takes them a lot of time to process the messages? HTTP actor will need to track how many DB actors are available, and have some kind of logic for load balancing between them. Or we could introduce another actor, something like DB Routing actor, which will encapsulate this logic, but will make our architecture even more complex.
Vert.x takes another approach to the problem, called Message Bus.
So, what this Event Bus is all about?
Instead of actors being aware of one another, we introduce a middleman, that is aware of all actors, and all actors can communicate with it.
As you’ve probably guessed, this component called message bus, or Event Bus in Vert.x
Let’s see how the same flow will work with Event Bus and Verticles:
As you can see from the diagram, this is an implementation of Observer design pattern, or to be more precise, of its PubSub flavor.
So, with Event Bus in the middle, we can scale now DB Verticles without any problem. HTTP Verticle is aware of only Event Bus, and Event Bus will choose to which of the DB Verticles it will deliver the message.
Note though, that the arrow says “push to somebody who listens”.
If you send a message over Event Bus, and there are no subscribers, that message is lost forever, since Event Bus doesn’t persist messages in any way.
For the same reason there’s also no way to repeat old messages to new subscribers.
A common misconception regarding Event Bus in Vert.x is that there is an overhead when using it, even if your Verticles reside in the same JVM, as they usually do.
Let’s put that claim to test.
First, we’ll create a pretty big object (around 36MB):
One verticle will instantiate and send this object over the Event Bus:
While the other will receive it:
Let’s try to run this:
Something is obviously wrong:
java.lang.IllegalArgumentException: No message codec for type: class eventbus.BigSerializedObject
The way Event Bus in Vert.x works is that it can deliver messages to verticles running on different JVMs and written in different languages, as long as they are all part of the same Vert.x cluster.
For that reason, it requires to specify, how objects are encoded and decoded over the wire by specifying a codec for them.
Some objects, like
JsonObject have codecs out of the box.
But that’s obviously not our case.
Let’s create our own codec:
And register it:
Now we can see the results. I’m using pretty horrible code to track the timings, so it’s not part of the examples:
To create message: 45ms
To get message: 6ms
As you can see, when communicating inside the same JVM, object will be passed just as memory reference between verticles.
So, there’s literally no overhead for such case.
Here are few key points:
- Vert.x uses lightweight actor model
- Actors in Vert.x are called Verticles
- Verticles communicate using Event Bus
- Messages in the Event Bus are not persisted
- There’s no overhead in using Event Bus to pass messages withing same JVM
If you’re curious about other concepts in Vert.x framework, please let me know by leaving a comment.