Note based on Vert.x Core and Advanced Vertx Guide.

1 Vertx

A Vert.x instance val vertx = Vertx.verx() is the conrol center of Vert.x. It can create clients and servers, get a reference to the event bus, set timers, as well as many other things. Most applications need a single Vert.x instance. It takes a VertxOptions argument that configures clustering, pool size and others. You can create a Vert.x object asynchronously, especially for the clustering config.

1.1 Async API

The Vert.x APIs are largely async event driven. The Golden Rule is: Don’t block in event handlers.

Vert.x calls event handlers using a thread called an event loop. A Vert.x instance maintain several event loops. A handler always uses the same event loop and never be executed concurrently. Vert.x logs a warning meesage if a hanlder is slow (default is more than 2s). It logs a warning ever sencond and log a message every second with stack trace if it takes more than 5 seconds.

There are two ways to run blocking code:

  • Use vertx.executeBlocking to call a blocking function. If the ordered is true, functions are executed serially. Otherwise, functions are executed parallely. The blocking function runs in a blocking thread, the result handler is exectued in the same event loop context. The uncaught exception in blocking function is captured by the aysnc result.
  • Use a worker verticle. A worker verticle is always executed in the worker thread pool.

Vert.x has a default worker pool, configured with workerPoolSize. Additional pool can be created using val executor = vertx.createdSharedWorkerExecutor("my-woker-pool"). If not needed, close it executor.close(). Excutors with the same name will share the same pool. A pool is destroyed when all its worker excutors are closed.

When several blocking tasks are submitted, the current implementation picks an available worker for executing the first task, after its execution, it will execute any pending tasks. After the executions of all the tasks, the worker stops and goes back in the worker pool.

1.2 Async Coordination

Vert.x Future supports concurrent and sequential composition. CompositeFuture.all takes several futures arguments (up to 6) and returns a future that is succeeded when all the futures are, and failed when at least one of the futures is failed. CompositeFuture.any takes several futures arguments (up to 6) and returns a future that is succeeded when one of the futures is, and failed when all the futures are failed. They can also take a list of futres.

1
2
3
CompositeFuture.all(futureList).onComplete{...}
CompositeFuture.any(future1, future2).onComplete{...}
CompositeFuture.join(future1, future2).onComplete{...}  // wait till all completed

Use compose to run future sequentially.

1.3 Periodic and Delayed Actions

Use vertx.setTimer for one shot action.s Use vertx.setPeriodic for periodic actions. Use cancelTimer to cancel a timer.

2 Verticle

A Vertcile is a chunk of code that gets deployed and run by a Vert.x. A Vert.x instance maintains N (default is core * 2) event loop threads. Verticles communicate with each other via event bus.

2.1 Create Verticles

Create a class implementing the Verticle interface or extending the AbstractVerticle class. The class usually defines start and stop methods.

There are commonly used types of verticles:

  • Standard Verticle: they are always executed using an fixed event loop thread.
  • Worker Verticle: These run in the worker pool. An instance is never executed concurrently by more than one thread. It is used for calling block code. Use setWorker(true) when deploy a verticle.

2.2 Deploy Verticles

Verticles can be deployed by instances or by names and can be deployed asynchornously. The deployment id can be used to undeploy a verticle. You can deploy multiple instances of a verticle.

Vert.x threads are non daemon threads so they will prevent the JVM from exiting. You can close a Vert.x instance if you don’t need it.

3 Context Object

When Vert.x provides an event to a handler or calls the start/stop methods of a Verticle, the execution is associated with a Context. A context controls the scope and order in which a set of handlers are executed. When Vert.x consumes callbacks, it assoicates a callback handler with a context:

  • if the current thread is a Vert.x thread, it reuses the context associated with this thread: the context is propagated.
  • otherwise a new context is created for this purpose.

However there is an exception: deploying a Verticle creates a new context for this Verticle, according to the deployment options of the deployment. Therefore a Verticle is always associated with a context.

Use vertx.getOrCreateContext() to get a context. A context is passed to the start or stop method of a verticle. A context can be one of different type: event loop context or a worker context. You can run code in the context asynchornously. You can put or get data in the context that can be shared by handlers in the same context.

3.1 Event Loop Context

An event loop context executes handlers on an event loop. When Vert.x creates an event loop context, it chooses an event loop for this context, the event loop is chosen via a round robin algorithm. An event loop context guarantees to always use the same thread.

Runing code in vertx.runOnContext uses an event loop thread of the vertx.

3.2 Worker Context

Worker contexts are assigned to verticles deployed with the worker option enabled. The worker context is differentiated from standard event loop contexts in that workers are executed on a separate worker thread pool. Worker contexts ensure that handlers are only executed on one thread at any given time. That is, handlers executed on a worker context will always be executed sequentially - one after the other - but different actions may be executed on different threads. A common pattern is to deploy worker verticles and send them a message and then the worker replies to this message.

The same worker verticle class can be deployed several times by specifying the number of instances. This allows to concurrently process blocking tasks.

Workers can schedule timers that fires on a different worker thread.

3.3 Embedding Vert.x

When Vert.x is embedded like in a main Java method or a junit test, the thread creating Vert.x can be any kind of thread, but it is certainly not a Vert.x thread. Any action that requires a context will implicitly create an event loop context for executing this action.

4 The Event Bus

There is a single event bus instance for every Vert.x instance. The event bus forms a distributed p2p messaging systems spanning multiple server nodes and multiple browsers. It supports pub/sub, p2p and request-response messaging. The operations include registering/unregistering handlers, sending and publishing messages.

An address of the event bus is simple a string. In p2p messaging, multiple handlers can work in a round-robin way. Messages are deliveried in a best-effort way. Types of messages include primitive/simple types, String, buffer or JSON messages. You can define message coders.

5 Buffers

A buffer is a sequence of zero or more bytes that can read from or written to and which expands automatically as necessary to accommodate any bytes written to it. You can perhaps think of a buffer as smart byte array. Buffer support copy and slicing. After writing a buffer to a socket or other similar place, they cannot be re-used.

5.1 Create Buffers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Create a new empty buffer
Buffer buff = Buffer.buffer();

// Create a buffer from a String.
// The String will be encoded in the buffer with a default using UTF-8.
Buffer buff = Buffer.buffer("some string");
Buffer buff = Buffer.buffer("some string", "UTF-16");

// form a byte[]
byte[] bytes = new byte[] {1, 3, 5};
Buffer buff = Buffer.buffer(bytes);

// Create a buffer with an initial size hint.
// The buffer is not filled with zeros
Buffer buff = Buffer.buffer(10000);

5.2 Write/read a buffer

There are two ways to write to a buffer: appending, and random access. In either case buffers will always expand automatically to encompass the bytes. It’s not possible to get an IndexOutOfBoundsException with a buffer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Buffer buff = Buffer.buffer();
buff.appendInt(123).appendString("hello\n");
socket.write(buff);

// setXXX takes a position in the buffe
buff.setInt(1000, 123);
buff.setString(0, "hello");

// Data is read from a buffer using the getXXX methods.
// Get methods exist for various datatypes.
// The first argument to these methods is an index in the buffer
// from where to get the data.
for (int i = 0; i < buff.length(); i += 4) {
  System.out.println("int value at " + i + " is " + buff.getInt(i));
}

// For unsigned numbers, use getUnsignedXXX, appendUnsignedXXX and setUnsignedXXX methods.
Buffer buff = Buffer.buffer(128);
int pos = 15;
buff.setUnsignedByte(pos, (short) 200);
System.out.println(buff.getUnsignedByte(pos));