繁体   English   中英

使用 play-ws 读取许多主体时内存不足

[英]Out of memory when reading many bodies with play-ws

当使用 play WS standalone 从服务器读取多个主体时,我得到一个 OOM:

java.lang.OutOfMemoryError: Java heap space
    at java.lang.StringCoding$StringEncoder.encode(StringCoding.java:300)
    at java.lang.StringCoding.encode(StringCoding.java:344)
    at java.lang.String.getBytes(String.java:918)
    at akka.util.CompactByteString$.apply(ByteString.scala:872)
    at akka.util.ByteString$.apply(ByteString.scala:51)
    at play.api.mvc.Codec$.$anonfun$javaSupported$1(Results.scala:346)
    at play.api.mvc.Codec$$$Lambda$846/1241362979.apply(Unknown Source)
    at play.api.http.DefaultWriteables.$anonfun$wString$1(Writeable.scala:171)
    at play.api.http.DefaultWriteables$$Lambda$849/1109231015.apply(Unknown Source)
    at play.api.http.Writeable.toEntity(Writeable.scala:25)
    at play.api.mvc.Results$Status.apply(Results.scala:429)
    ...

您可以使用以下示例重现它:

val bigString: String = (1 to 1000000).mkString("")

val serverConfig = ServerConfig(port = Some(findFreeTcpRandomPort()))
val server = AkkaHttpServer.fromRouterWithComponents(serverConfig) { components =>
  import Results._
  import components.{defaultActionBuilder => Action}
{
  case GET(p"/big") => Action {
    Ok(bigString)
  }
}
}

val url = s"http://localhost:${server.httpPort.get}/big"
implicit val system: ActorSystem = ActorSystem()
implicit val mat: ActorMaterializer = ActorMaterializer()

val ws = StandaloneAhcWSClient()
try {
  val f = Future.traverse((1 to 1000).toList) { _ =>
    ws.url(url).get().map(_ => ())
  }
  Await.result(f, 1 hour)
} finally {
  ws.close()
  server.stop()
  system.terminate()
}

使用库:

"com.typesafe.play" %% "play-ahc-ws-standalone" % "2.0.3"
"com.typesafe.play" %% "play-akka-http-server" % "2.6.21"

似乎 ws 客户端正在累积响应而不清理它们。 如果我为每个请求创建并关闭一个新客户端,那么它就可以工作。

知道如何避免这种情况吗?

您并行运行了太多请求,特别是当您的每个响应正文的长度至少为5888896

为了证明问题不在于 ws 客户端,我将请求拆分为 100 个块,并且仅在前一个块完成时才开始接下来的 100 个块。

  val url                             = s"http://localhost:${server.httpPort.get}/big"
  implicit val system: ActorSystem    = ActorSystem()
  implicit val mat: ActorMaterializer = ActorMaterializer()

  val ws = StandaloneAhcWSClient()
  try {
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
    run100Requests()
  } finally {
    ws.close()
    server.stop()
    system.terminate()
  }

  def run100Requests(): Unit = {
    val f = Future.traverse((1 to 100).toList) { _ =>
      ws.url(url).get().map(_ => ())
    }
    Await.result(f, 1 hour)
  }

当我这样做时,我不再收到 OOM 错误。

因此,我认为您应该对进行中的请求数量进行一些限制。 (显然不要使用 Await.result )

最好的方法可能是将输入列表分块并为每个块发送请求。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM