Утечка памяти Akka http с веб-сокетом - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть веб-сервер, который принимает входящее соединение через веб-сокет, реализованный с помощью akka http в Scala.Тем не менее, я наблюдаю монотонное увеличение использования памяти моего приложения.После долгого копания я обнаружил, что некоторые внутренние объекты Akka создаются для каждого соединения, но не очищаются после отключения клиента.Специально этот класс: akka.stream.impl.fusing.ActorGraphInterpreter.Один новый такой объект создается для каждого соединения.Я использовал jmap для подсчета количества объектов, команда приведена ниже.Я не уверен, что делаю что-то не так здесь.Любой совет будет высоко ценится.

У меня есть супер простой эхо-сервер веб-сокета, чтобы повторить это наблюдение:

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 для подсчета количества объектов и обнаружения строго 1 нового объекта для каждого соединения.Я попробовал это и с тысячами соединений, происходит то же самое.

Я использовал эту команду для подсчета количества объектов:

jmap -histo: live [pid] |grep ActorGraphInterpreter

Вот результаты при запуске и после открытия и закрытия 1000 подключений

ip-192-168-30-10: ~ liuh $ jps |Греп Акка |awk '{print $ 1}' |xargs jmap -histo: live |grep ActorGraphInt |head -n1

701: 1 56 akka.stream.impl.fusing.ActorGraphInterpreter

ip-192-168-30-10: ~ liuh $ jps |Греп Акка |awk '{print $ 1}' |xargs jmap -histo: live |grep ActorGraphInt |head -n1

119: 1001 56056 akka.stream.impl.fusing.ActorGraphInterpreter

Вы можете видеть, что количество объектов увеличилось строго на числосоединения.Я удостоверился, что моя клиентская сторона отключена - я закрыл процессы и также подтвердил с помощью netstat, что соединения были закрыты.

1 Ответ

0 голосов
/ 28 ноября 2018

Вы, вероятно, не принимаете во внимание тот факт, что Scala основана на JVM, которая использует сборщик мусора, который, в свою очередь, не является детерминированным.В частности, если вы не производите достаточное давление памяти (по сравнению с допустимым пределом памяти), сборщик мусора может вообще не работать.Вы можете легко проверить эту теорию, принудительно установив 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 каждую секунду.Могу поспорить, что с таким кодом ваш тест не покажет более нескольких живых ActorGraphInterpreter объектов.По крайней мере, это то, что я вижу в вашем примере.Если вы все еще видите много ActorGraphInterpreter в своем реальном коде, ваш пример, вероятно, неадекватен MCVE , и вы должны опубликовать более качественный.

...