Reactive programming is where the program reacts to events. With the popularity of event-driven, scalable, and real-time interactive architectures the concept of “reactiveness” is increasingly gaining attention. The concept is growing in importance in the Java domain in recent years as Netflix has created its RxJava library and Lightbend has created its Akka middleware stack.
The Reactive Manifesto has four main principles;
- Responsive – React in a timely manner
- Resilient – React to failure
- Scalable – React to increased load
- Message Driven – React to events
The responsiveness of an application is achieved through resilience and scalability. For example, let’s take the examination department website. The traffic will be at a peak when the results get published. So a user should have the same user experience and response time if he tries to log in to the website 5 minutes before the results get published or at the time the results get published. Responsive is not to be mistaken with the Responsive UI design, responsive is meant to be a consistent and rapid response.
Message-Driven architecture plays a significant role in the reactiveness of the application. This can be simplified by using the coffee making example. Let’s say I want to make a coffee, and I’m out of sugar. One option is to go to the shop, buy sugar, come home, boil the water, and brew the coffee. The other option is to boil the water while in the meantime, we can go buy the sugar and come back in time to make the rest of the coffee. This can be seen as a series of messages.
The reactive manifesto reflects the emerging field in nonblocking and scalable applications. Play is a stateless framework. This helps to quickly scale up horizontally, and play offers an asynchronous nonblocking model to help program with asynchronous data streams. Before talking about the asynchronous data model, let’s look at some significant features provided by the play framework.
- Hot Deployment: Modify the code, refresh the page, and instantly see the changes. This is made possible because play is stateless. It simply trashes away the old class loader and adds the new class loader when modifications are done. This is not possible with J2EE architectures because of its stateful. So discarding the previous class loader will discard the instances which have been created.
- Reactive: Asynchronous, Non-blocking IO model. This makes parallel data fetch easy and real-time web applications possible.
- Modularization: Everything is modular based and customizable
- Open Source: Github repo, proper documentation, and an Active community.
The asynchronous model in Play Framework
Many web applications nowadays access external web services. This involves a significant amount of waiting time incurring service and network latencies. In the traditional model, for each HTTP request, a new thread is spawned to serve the new request. This approach is infeasible when the number of HTTP requests increases because many spawned threads will be in the waiting stage to complete the database accessor to get the responses from the web service calls. When the server gets overloaded with requests, the new requests have to wait a while to get the response although many threads are in the idle state waiting for the response. This is known as the thread per request model.
The play tackles this by acting as an asynchronous and non-blocking HTTP server. In this model, the work related to network I/O is given to a small number of worker threads. Let’s say 400 concurrent requests have been fired up and 300 of those requests are going to make web service calls and only 100 of them are going to execute application level code, then the 300 requests can be handled by a very small number of worker threads.
Let’s Dive Deep
The following image shows the live threads in the JVM at the fresh start of a play project. Play framework comes with the default I/O workers and dispatcher thread pool. From the image, it can be seen that play starts with a fixed number of default threads in the pool. These threads can be divided into worker threads and application threads. Tasks that are related to network, I/O are given to a small number of worker threads.
Figure 1.0 Play Live threads view in Java VisualVM PAGE_BREAK: PageBreak
Play in Action
Figure 2.0 Code segment for asynchronous in Play
The Play offers the Future(Promise) concept to do asynchronous tasks. This can be used when calling external web services by using play.libs.WS API, or using play.libs.Akka to schedule asynchronous tasks.
Now let’s take the example above to action by doing a load test by creating 500 concurrent requests to the server. Apache Bench can be used for this purpose.
ab -n 500 -c 500 http://localhost:9000/
As from the terminal logs it can be seen that for each new request a new thread is not created and the same dispatcher thread has been used to serve many of the new requests. And new dispatcher threads have been created when the load increases.
Responsiveness in Action
From the results of the load testing, it can be seen, for the 500 requests, the response time remains the same. So for the increased amount of requests, the server responds with consistent speed.
When analyzing the threads during the load testing time, 8 more I/O worker threads have been created making it total (16 I/O worker threads and 2 I/O boss threads) and around 10 dispatcher threads. At the time of the request, a thread dump will show the worker threads in action. From the image, it can be seen the worker thread 17 is calling the Select system call.
A Selector is a Java NIO component that can examine one or more NIO Channels, and determine which channels are ready, for e.g. reading or writing. This way, a single worker thread can manage multiple channels, and thus multiple network connections. So when the WS resource is getting loaded, the worker thread checks continuously whether the socket is available to read.
So in summary, it is clearly visible with the Play approach, that 500 requests have been handled with less than 30 threads in the example that we tried with.
A simple way to execute a code block asynchronously is by using the promised helper:
Promise promiseOfInt = Promise.promise(() -> timeConsumingMethods());
But it should be noted that by simply wrapping the code in a promise will not make a code block run asynchronously. So it is always good to do a load test and observe the threads to know that your application makes use of the Play’s asynchronous model.