Vert.x

Node for the JVM


WHOAMI


Ryan Applegate
@rappleg
https://github.com/rappleg 

Developer @ SmartThings
Grails Developer - 4+ years
Java Background - 7 years

What is Node?


Server Side Javascript
Event Driven Non-Blocking I/O
Single thread/single event loop
Application registers handlers
Events trigger handlers
Everything runs on the event loop

Node is Not Web Scale


Why Vert.x?


Same event-driven non-blocking IO programming model as Node
Polyglot (Groovy, Ruby, Java, Javascript, Python, Scala and Clojure coming soon)
Mature concurrency framework (JVM)
Hazelcast for Clustering
Interprocess Communication via Event Bus
Asynchronous & Effortlessly Scalable

Vert.x Caveat





Built on Netty and NIO2 for Network I/O

MUST be running Java 7


Benchmark #1

Benchmark #2


Verticle

The unit of deployment in vert.x is called a verticle (think of a particle, for vert.x). Verticles can currently be written in JavaScript, Ruby, Java, or Groovy
A verticle is defined by having a main which is just the script (or class in the case of Java) to run to start the verticle.
Verticle

Vert.x Instance

Event Loops
Verticle
Verticle
Verticle
Verticle
vertx run HelloWorld
-instances 4

Running Vert.x Server


     Server.groovy
vertx.createHttpServer().requestHandler { req ->   def file = req.uri == "/" ? "index.html" : req.uri              req.response.sendFile "webroot/$file" }.listen(8080) 
 
     Start the server 
vertx run Server.groovy
     
     Utilize more cores, up your instances...
vertx run Server.groovy -instances 32 

Running Vert.x Server (runmod)


No need to specify your classpath everytime to use additional classes in your verticle implementations

Package your application as a module

vertx runmod server-mod -instances 32 

Concurrency



Verticle instance ALWAYS executes on assigned thread/event loop.

Verticles have isolated classloaders and cannot share global state.

Write all your code as single threaded.
No more synchronized and volatile!
Verticle
Verticle
Verticle
Verticle
Event Bus

Event Bus Addressing



Address simply a String

Dot-style namespacing recommended  

"messages.inbound.foo"

Handler Registration

Handler 1

Handler 2

Handler 3
messages.inbound.foo

Handler Registration



def eb = vertx.eventBus()

eb.registerHandler("test.address") { message -> println "I received a message ${message.body}"}

Pub/Sub

Deliver single message to all handlers registered at an address
Handler 1

Handler 2

Handler 3
Sender
messages.inbound.foo

Pub/Sub

Deliver single message to all handlers registered at an address




eb.publish("test.address", "hello world") 

P2P

Deliver message to only one handler registered at an address
Handler 1

Handler 2

Handler 3
Sender
messages.inbound.foo

P2P

Deliver message to only one handler registered at an address




eb.send("test.address", "hello world") 

P2P Messaging Options




Send (Fire and Forget)

Request/Reply Model
Implement replyHandler for messages

Sender

eb.send("test.address", "Some message") { message ->
println "I received a reply ${message.body}"}

Receiver

