Ответ 1
Несколько предположений:
1) HTTP-запрос на второй второй строке
2) Вы не говорите, должно ли перенаправление ждать ответа от Http-вызова, но я предполагаю, что он это делает.
Блокирующий вызов должен быть перенесен в другой поток, чтобы вы не блокировали потоки, обрабатывающие запросы. Play docs довольно конкретны. Функция Akka.future
в сочетании с Async
помогает.
Код контроллера:
1 def deleteNode(nodeId: Long) = Action { request =>
2 Async{
3 val response = Akka.future( BusinessService.businessLogic(nodeId) )
4
5 response.map { result =>
6 result map {
7 Redirect( routes.NodeRender.listNodes)
8 } recover {
9 InternalServerError("Failed due to ...")
10 } get
11 }
12 }
13}
Это немного больше, чем ваш PHP, но он многопоточен.
Код, переданный в Akka.future
в строке 3, будет вызываться в будущем с использованием другого потока. Но вызов Akka.future
немедленно возвращается с помощью Future[Try]
(см. Ниже тип возврата бизнес-метода). Это означает, что переменная response
имеет тип Future[Try]
. Вызов метода map
в строке 5 не вызывает код внутри блока карты, скорее он регистрирует этот код (строки 6-10) в качестве обратного вызова. Нить не блокируется в строке 5 и возвращает блок Future
в блок Async
. Блок Async
возвращает AsyncResult
для воспроизведения, и он сообщает Play регистрировать себя для обратного вызова, когда будущее будет завершено.
В то же время, какой-то другой поток выполнит вызов BusinessService
из строки 3, и как только HTTP-вызов, который вы делаете в обратную систему, вернется, переменная response
в строке 3 будет "завершена", что означает, что вызывающий вызов на линиях 6-10 вызывается. result
имеет тип Try
, который является абстрактным и имеет только два подкласса: Success
и Failure
. Если result
является sucess, тогда метод map
вызывает строку 7 и переносит ее в новый Success
. Если result
является сбой, то метод карты возвращает сбой. Метод recover
в строке 8 делает обратное. Если результат метода карты является успешным, он возвращает успех, иначе он вызывает строку 9 и обертывает ее в Success
(а не Failure
!). Вызов метода get
в строке 10 принимает перенаправление или ошибку из Success
, и это значение используется для завершения AsyncResult
, к которому принадлежит игра. Воспроизведение затем получает обратный вызов, что ответ готов и может быть отображен и отправлен.
Используя это решение, ни один поток, который обслуживает входящие запросы, не блокируется. Это важно, потому что, например, на 4-х основных машинах, Play имеет только 8 потоков, способных обрабатывать входящие запросы. Он не будет создавать новые, по крайней мере, не при использовании конфигурации по умолчанию.
Вот код из объекта Business Service (в значительной степени скопировал ваш код):
def businessLogic(nodeId: Long): Future[Try] {
val commitDocument = Json.toJson(
Map(
"delete" -> Seq( Map( "id" -> toJson( nodeId)))
))
val commitSend = Json.stringify( commitDocument)
val commitParams = Map( "commit" -> "true", "wt" -> "json")
val headers = Map( "Content-type" -> "application/json")
val sol = host( "127.0.0.1", 8080)
val updateReq = sol / "solr-store" / "collection1" / "update" / "json" <<?
commitParams <:< headers << commitSend
val commitResponse = Http( updateReq)()
Success(commitResponse) //return the response or null, doesnt really matter so long as its wrapped in a successful Try
}
Логика представления и бизнес-логика теперь полностью развязаны.
Подробнее см. https://speakerdeck.com/heathermiller/futures-and-promises-in-scala-2-dot-10 и http://docs.scala-lang.org/overviews/core/futures.html.