Scala Akka http内存泄漏与web套接字
我有一个web服务器,它接受传入的websocket连接,在Scala中使用akka http实现。然而,我一直在观察我的应用程序内存使用的单调增加。经过长时间的挖掘,我发现每个连接都会创建一些内部Akka对象,但在客户端断开连接后不会得到清理。特别是,这个类:Scala Akka http内存泄漏与web套接字,scala,akka,akka-stream,akka-http,Scala,Akka,Akka Stream,Akka Http,我有一个web服务器,它接受传入的websocket连接,在Scala中使用akka http实现。然而,我一直在观察我的应用程序内存使用的单调增加。经过长时间的挖掘,我发现每个连接都会创建一些内部Akka对象,但在客户端断开连接后不会得到清理。特别是,这个类:akka.stream.impl.fusing.ActorGraphExplorer。每个连接将创建一个新的此类对象。我使用jmap来计算对象的数量,命令如下所示。我不确定我是不是做错了什么。任何建议都将不胜感激 我有一个超级简单的ech
akka.stream.impl.fusing.ActorGraphExplorer
。每个连接将创建一个新的此类对象。我使用jmap
来计算对象的数量,命令如下所示。我不确定我是不是做错了什么。任何建议都将不胜感激
我有一个超级简单的echo websocket服务器来复制这一观察结果:
package samples
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.ws.{Message, TextMessage}
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Flow, Source}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
object AkkaWsExample {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
private val greeterWebSocketService = {
Flow[Message]
.collect {
case tm: TextMessage =>
println(s"Received $tm")
TextMessage(Source.single("Hello ") ++ tm.textStream)
}
}
def main(args: Array[String]): Unit = {
//#websocket-routing
val route =
path("greeter") {
get {
handleWebSocketMessages(greeterWebSocketService)
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
}
然后,我使用任何方法连接到该服务器并断开连接,然后运行jmap来计算对象数,并注意到每个连接有一个严格意义上的新对象。我也尝试了数千个连接,同样的事情也发生了
我使用此命令计算对象的数量:
jmap-histo:live[pid]| grep ActorGraphTranslator
以下是启动时以及打开和关闭1000个连接后的结果
ip-192-168-30-10:~liuh$jps | grep Akka | awk{print$1}| xargs jmap-histori:live | grep ActorGraphInt | head-n1
701:156 akka.stream.impl.fusing.ActorGraphTranslator
ip-192-168-30-10:~liuh$jps | grep Akka | awk{print$1}| xargs jmap-histori:live | grep ActorGraphInt | head-n1
119:100156056 akka.stream.impl.fusing.ActorGraphTranslator
您可以看到,对象计数严格地随着连接数的增加而增加。我确保客户端已断开连接-我关闭了进程,并使用
netstat
验证连接是否已关闭。您可能没有考虑到Scala基于JVM,JVM使用垃圾收集器,而垃圾收集器又不是确定性的。特别是如果没有产生足够的内存压力(与允许的内存限制相比),GC可能根本不会运行。您可以通过强制使用GC(这在生产中可能很糟糕,但在调试时可以)来轻松验证这一理论。尝试在main
方法的开头添加以下代码:
new Thread() {
override def run(): Unit = {
println("Start GC-thread")
val start = System.currentTimeMillis()
while (true) {
Thread.sleep(1000)
System.gc()
}
}
}.start()
这段代码启动一个独立的线程,要求VM每秒执行GC。我敢打赌,使用这样的代码,您的测试将不会显示超过几个活动的
ActorGraphExplorer
对象。至少这是我在你的例子中看到的。如果您在实际代码中仍然看到许多ActorGraphTranslator
,那么您的示例可能不够充分,您应该发布一个更好的示例。谢谢您的建议。jmap-histo:live实际上强制执行一个完整的GC。我解决了这个问题。这是akka http中的一个bug。我使用的版本只是其中的一个版本,它已经被引入,但尚未修复。github.com/akka/akka-http/issues/2067Hi。您使用的是哪个版本?我有同样的problem@user1861088,最后一个问题可能是向您提出的