Какой эффект имеет использование Action.async, поскольку Play использует Netty, который не блокирует
Так как Netty является неблокирующим сервером, какое действие меняет действие на использование .async
?
def index = Action { ... }
против
def index = Action.async { ... }
Я понимаю, что с .async
вы получите Future[SimpleResult]
. Но так как Netty не блокирует, будет ли вообще играть что-то подобное под обложками?
Какое влияние это будет на пропускную способность/масштабируемость? Не трудно ли ответить на вопрос, где это зависит от других факторов?
Причина, по которой я спрашиваю, у меня есть собственный пользовательский Action
, и я хотел reset тайм-аут cookie для каждого запроса на страницу, поэтому я делаю это, что является вызовом async
:
object MyAction extends ActionBuilder[abc123] {
def invokeBlock[A](request: Request[A], block: (abc123[A]) => Future[SimpleResult]) = {
...
val result: Future[SimpleResult] = block(new abc123(..., result))
result.map(_.withCookies(...))
}
}
Убрать из вышеприведенного фрагмента я использую Future[SimpleResult]
, похоже ли это на вызов Action.async
, но это внутри самого моего действия?
Я хочу понять, какое влияние это окажет на мой дизайн приложения. Кажется, что только для возможности установить мой файл cookie на основе запроса я изменил с блокировки на неблокирующую. Но я смущен, так как Netty не блокирует, может быть, я на самом деле ничего не изменил, поскольку это было уже асинхронно?
Или я просто создал другой асинхронный вызов, встроенный в другой?
Надеясь, что кто-то сможет прояснить это с некоторыми деталями и как или каким эффектом это будет иметь производительность/пропускная способность.
Ответы
Ответ 1
def index = Action { ... }
неблокирует, вы правы.
Цель Action.async
состоит в том, чтобы упростить работу с Futures
в ваших действиях.
Например:
def index = Action.async {
val allOptionsFuture: Future[List[UserOption]] = optionService.findAll()
allOptionFuture map {
options =>
Ok(views.html.main(options))
}
}
Здесь моя служба возвращает Future
, и, чтобы избежать обработки результата, я просто сопоставляю его с Future[SimpleResult]
и Action.async
, который заботится обо всех остальных.
Если моя служба возвращала List[UserOption]
напрямую, я мог просто использовать Action.apply
, но под капотом все равно будет неблокировать.
Если вы посмотрите на исходный код Action
, вы даже увидите, что apply
в конечном итоге вызывает async
:
https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/api/mvc/Action.scala#L432
Ответ 2
Мне довелось встретить этот вопрос, мне нравится ответ от @vptheron, и я также хочу поделиться чем-то, что я прочитал из книги " Reactive Web Applications", что, я думаю, также Великий.
Конструктор Action.async
рассчитывает получить функцию типа Request => Future[Result]
. Действия, объявленные таким образом, мало чем отличаются от обычных вызовов Action { request => ... }
, единственное отличие состоит в том, что Play знает, что действия Action.async
уже асинхронны, поэтому они не обертывают их содержимое в будущий блок.
Правильно. По умолчанию Play будет планировать любое действие, которое будет выполняться асинхронно с его пулом веб-рабочих по умолчанию, завершая выполнение в будущем. Единственное различие между Action
и Action.async
заключается в том, что во втором случае заботились о предоставлении асинхронного вычисления.
Он также представил один образец:
def listFiles = Action { implicit request =>
val files = new java.io.File(".").listFiles
Ok(files.map(_.getName).mkString(", "))
}
что является проблематичным, учитывая его использование блокирующего API java.io.File
.
Здесь API java.io.File
выполняет операцию блокирующего ввода-вывода, что означает, что один из немногих потоков пула веб-рабочих Play будет захвачен, а ОС отобразит список файлов в каталоге выполнения. Это такая ситуация, которую вы должны избегать любой ценой, потому что это означает, что у пула работников может закончиться поток.
-
Инструмент реактивного аудита, доступный https://github.com/octo-online/reactive-audit, направлен на то, чтобы указать блокирующие вызовы в проекте.
Надеюсь, что это тоже поможет.