Understanding suspendable requests

Most web frameworks bind an incoming HTTP request via the servlet to a thread. However, this means that each connection requires a thread, resulting in 300 concurrent connections needing 300 threads. Often, having that many threads does not scale when switching threads. This means that the computation time to manage threads and grant resources to them in a synchronized and fair mode increases with the amount of threads. Especially, when you consider the fact that most web connections are blocking and are waiting for something to happen in the background, like persisting an entity or executing another web request. This means that you can keep a small thread pool for accepting HTTP requests and have a bigger thread pool for executing business logic. Here comes the feature of continuations and suspendable requests into Play.

Getting ready

In order to have a simple test, you could create a small application which creates a big PDF report. Then access the URL mapped to the PDF report creation more often simultaneous than you have CPU cores. So you would have to request this resource three times at once on a duo core machine. You will see that a maximum two HTTP connections are executed simultaneously; in development mode it will be only one, regardless of your CPU count.

How to do it...

Play 1.2 introduces a new feature called continuations, which allows transparent suspension of threads including recovery without writing any additional code to do this:

public static void generateInvoice(Long orderId) {
    Order order = Order.findById(orderId);
    InputStream is = await(new OrderAsPdfJob(order).now());
    renderBinary(is);
}

Of course, the OrderAsPdfJob needs a signature like this:

public void OrderAsPdfJob extends Job<InputStream> {

    public InputStreamdoJobWithResult() {
        // logic goes here
    }
}

Note

There is an alternative approach in play before version 1.2, which needed a little bit more core but still allowed asynchronous and non thread bound code execution.

You can suspend your logic for a certain amount of time like this:

public static void stockChanges() {
   List<Stock> stocks = Stock.find("date > ?", request.date).fetch();
   if (stocks.isEmpty()) {
       suspend("1s");
   }
renderJSON(stocks);
}

Alternatively, you can wait until a certain job has finished its business logic:

public static void generateInvoice(Long orderId) {
    if(request.isNew) {
        Order order = Order.findById(orderId);
        Future<InputStream> task = new OrderAsPdfJob(order).now();
        request.args.put("task", task);
        waitFor(task);
    }
    renderBinary((Future<InputStream>)request.args.get("task").get());
}

How it works...

Following the three lines of code in the first example, you see that there is actually no invocation telling the framework to suspend the thread. The await() method takes a so-called Promise as argument, which is returned by the now() method of the job. A Promise is basically a standard Java Future with added functionality for invocation inside of the framework, when the task is finished.

The stockChanges() example is pretty self explanatory as it waits the defined amount of time before it is called again. This means that the operation is only called again if there was no updated stock available and it is very important it is called again from the beginning. Otherwise it will happily render the JSON output and has to be triggered by the client again. As you can see, this would be a pretty interesting starting point for implementing SLAs for your customers in a stock rate application, as you could allow your premium customers quicker updates.

The second example takes another approach. The controller logic is actually run twice. In the first run, the isNew parameter is true and starts a Play job to create the PDF of an invoice. This parameter is automatically set by the framework depending on the status of the request and gives the developer the possibility to decide what should happen next. The waitFor() tells the framework to suspend here. Again, after the task is finished, the whole controller method will be called again, but this time only the renderBinary() method is called as isNew is false, which returns the result by calling get() on the Future type.

There's more...

Always think whether introducing such a suspended response is really what you want or whether this just shows certain problems in your application design. Most of the time such a mechanism is not needed.

More about promises

Promises are documented in the javadoc at http://www.playframework.org/documentation/api/1.2/index.html?play/libs/F.Promise.html as well as in the play 1.2 release notes at http://www.playframework.org/documentation/1.2/releasenotes-1.2#Promises. There are even better features like waiting for the end of a list of promises or even waiting for only one result of a list of promises.

More about jobs

The job mechanism inside a Play is used to execute any business logic either on application startup or on regular intervals and has not been covered yet. It is however pretty well documented in the public documentation at http://www.playframework.org/documentation/1.2/jobs.

More information about execution times

In order to find out whether parts of your business logic need such a suspendable mechanism, use playstatus in your production application. You can check how long each controller execution took in average and examine bottlenecks.

See also

The recipe Integration with Munin in Chapter 7 shows how to monitor your controller execution times in order to make sure you are suspending the right requests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.96.94