简体   繁体   中英

Akka HTTP client side websocket closes unexpectedly

I have a websocket endpoint which sends a text message to the client every second. The client never sends any message to the server.

Using the below JS code, it works as expected, it keeps logging out the message every second:

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

I want to create a similar consumer in Scala, using Akka HTTP. I have created the below code, based on the 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."))

The problem is that the connection closes after a few seconds. Sometimes after 1 sec, sometimes after 3-4 seconds. The JS client works just fine, so I assume that the problem is not on the server.

What is the problem in the code? How should it be changed, so it tells me what went wrong?

From the documentation :

Note

Inactive WebSocket connections will be dropped according to the idle-timeout settings . In case you need to keep inactive connections alive, you can either tweak your idle-timeout or inject 'keep-alive' messages regularly.

The problem is that you're not sending any messages through the stream, so the inactive connection is closed:

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

Try something like the following, which sends a random TextMessage every second:

import scala.concurrent.duration._

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

Alternatively, adjust the aforementioned idle timeout settings or configure the automatic keep-alive support :

This is supported in a transparent way via configuration by setting the: akka.http.client.websocket.periodic-keep-alive-max-idle = 1 second to a specified max idle timeout. The keep alive triggers when no other messages are in-flight during the such configured period. Akka HTTP will then automatically send a Ping frame for each of such idle intervals.

By default, the automatic keep-alive feature is disabled.

It's possible the documentation has changed since you looked at it as there's now a section to deal with the problem you're having: https://doc.akka.io/docs/akka-http/current/client-side/websocket-support.html#half-closed-websockets

It explains:

The Akka HTTP WebSocket API does not support half-closed connections which means that if either stream completes the entire connection is closed (after a “Closing Handshake” has been exchanged or a timeout of 3 seconds has passed). This may lead to unexpected behavior, for example if we are trying to only consume messages coming from the server

So the line

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

is causing the problem. And could be fixed with the below line which never completes (unless you complete the Promise linked to Source.maybe )

val outgoing = Source.empty.concatMat(Source.maybe[Message])(Keep.right)

I ran into this problem myself and find the behaviour pretty confusing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM