14
votes

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)

1
Run long lasting operations on a separate thread pool, or start a separate thread for each operation.Alexei Kaigorodov
@AlexeiKaigorodov: I would, but I haven't found such a possibility in Java api. I read about such a possibility in Scala api though. But I'm using Java right now.oo_olo_oo
you did not find how to start a thread or thread pool via java API?Alexei Kaigorodov
:-) no, I haven't found how to do that using native Play framework api. I could do it myself - this is one solution. but it requires lower level knowledge about the Play itself, in order to do it correctly. and may be not compatible with different Play helpers like e.g. action decorators, etc.oo_olo_oo
I am not an expert in Play, but I think starting a thread (pool) would do no harm. But the question is, how to deliver the result of long operation back into Play framework - some async mechanism should be used, instead of waiting for result on a Play-controlled thread.Alexei Kaigorodov

1 Answers

23
votes

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.