Какой эффект имеет использование 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, направлен на то, чтобы указать блокирующие вызовы в проекте.

Надеюсь, что это тоже поможет.