Scala控制台应用程序在等待未来时从不退出

Scala控制台应用程序在等待未来时从不退出,scala,playframework,future,threadpoolexecutor,executioncontext,Scala,Playframework,Future,Threadpoolexecutor,Executioncontext,每当我运行一个scala进程,通过wait、map、onComplete等使用未来的结果时,它永远不会退出,迫使我们手动终止该进程。无论我使用的是extends App还是标准的def mainargs:Array[String]方法,都会发生这种情况 在我看来,这似乎与scala将启动以执行的ThreadPoolExecutor有关。未来,它将在函数末尾挂起,但我似乎无法找到它的句柄来结束它 例如,以下代码将无法退出: object ExecuteApi extends App with St

每当我运行一个scala进程,通过wait、map、onComplete等使用未来的结果时,它永远不会退出,迫使我们手动终止该进程。无论我使用的是extends App还是标准的def mainargs:Array[String]方法,都会发生这种情况

在我看来,这似乎与scala将启动以执行的ThreadPoolExecutor有关。未来,它将在函数末尾挂起,但我似乎无法找到它的句柄来结束它

例如,以下代码将无法退出:

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))
}
虽然此代码很好地退出,但唯一的更改是返回未来的异步版本,然后等待:

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 library HTTP客户端创建未来。然而,我们在以其他方式执行期货时也看到了这一点:

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))

您可以映射未来的值,并在最后添加Thread.sleep

val result = layersResponse  map { futureValue =>
 Json.parse(layerResponse)
}

Thread.sleep(10000)
println(Json.prettyPrint(result))

我们最终找到了问题所在。Play wsClient需要一个从Play 2.5开始的参与者系统。我们需要在退出主类之前手动终止这个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()
 }
}

我们最终找到了问题所在。Play wsClient需要一个从Play 2.5开始的参与者系统。我们需要在退出主类之前手动终止这个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()
 }
}

我在这两种情况下都看到了结果,但在第一种情况下,返回结果后进程永远不会退出?通常情况下,如果线程池创建守护进程线程,应用程序将悄悄退出。我将用这个案例的详细信息更新这个问题,但它会发生在我们运行的任何访问未来的程序中result@som-snytt,谢谢你的评论,它最终导致我们发现问题在于Play lib创建的一个线程池没有退出。我在这两种情况下都看到了结果,但在第一种情况下,在返回结果后,进程永远不会退出。对不起,Helper类在哪里?通常情况下,如果线程池创建守护进程线程,应用程序将悄悄退出。我将用这个案例的详细信息更新这个问题,但它会发生在我们运行的任何访问未来的程序中result@som-snytt,谢谢你的评论,它最终让我们发现问题在于Play lib创建的线程池没有退出。您需要将结果设置为var,并在将来设置它。不管怎样,这都有同样的问题。我更新了问题。您需要将结果设置为var,并在将来设置它。不管怎样,这都有同样的问题。我更新了问题。