Консольное приложение Scala никогда не завершается в ожидании будущего
Всякий раз, когда я запускаю процесс Scala, который использует результат Future
(либо через Await
, map
, onComplete
и т. д.), он никогда не завершается, вынуждая нас завершить процесс вручную. Это происходит ли я использую extends App
или просто стандарт def main(args: Array[String])
метод.
Мне кажется, что это связано с ThreadPoolExecutor
что скала раскрутится, чтобы выполнить Future
висит в конце функции, но я не могу получить ручку, чтобы закрыть его.
Например, следующий код не сможет выйти:
object ExecuteApi extends App with StrictLogging{
lazy val config = StratumConfiguration.setupConfiguration()
lazy val apiEndpoint = config.getProperty("APIEndpoint").split("/").head
lazy val packetApiPath = "packets/getpackets"
val resourceNames = sys.env.getOrElse("ResourceNames", "").stripMargin.split("[\n\\s]+").map(_.trim).filterNot(_.isEmpty)
val searchBody =
s"""{
| "resourceNames": [
| "${(resourceNames).mkString("\",\"")}"
| ]
|}""".stripMargin
logger.info(searchBody)
val responseFuture = AmazonAsyncApiGatewayHelper.executeHttpRequest(apiEndpoint, searchBody, Some(packetApiPath), Some("api"))
val response = Await.result(responseFuture, Duration(25, TimeUnit.SECONDS))
val layerDefinitions = Json.parse(response)
println(Json.prettyPrint(layerDefinitions))
}
В то время как этот код завершается просто отлично (единственное изменение - версия Async, которая возвращает будущее, которое затем ожидается):
object ExecuteAPI extends App with StrictLogging{
lazy val config = Configuration.setupConfiguration()
lazy val apiEndpoint = config.getProperty("APIEndpoint").split("/").head
lazy val packetApiPath = "packets/getpackets"
val resourceNames = sys.env.getOrElse("ResourceNames", "").stripMargin.split("\n").map(_.trim).filterNot(_.isEmpty)
val searchBody =
s"""{
| "resourceNames": [
| "${(resourceNames).mkString("\",\"")}"
| ]
|}""".stripMargin
logger.info(searchBody)
val layersResponse = AmazonapiGatewayHelper.executeHttpRequest(apiEndpoint, searchBody, Some(packetApiPath), Some("api"))
val layerDefinitions = Json.parse(layersResponse)
println(Json.prettyPrint(layerDefinitions))
}
Код в AmazonAsyncApiGatewayHelper
в конечном итоге создает Future, выполняя HTTP-клиент библиотеки Play. Однако мы видели это при выполнении Futures и другими способами:
val request = wsClient.url(fullUrl)
.withRequestTimeout(readTimeout)
val requestWithHeaders = headers.foldLeft(request) { (r, h) =>
r.withHeaders(h)
}
val playResponseFuture = requestWithHeaders.post(requestBody)
2 ответа
Мы закончили тем, что нашли проблему. Для Play wsClient требуется система актера (начиная с Play 2.5). Нам нужно было вручную завершить эту систему акторов, прежде чем мы вышли из нашего основного класса. Выходящий код выглядит так:
object ExecuteAPI extends App with StrictLogging{
try {
lazy val config = Configuration.setupConfiguration()
lazy val apiEndpoint = config.getProperty("APIEndpoint").split("/").head
lazy val packetApiPath = "packets/getpackets"
val resourceNames = sys.env.getOrElse("ResourceNames", "").stripMargin.split("\n").map(_.trim).filterNot(_.isEmpty)
val searchBody =
s"""{
| "resourceNames": [
| "${(resourceNames).mkString("\",\"")}"
| ]
|}""".stripMargin
logger.info(searchBody)
val responseFuture = AmazonAsyncApiGatewayHelper.executeHttpRequest(apiEndpoint, searchBody, Some(packetApiPath), Some("api"))
val response = Await.result(responseFuture, Duration(25, TimeUnit.SECONDS))
val layerDefinitions = Json.parse(response)
println(Json.prettyPrint(layerDefinitions))
} finally {
AmazonAsyncApiGatewayHelper.client.actorSystem.terminate()
}
}
Вы можете отобразить будущую стоимость и добавив Thread.sleep()
в конце.
val result = layersResponse map { futureValue =>
Json.parse(layerResponse)
}
Thread.sleep(10000)
println(Json.prettyPrint(result))