Akka HTTP客户端Websocket意外关闭

时间:2019-05-10 13:13:53

标签: scala websocket akka-http

我有一个websocket端点,它每秒发送一条文本消息给客户端。客户端从不向服务器发送任何消息。

使用下面的JS代码,它可以按预期工作,并且始终每秒注销一次消息:

var ws = new WebSocket("ws://url_of_my_endpoint");
ws.onmessage = (message) => console.log(message.data);

我想使用Akka HTTP在Scala中创建一个类似的使用者。 我基于official docs创建了以下代码。

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
import system.dispatcher

val url = "ws://url_of_my_endpoint"

val outgoing: Source[Message, NotUsed] = Source.empty

val webSocketFlow =
  Http().webSocketClientFlow(WebSocketRequest(url))

val printSink: Sink[Message, Future[Done]] =
  Sink.foreach[Message] {
    case message: TextMessage.Strict =>
      println("message received: " + message.text)
    case _  => println("some other message")
  }

val (upgradeResponse, closed) =
  outgoing
    .viaMat(webSocketFlow)(Keep.right)
    .toMat(printSink)(Keep.both)
    .run()

val connected = upgradeResponse.map { upgrade =>
  if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
    Done
  } else {
    throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
  }
}

connected.onComplete(_ => println("Connection established."))
closed.foreach(_ => println("Connection closed."))

问题在于连接会在几秒钟后关闭。有时在1秒后,有时在3-4秒后。 JS客户端工作正常,因此我认为问题不在服务器上。

代码中的问题是什么?应该如何更改它,以便告诉我出了什么问题?

2 个答案:

答案 0 :(得分:1)

自您查看文档以来,文档可能已更改,因为现在有一个部分可以解决您遇到的问题: https://doc.akka.io/docs/akka-http/current/client-side/websocket-support.html#half-closed-websockets

它说明:

Akka HTTP WebSocket API不支持半关闭的连接,这意味着如果其中一个流完成,则整个连接都将关闭(在交换“关闭握手”或经过3秒钟的超时之后)。例如,如果我们仅尝试使用来自服务器的消息,则可能会导致意外的行为

所以这行

val outgoing: Source[Message, NotUsed] = Source.empty

引起了问题。并可以通过以下行将其修复,该行将永远不会完成(除非您完成链接到Promise的{​​{1}})

Source.maybe

我自己遇到了这个问题,发现行为令人困惑。

答案 1 :(得分:0)

一个问题是您没有通过流发送任何消息:

val outgoing: Source[Message, NotUsed] = Source.empty

尝试以下类似的方法,该方法每秒发送一次随机TextMessage

import scala.concurrent.duration._

val outgoing: Source[Message, NotUsed] =
  Source
    .fromIterator(() => Iterator.continually(TextMessage(scala.util.Random.nextInt().toString)))
    .throttle(1, 1 second)