eb.registerHandler("test.address") { message ->
println "I received a message ${message.body}"   // Do some work here message.reply("Some reply")}

Message Types


  • String
  • Primitives
  • Boxed Primitives
  • Boolean
  • java.util.map - representing JSON
  • org.vertx.java.core.JsonObject
  • org.vertx.java.core.Buffer

Vert.x in the Browser



Clustered along with Vert.x instances using HazelCast 

SockJS - Older browsers/Corp Proxy
Talk to event bus through SockJS Bridge

WebSockets - HTML 5 feature that allows a full duplex between HTTP servers

SockJS Server

def server = vertx.createHttpServer()
server.requestHandler { req -> if (req.uri == "/")  req.response.sendFile("index.html") if (req.uri.endsWith("vertxbus.js")) req.response.sendFile("vertxbus.js")}.listen(8080)
def sockJSServer = vertx.createSockJSServer(server)sockJSServer.bridge( { prefix: "/eventBus"} )
server.listen(8080)

SockJS Client

<script src="http://cdn.sockjs.org/sockjs-0.2.1.min.js"></script><script src="vertxbus.js"></script>
<script>var eb = new vertx.EventBus('http://localhost:8080/eventBus')
eb.onopen = function() { eb.registerHandler('msgs.echo', function(message) { alert('Echoing msg: ' + JSON.stringify(message)) });}

WebSockets on the Server


def server = vertx.createHttpServer()

server.websocketHandler{ ws ->

println "A websocket has connected!"

}.listen(8080, "localhost")

Rejecting WebSockets


def server = vertx.createHttpServer()

server.websocketHandler{ ws ->

  if (ws.path == "/services/echo") {

    Pump.createPump(ws, ws).start()

  } else {

    ws.reject()

}

}.listen(8080, "localhost")

WebSockets on the HTTP Client


def client = vertx.createHttpClient()

client.connectWebsocket("http://localhost:8080/some-uri") { ws ->

// Connected!

}

WebSockets in the browser

<script>

  var socket = new WebSocket("ws://localhost:8080/services/echo");

socket.onmessage = function(event) {

alert("Received data from websocket: " + event.data);

}

socket.onopen = function(event) {

alert("Web Socket opened");

    socket.send("Hello World");

  };

  socket.onclose = function(event) {

    alert("Web Socket closed");

};

</script>

Vert.x Shared State



Shared Data Object (vertx.sharedData())
ConcurrentMap or Set

Elements MUST be immutable values

Currently only available within a Vertx instance, not across the cluster

Allowed Values



  • Strings
  • Boxed Primitives
  • byte[]
  • org.vertx.java.core.buffer.Buffer
  • org.vertx.java.core.shareddata.Shareable

Shared Map



Verticle 1

def map = vertx.sharedData.getMap('demo.mymap')map["some-key"] = 123

Verticle 2

def map = vertx.sharedData.getMap('demo.mymap')// Retrieve value 123 from the mapdef value = map."some-key"

Shared Set



Verticle 1

def set = vertx.sharedData.getSet('demo.myset')set << "some-value"

Verticle 2

def set = vertx.sharedData.getSet('demo.myset')// Set will now contain some-valueset.contains("some-value")

Reactor Pattern Issues




  • MUST not block the event loop
  • Some work is natually blocking
    • Intensive data crunching
    • 3rd-party blocking API's (e.g. JDBC, etc...)
    • Node.js is not good for this type of work
Verticle
Verticle
Verticle
Verticle
Worker Verticle
Worker Verticle
Event Bus

Worker Verticle Example

public class FibWorker extends Verticle {  @Override  public void start() {    def eb = vertx.eventBus()    eb.registerHandler("fib.request") { message ->      def result = fib(message.body.intValue())      def resultMessage = { nbr: message.body,                            result: result }      eb.send("fib.response", resultMessage)    }  }
def fib(n) { n < 2 ? 1 : fib(n-1) + fib(n-2) }}

Verticle (Running on Event Loop)

public class WorkerExample extends Verticle {  @Override  public void start() {    def eb = vertx.eventBus()    eb.registerHandler("fib.response") { msg ->      println "Fib:${msg.body.nbr}=${msg.body.result}"    }
container.deployWorkerVerticle("worker.FibWorker")   { msg -> eb.send("fib.request", 20) } }}

More stuff with Vert.x Core APIs

  • 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
  • Logging
  • Accessing configuration
  • Writing SockJS servers
  • Deploying and undeploying verticles

How do I contribute to Vert.x?



IRC: irc://freenode.net/vertx

No support for the language you want to use?
Write a language handler for it and submit a pull request

Add more examples, modules, or update documentation

How does SmartThings use Vert.x?


Hubs/Clients need to maintain always open socket

amqp bus mode to push/pull events to/from Rabbit MQ

Event Bus to get messages to the right socket

SmartThings Vert.x Throughput



~6 million events/day from hubs to Vert.x
~70 events/second

Cluster of 3 Vert.x instances
Primary reason is stability, not throughput

Mirrored on ios and android clients

Vert.x Common Mistake


Once you have finished with the HTTP response you must call the end() method on it.

vertx.createHttpServer(someHttpServerConfig).requestHandler { request ->  def responseString = "ERROR"
  try {
    // Do some stuff with the request    	
    responseString = "OK"
  } catch (Exception e) {      responseString = "ERROR (${e.getClass().name})"  } finally {    request.response.end("${responseString}")
  }
}.listen(someHttpServerConfig.port)

Resources






web scale picture reference (watch video here)



Questions?



Slides Available

VERT.X

By rappleg

VERT.X

  • 7,541