Scala 控制台应用程序在等待 Futures 时永远不会退出
Scala console application never exits when awaiting Futures
每当我 运行 一个使用 Future
结果的 Scala 进程时(通过 Await
、map
、onComplete
等) ,它永远不会退出,迫使我们手动终止进程。无论我使用 extends App
还是仅使用标准 def main(args: Array[String])
方法,都会发生这种情况。
在我看来,它与 scala 将旋转起来执行的 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))
}
虽然这段代码退出得很好(唯一的变化是 returns 未来的异步版本,然后等待):
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
中的代码最终通过执行 Play 库 HTTP 客户端来创建 Future。但是,我们在以其他方式执行 Futures 时也看到了这一点:
val request = wsClient.url(fullUrl)
.withRequestTimeout(readTimeout)
val requestWithHeaders = headers.foldLeft(request) { (r, h) =>
r.withHeaders(h)
}
val playResponseFuture = requestWithHeaders.post(requestBody)
您可以映射未来值并在末尾添加 Thread.sleep()
。
val result = layersResponse map { futureValue =>
Json.parse(layerResponse)
}
Thread.sleep(10000)
println(Json.prettyPrint(result))
我们最终找到了问题所在。 Play wsClient 需要一个演员系统(从 Play 2.5 开始)。我们需要在退出主 class 之前手动终止这个 actor 系统。退出的代码如下所示:
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()
}
}
每当我 运行 一个使用 Future
结果的 Scala 进程时(通过 Await
、map
、onComplete
等) ,它永远不会退出,迫使我们手动终止进程。无论我使用 extends App
还是仅使用标准 def main(args: Array[String])
方法,都会发生这种情况。
在我看来,它与 scala 将旋转起来执行的 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))
}
虽然这段代码退出得很好(唯一的变化是 returns 未来的异步版本,然后等待):
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
中的代码最终通过执行 Play 库 HTTP 客户端来创建 Future。但是,我们在以其他方式执行 Futures 时也看到了这一点:
val request = wsClient.url(fullUrl)
.withRequestTimeout(readTimeout)
val requestWithHeaders = headers.foldLeft(request) { (r, h) =>
r.withHeaders(h)
}
val playResponseFuture = requestWithHeaders.post(requestBody)
您可以映射未来值并在末尾添加 Thread.sleep()
。
val result = layersResponse map { futureValue =>
Json.parse(layerResponse)
}
Thread.sleep(10000)
println(Json.prettyPrint(result))
我们最终找到了问题所在。 Play wsClient 需要一个演员系统(从 Play 2.5 开始)。我们需要在退出主 class 之前手动终止这个 actor 系统。退出的代码如下所示:
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()
}
}