Анимированная обработка и блокировка ввода-вывода в Java

В моем приложении используется среда воспроизведения для обработки запросов REST. Мне нужно выполнить некоторые потенциально длительные операции блокировки ввода-вывода в обработчике запросов HTTP. Параллельно я хотел бы эффективно обрабатывать короткие запросы.

Как описано здесь:

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

длительная операция может выполняться асинхронно. С другой стороны, как описано здесь:

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

Play Framework использует тот же пул потоков по умолчанию, в котором выполняется весь код приложения. По крайней мере, в Java api нет возможности запуска асинхронной работы в разных пулах потоков.

Итак, мой вопрос в том, стоит ли асинхронно запускать потенциально блокирующую операцию ввода-вывода, учитывая тот факт, что такая операция использует один и тот же пул потоков. Или, может быть, лучше увеличить размер пула потоков по умолчанию и не беспокоиться о async api в таком случае? (таким образом, хотя бы читаемость кода была бы намного выше)

Ответ 1

Я бы рекомендовал вам настроить свой собственный контекст и запустить интенсивные операции блокировки/процессора, используя Plays F.Promise<A>. Как всегда с потоками, оптимальное решение зависит от множества вещей, таких как количество ядер и т.д.

Сначала настройте свой контекст в 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
        }
      }
    }
  }
}

Затем в вашем контроллере используйте свой контекст, используя Plays Promises (я использую 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()));
}

Таким образом, ваше приложение Play по-прежнему будет обрабатывать короткие длительные запросы в контексте выполнения default-dispatcher, а интенсивность блокировки /cpu будет работать в my-context.

Я сделал очень короткий пример, чтобы продемонстрировать это, проверьте его на github.