Есть ли какие-либо преимущества в использовании не асинхронных действий в Play Framework 2.2?
Документация Play 2.2 гласит, что:
Из-за того, как работает Play, код действия должен быть максимально быстрым (т. Е. Неблокирующим). Так что же нам возвращать в качестве результата, если мы еще не можем его сгенерировать? Ответ - будущий результат!
Future[Result] будет в конечном итоге выкуплен значением типа Result. Предоставляя Future[Result] вместо обычного Result, мы можем быстро сгенерировать результат без блокировки. Тогда Play будет служить этому результату, как только обещание будет погашено.
Веб-клиент будет заблокирован во время ожидания ответа, но на сервере ничего не будет заблокировано, и ресурсы сервера могут использоваться для обслуживания других клиентов.
Действия, которые возвращают будущее, созданы Action.async
в отличие от Action.apply
для нормальных, не асинхронных действий.
Есть ли польза от не асинхронных действий? Мне кажется, что лучший способ убедиться, что ни одно из моих действий не будет заблокировано, - это объявить их все, используя Action.async
,
Фактически, согласно документации Play Framework 2.3, в Play 2.3 все выглядит так, что все действия асинхронные:
Примечание. Как Action.apply, так и Action.async создают объекты Action, которые обрабатываются одинаково. Существует один вид действий, который является асинхронным, а не два вида (синхронный и асинхронный). Конструктор.async - это просто средство для упрощения создания действий на основе API, которые возвращают будущее, что облегчает написание неблокирующего кода.
1 ответ
Просто потому, что вы могли бы использовать Action.async
, не означает автоматически, что вы не блокируете. Все зависит от того, используете ли вы блокирующий API или нет.
Таким образом, Play 2.2 работает так же, как и Play 2.3. На самом деле нет разницы между Action.apply
а также Action.async
кроме своих подписей. Action.async
ожидает некоторый блок кода, который возвращает Future[Result]
, в то время как Action.apply
ожидает блок кода, который возвращает Result
, Action.apply
преобразует это block: => Result
в Future[Result]
просто позвонив Future.successful(block)
, (Перед звонком идет еще немного работы Future.successful
, но это суть этого.)
Таким образом, варианты использования для каждого сводятся к API, который вы используете. Например, JDBC против ScalikeJDBC-async, блокирующие и неблокирующие API-интерфейсы базы данных. Допустим, вы выбираете пользователя из базы данных и отправляете его обратно клиенту как json.
Сигнатура типичной JDBC-поддерживаемой функции может выглядеть следующим образом (игнорируя ошибки для упрощения):
def read(id: Long): User
Ваша функция контроллера может выглядеть так:
def read(id: Long) = Action {
Ok(Json.toJson(User.read(id))
}
Это примерно эквивалентно тому, что Action.apply
делает:
def read(id: Long) = Action.async {
Future.successful(Ok(Json.toJson(User.read(id)))
}
User.read
однако блокирующий вызов JDBC по-прежнему блокируется, так что это не лучше, чем раньше.
Теперь предположим, что мы используем асинхронный вызов БД, который выглядит примерно так:
def read(id: Long): Future[User]
Функция контроллера будет выглядеть примерно так:
def read(id: Long) = Action.async {
User.read(id).map(user => Ok(Json.toJson(user)))
}
Думайте об этом как о помощнике для использования API, которые возвращают Future
s. Реальные преимущества приходят от реальных асинхронных реализаций этих API. Если вы застряли с блокирующим API (возможно, JDBC), есть и другие способы управления этим. Эта тема в списке рассылки Play - хорошая статья на эту тему: https://groups.google.com/forum/.