PJUG
Vert.x

Asynchronous Application Development on the JVM

June 18, 2013

Ravi Luthra

Contents


What we're focusing on today

Contents


Not focusing on...

But feel free to ask questions

Who's Involved

Tim Fox
Pidster
normanmaurer

Project Founder Tim Fox (purplefox)

Stuart Williams (pidster)

Norman Maurer

Scott Horn

What is Vert.x?

Vert.x is the framework for the next generation of asynchronous, effortlessly scalable, concurrent applications.

Vert.x is an event driven application framework that runs on the JVM - a run-time with real concurrency and unrivalled performance. Vert.x then exposes the API in Ruby, Java, Groovy, JavaScript and Python. So you choose what language you want to use. Scala and Clojure support is on the roadmap too.

Source

The inspiration for the project came from two main areas:

  1. The growing interest in non blocking, asynchronous approaches to application design motivated by scalability.
  2. The recent trend towards stripped down simplicity in APIs and a rejection of complexity that had dominated application platforms in the previous 10 or more years.
Source

What is Vert.x?

Runtime Concepts

  • Verticle: Basic execution unit of Vert.x
  • Module: Collection of Verticles
  • Vert.x Instances are Clusterable

Features

  • Polyglot: Java, JavaScript (Rhino), CoffeeScript, Ruby (JRuby), Python (Jython), Groovy, Scala and others coming soon
  • Concurrency: Multi-threaded event system, but each verticle instance is never multi-threaded.
  • Async: APIs are designed to accept callbacks for anything that blocks.

Event Bus

  • Publish Subscribe: interested parties subscribe, publisher may publish message to all subscribers.
  • Point to Point: Send message, get a response (and send a response to that!)
  • And where this get's interesting: This event bus extends the to the browser.

Core API

  • TCP/SSL servers and clients
  • HTTP/HTTPS servers and clients
  • WebSockets servers and clients
  • Accessing the distributed event bus
  • Periodic and one-off timers
  • Buffers
  • Flow control
  • Accessing files on the file system
  • Shared map and sets
  • Accessing configuration
  • SockJS

Features in Depth

Verticles

Verticles are instantiated by Vert.x, can use the Vert.x API's to compose applications. The start method is called when an instance is created. From here, Verticles will typically call various Vert.x APIs to set up handlers.

package org.pjug.vertxpres;

import org.vertx.java.platform.Verticle;
//            * A Java class can be run from the command line
//            | vertx run org/pjug/vertxpres/TestVerticle1.java
//            | (Compilation will happen in vertx)
//            v 
public class TestVerticle1 extends Verticle {

    public void start() {
        getContainer().logger().info("Test Verticle Started!");
    }
}
Create Servers, Set up Timers, use the Vert.x API
package org.pjug.vertxpres;

import org.vertx.java.core.Handler;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.platform.Verticle;

public class TestVerticle2 extends Verticle {

  public void start() {
//     * vertx Variable comes from the Verticle class that TestVerticle2
//     | extends. From here we have access to the rich Async APIs that
//     | Vert.x provides. This is anywhere from network clients, servers
//     | file I/O, timers and the shared data structures.
//     v
    vertx.createHttpServer()
//                           * Here's where the Async concepts become
//                           | clearly visible. There are optional
//                           | methods on most of the API's the provide
//                           | a user to pass in a callback.
//                           v
        .requestHandler(new Handler<HttpServerRequest>() {

//           * The APIs are designed with Java 8 and other languages
//           | that support "functional interfaces" that is interfaces
//           | with a single method. Languages like Java 8 and Groovy
//           | can instead use a type-hinted closure instead of all
//           | of the usual boiler plate code.
//           v
          @Override
          public void handle(HttpServerRequest request) {
//             * The response is part of the request object
//             | think of this as similar streamlining we get
//             | from the concepts behind Vert.x - to present
//             | a simpler application development platform.
//             | This is a concept Node.js started, and Vert.x
//             | takes to heart.
//             |                * For more methods available in the
//             |                | Vert.x API, please see the manual.
//             v                v
            request.response().end("Thanks!");
          }

        }).listen(8080);    
    vertx.setTimer(1000, new Handler<Long>() {
      
      @Override
      public void handle(Long event) {
        container.logger().info("1 second elapsed");
      }
    });
  }
}
Worker Verticles
Worker verticles are similar to normal Verticles, but are processed on threads that are blocking sensitive. If many worker threads being to block, it does not halt processing of other waiting work. Worker verticles are generally considered a plan-B for using APIs that have no async capabilities. It's always considered a better option to find an API that supports Async.

In addition to supporting blocking calls, worker verticles are also able to make network connections. Normal verticles will throw an exception if a network connection is made in a non-async way.

Event Bus

