play - как обернуть блокирующий код фьючерсами
Я пытаюсь понять разницу между этими двумя методами с точки зрения функциональности.
class MyService (blockService: BlockService){
def doSomething1(): Future[Boolean] = {
//do
//some non blocking
//stuff
val result = blockService.block()
Future.successful(result)
}
def doSomething2(): Future[Boolean] = {
Future{
//do
//some non blocking
//stuff
blockService.block()
}
}
}
В моем понимании разница между 2 состоит в том, какой поток является фактическим потоком, который будет заблокирован.
Таким образом, если есть поток: thread_1, который выполняет something1
, thread_1 будет заблокирован, а если thread_1 выполняется something2
, то новый поток будет выполнять его - thread_2, и thread_2 будет заблокирован.
Это правда?
Если так, то там нет ли действительно предпочтительного способа написать этот код? если мне все равно, какой поток в конечном итоге будет заблокирован, то конечный результат будет таким же.
dosomething1
кажется странным способ написания этого кода, я бы выбрал dosomething2
.
Есть смысл?
4 ответа:
Да,
Как сказал @AndreasNeumann, у вас могут быть разные контексты выполнения вdoSomething1
иdoSomething2
блокируют разные потоки, но в зависимости от вашего сценария это важное решение.doSomething2
. Представьте себе, что основной контекст выполнения-это тот, который получает HTTP-запросы от ваших пользователей. Блокировать потоки в этом контексте плохо, потому что вы можете легко исчерпать контекст выполнения и повлиять на запросы, которые не имеют ничего общего сdoSomething
.Play docs есть лучше объяснение возможных проблем с наличием блокирующего кода:
Если вы планируете написать блокирующий код ввода-вывода или код, который потенциально может выполнять большую нагрузку на процессор, вам нужно точно знать, какой пул потоков несет эту нагрузку, и вам нужно настроить его соответствующим образом. выполнение блокировки ввода-вывода без учета этого, вероятно, приведет к очень низкой производительности от Play framework , например, вы можете видеть, что обрабатывается всего несколько запросов в секунду, в то время как загрузка процессора составляет 5%. Для сравнения, тесты на типичном оборудовании для разработки (например, MacBook Pro) показали, что Play может обрабатывать рабочие нагрузки в сотни или даже тысячи запросов в секунду без пота при правильной настройке.
В вашем случае оба метода выполняются с использованием пула потоков Play default. Я предлагаю вам взглянуть нарекомендуемые рекомендации и посмотреть, нужен ли вам другой контекст выполнения или нет. Я также предлагаю вам: прочитайте Akka docs о диспетчерах и фьючерсах, чтобы лучше понять, что такое выполнение фьючерсов и имеют блокирующий/неблокирующий код.
Этот подход имеет смысл, если вы используете другой
execution contexts
во втором методе.Таким образом, имея, например, один для ответа на запросы и другой для блокировки запросов. Таким образом, вы будете использовать обычный
playExecutionContext
, чтобы приложение работало и отвечало, и отделятьblocking
операцию в другом.def doSomething2(): Future[Boolean] = Future{ blocking { blockService.block() } }( mySpecialExecutionContextForBlockingOperations )
Для получения дополнительной информации: http://docs.scala-lang.org/overviews/core/futures.html#blocking