Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Ratpack 1.0 Launches Aiming to make Asynchronous Programming Easier on the JVM

Ratpack 1.0 Launches Aiming to make Asynchronous Programming Easier on the JVM

This item in japanese

Ratpack, a high performance Java web framework, has reached 1.0 status. The 1.0 release is API-stable and can be considered production ready. The main thing that makes Ratpack interesting is the execution model, which aims to make asynchronous programming on the JVM easier. "There’s a problem domain in the JVM space that doesn’t have a solution at this point," Dan Woods argued during his presentation at SpringOne2GX, "so Ratpack has taken that on".

The Ratpack processing model is similar to node.js. This is what it looks like:

In a four core set-up like this one, when a Ratpack application starts up an event loop gets bound to each of the cores. From a performance standpoint, in common with most frameworks, Ratpack is CPU-bound in the sense that when it starts up the event loop it’s using for non-blocking processing it ties one thread per event loop to each of the cores on the system; in other words the more cores you have the more traffic you can satisfy. This is somewhat similar to how Servlet applications work except that with Servlets each request gets a new thread. The Servlet approach has the advantage of a straightforward programming model, but if you need to move data from one thread to another then, of course, you incur a CPU context switch and a corresponding performance penalty as the data is moved on from one core to another.

Through Netty, Ratpack creates an "Event Loop Group", which binds a single-threaded event loop to each available CPU core. Each event loop is participant in processing any of the non-blocking networking requests that come into an application. As non-blocking requests are received into an event loop, they are asynchronously delegated to Ratpack for processing.  

Woods points out that there are several trouble points for building non-blocking, asynchronous web applications on the JVM. Specifically, he notes that many JVM libraries - like those build on JDBC - are not built to be used asynchronously. He goes on to state that a value proposition of Ratpack's is that it can make "any API asynchronous". That is to say, through its "execution model", Ratpack is able to seamlessly schedule work between the event loop ("compute thread") or a blocking thread. Non-asynchronous, JDBC-like calls can then be wrapped in an asynchronous fixture that will place their processing outside of the request-taking thread pool. When the call completes, processing is returned to the event loop. An important attribute of this flow is that when blocking calls return, their processing is returned to the originating computation thread, thus not requiring an additional context switch to resume processing. This means that any data that was computed prior to the blocking call is already primed in the CPU's cache, leading to better performance and overall efficiency. This is very efficient. On a 32 core machine “we can get pretty close to a million requests/second for a simple “hello world” application using just a single instance of Ratpack,” Woods said.

Asynchronous programming on the JVM is notoriously hard partly because the virtual machine has no inherent concept of Continuations. It’s typically also non-deterministic, which is far from ideal for a web application where waiting for a timeout in order to know that a problem has occurred is hardly satisfactory. Ratpack has a concept the team refer to as an execution, a processing stream which is somewhat analogous to a continuation. For each promise type (a representation of a value which will become available later) where you are doing an asynchronous operation the framework creates a sub-processing stream which can be thought of as a continuation in its own right. The complete chain of these will execute in FIFO order. The stream is started with a marker, so that the framework can determine when the end of the stream is reached. If something goes wrong in an individual sub-stream, say some sort of silent failure, Ratpack has awareness of what processing should have happened so it can inform the client that it isn’t going to get a response, thus providing a mechanism for giving deterministic behaviour.

The core of Ratpack 1.0 is written in Java, requiring Java 8, with the build system using Gradle 2.6. It is built on top of the Netty non-blocking event-driven networking engine and provides a number of low-level constructs for working with asynchronous APIs. It has been specifically designed for building microservices with first class support for language agnostic transports such as JSON. Optional support for Netflix’s circuit breaker library Hystrix is provided, and it integrates with Dropwizard Metrics for reporting. Ratpack has an extensive configuration model with support for YAML, JSON, Java properties, system properties, environment variables and so on, and integrates with Pac4j for authentication. HTML templating is provided by both Groovy and Handlebars.

Despite the core being written in Java Ratpack has good support for Groovy, making use of some of the advanced features of the Groovy compiler. During his presentation Woods stated that these include “the ability to inform a closure as to what object type you’ll be delegating the processing to, so we can still get, for example, a statically typed DSL out of this thing… And at this point Groovy is performant enough, with static compilation and even with InvokeDynamic, that we can get pretty close to the performance that Java offers”.

Ratpack has a long history. Starting as a Groovy DSL implementation example in 2010, it evolved into a JVM Sinatra clone before dumping that legacy and focussing on NIO/Performance in 2012. As such, if you’re familiar with some of the early legacy and haven’t followed it since, it may well be worth another look.

Rate this Article