May 12, 2015

My First Ratpack App: What I Learned,

Recently I had the opportunity to rewrite an existing Spring Boot application as a Ratpack application at my current client. The application is a sort of security proxy to pass through requests from the wild, untamed internet to sane, internal services. We decided to try Ratpack over Spring Boot for the potential performance gains that Ratpack should provide.

If you’ve been living under a rock and haven’t heard of Ratpack, it’s a collection of libraries for building web applications.  It’s not full stack like Grails, so you will need to plug in pretty much anything such as an ORM layer, if you need it.  Ratpack will certainly work to build a monolithic application with a frontend, but it seems well suited to a microservice architecture

A big difference between Spring Boot and Ratpack is the threading model.  Spring Boot follows the typical thread-per-request model which means that the thread that handles the request also handles all downstream processing.  If accessing a database or making an outgoing service call is part of the request, the thread is bound for the whole lifetime of that request, even while waiting for the database or webservice call to return.  The threadpool is arbitrarily set to a certain size based on various metrics (or the default value if no performance testing was completed).

Ratpack does things a little differently.  The threadpool is sized to the number of processor cores available in the system.  This will typically mean a much smaller threadpool than with Spring Boot and Tomcat, for example.  When a request comes in, a compute thread handles it.  If blocking IO needs to be performed, that work is passed off to an asynchronous blocking thread and the compute thread goes back to handling requests.  When the blocking thread is ready, a compute thread picks execution back up from the blocking thread.  This model should provide better performance with lower resource utilization.

All my code samples are using Ratpack version 0.9.15 and will be in Groovy, but Java is, of course, supported.  Groovy just works so much better.  I realize the code samples are short and contrived, but I was trying to keep them short.

Java 8

To use the latest and greatest versions of Ratpack, you will need Java 8 installed and on your path. Make sure when you run java -version that version 8 is displayed.  Ratpack takes advantage of some of the facilities that Java 8 provides like lambda expressions.

Get a skeleton project setup with Lazybones

Lazybones is a great way to get various applications up and running with little effort. Peter Ledbrook wrote it specifically to bootstrap a Ratpack project and it grew to include other frameworks and project types.

You should be able to point your browser to http://localhost:5050 and see a basic index page indicating everything worked.

When I started up my application, many screenfuls of exceptions are thrown and displayed. For some reason the latest Springloaded libraries didn’t make it up to Bintray JCenter or Maven Central repositories. So if you want to use Springloaded, you will need to add in the Spring repository and change the version of the Springloaded artifact in your build.gradle file:

The Ratpack.groovy file always reloads on code changes, with or without Springloaded. In Intellij Idea, you will need to do a ‘Make Project’ for other code changes to be picked up when using Springloaded.

Reading External Configuration is Easy

Ratpack allows you to load properties from different sources like regular old .properties files to YAML files and even settings from the environment.  The settings can be loaded into an object if desired or even into a Map or a String if it’s a basic key/value property.  To enable the configuration module, add this line to your gradle.build dependencies:  compile ratpack.dependency(‘config’)

Here are some examples of setting up the configurations:

They can all be loaded at once and if a property is found in more than one place, the last one is used:

The .properties file overwrites the property ‘property2.subproperty2’ from the yaml file.  Here is the intended output:

I will admit, I never got the environment variable to work.  The default is to look for environment variables prefixed with ‘RATPACK_’ and system properties that start with ‘ratpack.’.  Let me know if you figure it out.

Different Ways To Use Handlers

Handlers are the entry point into your application code.  They may or may not have a URL mapping associated with them.  The normal use case is to chain some handlers together into a pipeline to get some work done.  Handlers are executed in order and a handler MUST either generate a response or delegate to another handler by calling next() or inserting another handler.

This is a very basic example of using the Groovy DSL in Ratpack.groovy.  This will respond to all requests to the application.

If you want to respond only to certain HTTP methods and mappings, here is another simple example:

The above code can be moved out of the Groovy DSL and into a regular class as a handler chain.  Here is the Ratpack.groovy file:

