I'm using Play framework 2.2 for one of my upcoming web application. I have implemented my controllers in synchronous pattern, with several blocking calls (mainly, database).
For example,
public static Result index(){
User user = db.getUser(email); // blocking
User anotherUser = db.getUser(emailTwo); // blocking
...
user.sendEmail(); // call to a webservice, blocking.
return ok();
}
So, while optimising the code, decided to make use of Asynchronous programming support of Play. Gone through the documentation, but the idea is still vague to me, as I'm confused about how to properly convert the above synchronous block of code to Async.
So, I came up with below code:
public static Promise<Result> index(){
return Promise.promise(
new Function0<Result>(){
public Result apply(){
User user = db.getUser(email); // blocking
User anotherUser = db.getUser(emailTwo); // blocking
...
user.sendEmail(); // call to a webservice, blocking.
return ok();
}
}
);
}
So, I just wrapped the entire control logic inside a promise
block.
The play framework is asynchronous by nature and it allows the creation of fully non-blocking code. But in order to be non-blocking - with all its benefits - you can't just wrap your blocking code and expect magic to happen...
In an ideal scenario, your complete application is written in a non-blocking manner. If this is not possible (for whatever reason), you might want to abstract your blocking code in Akka actors or behind async interfaces which return scala.concurrent.Future
's. This way you can execute your blocking code (simultaneously) in a dedicated Execution Context, without impacting other actions. After all, having all your actions share the same ExecutionContext means they share the same Thread pool. So an Action that blocks Threads might drastically impact other Actions doing pure CPU while having CPU not fully utilized!
In your case, you probably want to start at the lowest level. It looks like the database calls are blocking so start by refactoring these first. You either need to find an asynchronous driver for whatever database you are using or if there is only a blocking driver available, you should wrap them in a future to execute using a DB-specific execution context (with a ThreadPool that's the same size as the DB ConnectionPool).
Another advantage of abstracting the DB calls behind an async interface is that, if at some point in the future, you switch to a non-blocking driver, you can just change the implementation of your interface without having to change your controllers!
In your re-active controller, you can then handle these futures and work with them (when they complete). You can find more about working with Futures here
Here's a simplified example of your controller method doing non-blocking calls, and then combining the results in your view, while sending an email asynchronous:
public static Promise<Result> index(){
scala.concurrent.Future<User> user = db.getUser(email); // non-blocking
scala.concurrent.Future<User> anotherUser = db.getUser(emailTwo); // non-blocking
List<scala.concurrent.Future<User>> listOfUserFutures = new ArrayList<>();
listOfUserFutures.add(user);
listOfUserFutures.add(anotherUser);
final ExecutionContext dbExecutionContext = Akka.system().dispatchers().lookup("dbExecutionContext");
scala.concurrent.Future<Iterable<User>> futureListOfUsers = akka.dispatch.Futures.sequence(listOfUserFutures, dbExecutionContext);
final ExecutionContext mailExecutionContext = Akka.system().dispatchers().lookup("mailExecutionContext");
user.andThen(new OnComplete<User>() {
public void onComplete(Throwable failure, User user) {
user.sendEmail(); // call to a webservice, non-blocking.
}
}, mailExecutionContext);
return Promise.wrap(futureListOfUsers.flatMap(new Mapper<Iterable<User>, Future<Result>>() {
public Future<Result> apply(final Iterable<User> users) {
return Futures.future(new Callable<Result>() {
public Result call() {
return ok(...);
}
}, Akka.system().dispatcher());
}
}, ec));
}
It you don't have anything to not block on then there may not be a reason to make your controller async. Here is a good blog about this from one of the creators of Play: http://sadache.tumblr.com/post/42351000773/async-reactive-nonblocking-threads-futures-executioncont
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With