当使用akka-http实现Web socker服务器时,堆内存正在增长

时间:2017-03-15 23:29:05

标签: scala akka akka-stream akka-http

我正在使用akka-http来实现Web套接字服务器。

以下是它的请求处理程序代码:

  def decodeService: Flow[Message, Message, _] = { 
    Flow[Message].map { 
      case BinaryMessage.Strict(encodeMsg) => 
        try {
          WebServer.getWorkerActor ! QueueWork(protoMsg(this, encodeMsg))
        } catch {
          case e: Exception => {
            println("[ERROR] failed to send BinaryMessage.Strict: " + e)
            TextMessage("[ERROR] failed receiving BinaryMessage.Strict")
          }
        }
        TextMessage("[INFO] BinaryMessage.Strict")

      case BinaryMessage.Streamed(streamedMsg) => {
        implicit val system = ActorSystem("DecoderSystem")
        implicit val materializer = ActorMaterializer()
        val streamedMsgFuture: Future[Seq[ByteString]] = streamedMsg.runWith(Sink.seq)
        streamedMsgFuture.onComplete { completedStream =>
          var completeBytestring = new ByteStringBuilder()
          //I'm sure there's a better way to do this.. but hey, it's O(n)
          completedStream.foreach { x => 
            x.foreach { y => 
              completeBytestring ++= y  
            }
          }
          try {
              WebServer.getWorkerActor ! QueueWork(protoMsg(this, completeBytestring.result()))
          } catch {
            case e: Exception => {
              println("[ERROR] failed to send BinaryMessage.Streamed: " + e)
              TextMessage("[ERROR] failed receiving BinaryMessage.Streamed")
            }
          } finally {
            completeBytestring.clear()
          }
        }
        TextMessage("[INFO] BinaryMessage.Streamed")
      }

      case TextMessage.Strict(txt) => TextMessage("Succesfully receive text message")
      case _ => TextMessage("Message type unsupported")
    }
  }

我的网络服务器每1分钟频繁获取一次数据流。我看到记忆力在增长。如果我不处理流式消息,它能够保持。客户端和服务器之间的连接也是持久的。

我是否错误地使用了Flows / Sink / Source?如何刷新流?

由于

1 个答案:

答案 0 :(得分:3)

嗯,最明显的问题是,您为收到的每条流信息创建了一个全新的ActorSystemActorSystem就像是演员的线程池;你想尽可能少地创建它们,理想情况下只有一个用于整个应用程序。不仅你为每条消息创建它们,你也不会关闭它们 - 所有在ActorSystem中配置的调度程序,它所拥有的所有资源将永远保持不变。当然,如果您收到大量流式消息,您的内存使用量将会增长。

由于您使用的是akka-http,因此您必须在其中调用ActorSystem Http().bind*。您需要在decodeService方法中访问它。此外,计算组合字节流的方式对我来说似乎过于复杂。考虑这样写:

def decodeService: Flow[Message, Message, _] = Flow[Message].mapAsync(4) {
  case m: BinaryMessage.Strict =>
    Future.successful(m)
  case BinaryMessage.Streamed(streamMsg) =>
    streamMsg.runReduce(_ ++ _).map(BinaryMessage.Strict)
  case m =>
    Future.successful(m)
}.map {
  case BinaryMessage.Strict(encodeMsg) =>
    try {
      WebServer.getWorkerActor ! QueueWork(protoMsg(this, encodeMsg))
      TextMessage("[INFO] BinaryMessage.Strict")
    } catch {
      case NonFatal(e) =>
        println("[ERROR] failed to send BinaryMessage.Strict: " + e)
        TextMessage("[ERROR] failed receiving BinaryMessage.Strict")
    }
  case TextMessage.Strict(txt) => TextMessage("Succesfully receive text message")
  case _ => TextMessage("Message type unsupported")
}

在这里,首先我们将所有二进制消息转换为BinaryMessage.Strict,然后我们将像处理原始代码一样处理它们。请注意,您必须在try块内写入确认消息,否则即使您有异常,也会返回成功的消息。此外,如果您决定不处理短信,代码可能会变得更简单:

def decodeService: Flow[Message, Message, _] = Flow[Message]
  .filterNot(_.isText)
  .mapAsync(4) {
    case BinaryMessage.Strict(binary) =>
      Future.successful(binary)
    case BinaryMessage.Stream(binaryStream) =>
      binaryStream.runReduce(_ ++ _)
  .map { encodeMsg =>
    try {
      WebServer.getWorkerActor ! QueueWork(protoMsg(this, encodeMsg))
      TextMessage("[INFO] BinaryMessage.Strict")
    } catch {
      case NonFatal(e) =>
        println("[ERROR] failed to send BinaryMessage.Strict: " + e)
        TextMessage("[ERROR] failed receiving BinaryMessage.Strict")
    }
  }