The Vert.x Event Bus is a distributed across the Vert.x cluster (when enabled, and if clustering and used). The event bus is used to encapsulate the concept of a messaging bus. The event bus is the core of how Vert.x distributes work, communicates and integrates various modules and verticles together.
  • Send/Register
    package org.pjug.vertxpres;
    
    import org.vertx.java.core.Handler;
    import org.vertx.java.core.eventbus.Message;
    import org.vertx.java.platform.Verticle;
    
    public class TestVerticle3 extends Verticle {
    
      public void start() {
        //     * To access the Event Bus API
        //     |          * Register a handler (subscription) to an address
        //     |          | and all messages published will arrive here.
        //     v          v
        vertx.eventBus().registerHandler("random", new Handler<Message<Long>>() {
    
          @Override
          public void handle(Message<Long> event) {
            //  * The event object contains a body method to access the primitive
            //  | value that was sent. 
            //  |   * The reply method is used to send a message back to the
            //  |   | caller. This is how communication on the eventBus works,
            //  |   | send a message, get a reply, which can be repeated any
            //  |   | number of times until no reply Handler is provided.
            //  |   |                               * event.body() is the message
            //  |   |                               | from the original send call
            //  v   v                               v
            event.reply((long) (Math.random() * event.body()));
          }
        });
    
        // Do the following every second
        vertx.setPeriodic(1000, new Handler<Long>() {
    
          @Override
          public void handle(Long event) {
            //                * Send a message to the address random.
            //                |                        * The optional third
            //                |                        | parameter is a Handler
            //                |                        | instance that can receive
            //                |                        | the reply message.
            //                v                        v
            vertx.eventBus().send("random", 1000L, new Handler<Message<Long>>() {
    
              @Override
              public void handle(Message<Long> event) {
                // Once the reply is received, handle is called, event.body()
                // is the random number sent from the service.
                container.logger().info(
                    "Random number from random service: " + event.body());
              }
            });
          }
        });
      }
    }
    vertx run org/pjug/vertxpres/TestVerticle3.java
    Random number from random service: 145 
    Random number from random service: 307 
    Random number from random service: 10 
    Random number from random service: 774 
    Random number from random service: 52 
    Random number from random service: 471 
    Random number from random service: 84 
    Random number from random service: 416 
    Random number from random service: 449 
    Random number from random service: 445 
    Random number from random service: 662 
    Random number from random service: 645 
    Random number from random service: 607 
    Random number from random service: 619 
    

Event Loop

concurrency

Blocking Calls in the Event Loop

concurrency

The golden rule: Don't block the event loop

concurrency

Modules

  • Packaged application or reusable set of verticles or class path.
  • Suitable packaging for application development
  • Re-use simplified by supporting repositories (Maven Center, Bintray, Local)
  • Runtime deployable, deployable via container.deployModule(...) as well as command line vertx runmod ...
  • Module Registry
  • IDE support through Maven and Gradle build environments
  • Module deployment example
    package org.pjug.vertxpres;
    
    import java.util.Date;
    
    import org.vertx.java.core.AsyncResult;
    import org.vertx.java.core.Handler;
    import org.vertx.java.core.eventbus.Message;
    import org.vertx.java.core.json.JsonObject;
    import org.vertx.java.platform.Verticle;
    
    public class TestVerticle4 extends Verticle {
    
      public void start() {
    
        //               * Construct a config object for the Module we're deploying
        //               |  The configuration manual is located on the website
        //               |  of the module itself. (These are typically github sites)
        //               v
        final JsonObject config = new JsonObject();
        config.putString("db_name", "test");
        config.putString("address", "vertx.mongopersistor");
    
        //                       * The module is specified by the name found at the
        //                       | main Vert.x Module registry. The module is
        //                       | downloaded at runtime. (the vertx process will
        //                       | actually fetch this module when the verticle
        //                       | hits this async operation.
        //                       v
        container.deployModule("io.vertx~mod-mongo-persistor~2.0.0-beta2", config,
            new Handler<AsyncResult<String>>() {
    
              @Override
              public void handle(AsyncResult<String> event) {
                if (event.succeeded()) {
                  // * Once we're sure the persistor has started, we can do
                  // | additional things. In this case we'll just call another
                  // | method that saves a single object in MongoDB. It's important
                  // | to run this code INSIDE this callback handler. If the module
                  // | is not set up when we try to send a message to be persisted
                  // | the message will not go anywhere. (Vert.x just eats messages
                  // | that are sent to addresses that nothing is listening on.
                  // v
                  afterDeploySuccess();
                } else {
                  container.logger().info(event.result());
                }
              }
            });
    
      }
    
      protected void afterDeploySuccess() {
        JsonObject doc = new JsonObject();
        doc.putNumber("RandomNumber", Math.random());
        doc.putString("timestamp", (new Date()).toString());
    
        JsonObject save = new JsonObject();
        save.putString("action", "save");
        save.putString("collection", "test");
        save.putObject("document", doc);
        vertx.eventBus().send("vertx.mongopersistor", save, new Handler<Message>() {
    
          @Override
          public void handle(Message event) {
            container.logger().info("Saved: " + event.body());
          }
        });
    
      }
    }
                

Shared Data

  • Shared data is a memory-based storage facility accessible from the vertx object.
    vertx.sharedData()
  • Stores maps and sets using a String name.
Start a net server
test1.js
//   * Access to the 'vertx' API
//   |       * Each supported language comes with a language module "shim"
//   |       | to make vertx programs fit well with the idioms of the language
//   |       | Here we see require() as is typical in Node.js or any "CommonJS"
//   |       | environment
//   v       v
var vertx = require('vertx');
var console = require('vertx/console');

// We see the API is often chained. This is typical in environments
// that support async features
vertx.createNetServer().connectHandler(function(sock) {
    console.log("We have a connection");
    sock.dataHandler(function(buffer) {
        console.log('RCV: ' + buffer);
    });
}).listen(8080);
vertx run test1.js
telnet localhost 8080
Start an HTTP server
test2.groovy
// * Note the vertx variable. In groovy this variable is simply added
// | to the default binding
// |                                    * Groovy supports closures!, so
// v                                    v    shall the Groovy vertx shim!
vertx.createHttpServer().requestHandler { request ->
    println "A request has arrived on the server!"
    request.response.putHeader("Content-Type", "text/plain")
//                   *  end(...) is a way of saying, close the stream and send
//                   v   this data.
    request.response.end("Hello!")
}.listen(8080)
        
See it!
vertx = require('vertx');

vertx.createHttpServer().requestHandler((req) ->
  req.response.end('<html><body><h1>Hello from vert.x!</h1></body></html>')
).listen 8080