Here is the handler chain:

Alternatively, the handlers can be moved into their own classes and then inserted into the pipeline.

If you need more advanced mappings, use path variables and regular expressions:

Use Guice

Guice is Google’s lightweight dependency injection framework.  Ratpack has support for Guice built-in if you’d like to use it.  It makes most things much easier if you do.

Guice works by using constructor injection with the @Inject annotation (the one from com.google.inject.Inject, not javax.inject.Inject).  This is a basic example showing constructor injection.  The injected constructor parameters need to be bound in Guice or have a no-argument default constructor.

This example shows the configuration data and MyAppInjectService being bound and then used in the code.

Here is an example of using bindInstance to bind an instance of a service and then using that service in a handler:

Organize Into Modules

You can also load up Guice with modules of classes that contain related functionality.  These modules instantiate the objects required to get work done and can be reused across applications.

Modules are also useful for instantiating complex objects that can’t be instantiated with a simple constructor.  Guice provides a couple of annotations to help with this (@Provides and @Singleton).

Ratpack comes with a bunch of modules that you can add in if you need their functionality.  Some examples include modules for security, database access, metrics, and others.  They are defined like this in build.gradle:

A Handler can be used like a Servlet Filter

If the functionality of a servlet filter is needed, just add in another handler at the beginning or end of the chain.  For instance, if you need to check that a certain header exists for all requests, do something like this:

Error Handler

If you need to do something special when certain exceptions are thrown, create an error handler and bind it.

It works pretty well to handle them all in a case statement.  If you need more complex handling, it might be beneficial to register an error handler for that particular case.

Promises

To support it’s asynchronous nature, Ratpack provides the Promise class.  One of the main ways to use this asynchronous functionality is when a blocking operation is needed.  Blocking operations are things that take a relatively long period of time like database calls, file system operations, or webservice calls.  These blocking operations are performed in a separate thread pool from those that accept and process requests.  This separate thread pool is the secret to Ratpack’s performance gains over other web frameworks.

To setup a blocking operation in the Groovy DSL, use the blocking method:

To use the blocking threadpool in a service, inject ExecControl into it and call the blocking method on it.  Ratpack and Guice provide ExecControl for free without you having to do anything special.

The promise also provides a few methods for doing some processing on the returned results before they are handed back to the calling code.  The map method is a great example:

Note that the map() method runs in a compute thread.  Check out the API for further methods that can pre-process the Promised value.

Promises are Lazy But Order is Maintained

Promises are not executed right away.  They wait to execute until the current code block finishes.  The important thing about Promises is that they execute in the order they are defined which allows things to execute in a deterministic way.

The two service calls above don’t really do anything as implemented.  They just print out a log statement to prove the order that they executed in.  I added in a call to sleep for 5 seconds in the get() method to illustrate this powerful feature of Ratpack.  When executed, the println statement at the end is actually executed first, then the call to get (with a 5 second wait) and then the call to save.  The code will execute like this IN EVERY REQUEST because Promises execute at the end of the current code block and Promises are executed in the order they are defined.  Every time.  This leads to completely predictable behavior in Ratpack where in other asynchronous frameworks, you might not know what order things are executed.  This allows you to build up pipelines of (potentially) asynchronous processing.  Another great feature of this is that the Entertainer variable doesn’t need to be thread-safe since only one thread will be operating on it at a time.  That is a huge complexity saver.

Use the Built-In HttpClient (or not)

If you need to make webservice calls, Ratpack comes with a pretty decent HttpClient that returns a Promise out of the box.

In my case, I chose to use the Retrofit OkClient.  The reason for that is it’s much easier to mock out in unit and integration tests than the Ratpack HttpClient and it’s much more flexible.  I can still put the call on a blocking thread and not lose the asynchronicity.

Conclusion

