[英]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.