Question

My application uses Play framework to process REST requests. I need to perform some potentially long lasting blocking I/O operation in http request handler. In parallel I'd like to handle efficiently some short lasting requests.

As described here:

http://www.playframework.com/documentation/2.2.0/JavaAsync

long lasting operation can be run asynchronously. On the other hand, as described here:

http://www.playframework.com/documentation/2.2.x/ThreadPools

Play framework uses the same default thread pool, in which all application code is executed. At least in Java api there is no possibility to run asynchronous work on different thread pool.

So, my question is if it's worth to run the potentially blocking I/O operation asynchronously, considering the fact that such an operation uses the same thread pool anyway. Or maybe it's better to increase the default thread pool size, and don't bother with async api in such a case? (this way at least code readability would be much higher)

Was it helpful?

Solution

I would recommend that you set up your own context and run your blocking/cpu-intensive operations there using Plays F.Promise<A>. As always with threads, the optimal solution depends on numerous of things like number of cores etc.

First set up your context in applications.conf:

play {
  akka {
    akka.loggers = ["akka.event.Logging$DefaultLogger", "akka.event.slf4j.Slf4jLogger"]
    loglevel = WARNING
    actor {
      default-dispatcher = {
        fork-join-executor {
          parallelism-min = 1
          parallelism-factor = 2
          parallelism-max = 6
        }
      }
      my-context {
        fork-join-executor {
          parallelism-min = 1
          parallelism-factor = 4
          parallelism-max = 16
        }
      }
    }
  }
}

Then in your controller, make use of your context using Plays Promises (I'm using Java 8):

public static F.Promise<Result> love() {
    ExecutionContext myExecutionContext = Akka.system().dispatchers().lookup("play.akka.actor.my-context");

    F.Promise<Integer> integerPromise = F.Promise.promise(() ->
            LongRunningProcess.run(10000L)
    , myExecutionContext);

    F.Promise<Integer> integerPromise2 = F.Promise.promise(() ->
            LongRunningProcess.run(10000L)
    , myExecutionContext);

    return integerPromise.flatMap(i -> integerPromise2.map(x -> ok()));
}

This way your Play app will still handle short lasting requests on the default-dispatcher execution context and the blocking/cpu-intensive will run in my-context.

I made a very short example for you demonstrating this, check it out on github.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top