It, admittedly, took a little bit of thinking (and refactoring) to get my code into what I think is the Ratpack way of doing things.  Setting up a pipeline of promises through a series of handlers and knowing that my code is executed in the correct order was a bit of a leap coming from Spring Boot.  But now that it is at that point, it makes a lot of sense.  While Ratpack might not be full-stack, it certainly has a lot of great features and I hope to explore more of them in the future.

About the Author

Brendon Anderson profile.

Brendon Anderson

Sr. Consultant

Brendon has over 15 years of software development experience at organizations large and small.  He craves learning new technologies and techniques and lives in and understands large enterprise application environments with complex software and hardware architectures.

One thought on “My First Ratpack App: What I Learned,

  1. Dan Woods says:

    Brendon,

    This post is comprehensive and demonstrates a thorough understanding of Ratpack — it is also flat out brilliant. As usual, Object Partners leads the way!!

    Dan

  2. Rus says:

    Excellent post! Full of really great info!

    In terms of the http client, if you use a blocking client then your app won’t scale as well as if you use the provided non blocking one. Although I don’t have any numbers to back that statement up 🙂

    If the main reason for not using it is around the ability to mock it out then one approach is to mock the target instead. And you can even use Ratpack to do that. If you look at example-books you can see an example of this. I set the mock target here https://github.com/ratpack/example-books/blob/master/src/test/groovy/ratpack/examples/book/BookFunctionalSpec.groovy#L23, override the target url here https://github.com/ratpack/example-books/blob/master/src/test/groovy/ratpack/examples/book/BookFunctionalSpec.groovy#L32 and then my app will be making calls to my mock service.

    If it’s more feature related then I recommend you look at https://github.com/AsyncHttpClient/async-http-client which is a Netty based non blocking http client that is much more feature rich. I believe you can make it use the same event loop as Ratpack too which means less threads.

    Your proxy use case sounds really interesting too. I’m not sure if you’ve seen the proxy support we have in Ratpack but this gist might be of interest https://gist.github.com/rhart/eb1b701f348a155f2dad

    1. Dan says:

      Hi Rus – I was trying out your gist for proxying, and it does not appear to work when using requestStream and StreamedResponse. The first attempt always works, however, subsequent requests hang. It works fine when swapped for request and ReceivedResponse. Any idea why that happens?

    2. Brendon Anderson says:

      Hi Rus:

      Thanks for the feedback!

      I was thinking since I wrapped my http client calls in a blocking{ } block, that it would have the same effect as using Ratpack’s HttpClient. Do you think that is the case?

      Thanks,
      Brendon

  3. Álvaro Sánchez-Mariscal says:

    I not only agree with Dan, but also think that part of this post should go to the official documentation to fill some gaps 🙂

  4. Antonio Espinoza says:

    What about the performance gain over spring boot?

    1. Brendon Anderson says:

      Hi Antonio:

      I have not had a chance to do any load testing to come up with some metrics, however, even if there are no significant performance gains, I expect Ratpack to require less resources due to it’s threading model.

      Thanks,
      Brendon

  5. Michele Nasti says:

    Hi, it seems that al the examples have disappeared. The gists are imported in a bad way. Can you fix it?

    1. Nick S says:

      Indeed! This is because a lot of the tags linking to the example gists have bad URLs with double backquotes surrounding them. Presumably some markup processing mistake.

      Here is a workaround for other readers. Works in Chrome, should work in Firefox too. Hit F12 to open the javascript developer pane, select the console, and paste the following code into the console. Hit enter. You should find the page reloads with all the gist URLs corrected.

      newdoc = document.documentElement.outerHTML.replace(/”/g,”); document.open(); document.write(newdoc)

      1. Nick S says:

        Hmm. Something is replacing quotes in comments with double backquotes, too. That script fragment has been broken. Trying again:

        newdoc = document.documentElement.outerHTML.replace(//g, “”); document.open(); document.write(newdoc)

        Or perhaps a friendly moderator will notice and be able to actually fix this problem at source.

  6. Bhanu says:

    i am new to java, Could you point out articles to integrate Spring Data JPA in fresh Ratpack application,

Leave a Reply to Rus Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]