WebSocket对Scala和fs2 / cats提出了挑战

时间:2018-04-24 19:26:47

标签: scala websocket http4s

我正在使用Http4s挂载一个websocket服务,我可以用它在这个后端服务和UI之间进行通信(管道状态更新和批处理作业的完成%)。

我正在使用BlazeBuilder Websocket Example来设置服务。

该服务有效,但我尝试做的是从类实例中发出套接字消息。例如,我想实例化一个worker,传递一个socket连接的引用,并能够将数据发送到该连接。不幸的是,我很难完成这项工作!它在Python和JS中要简单得多。

请参阅下面的代码,该代码主要是我上面链接的示例代码。在我调用Stream.emit(...)的地方,如何将引用传递给#34; toClient"并仍然向它发射?如果我将toClient实例传递给类实例,它似乎无法工作。

case GET -> Root / "ws" =>
      val toClient: Stream[F, WebSocketFrame] = Stream.emit(Text("How can I do this from a class instance?"))
      val fromClient: Sink[F, WebSocketFrame] = _.evalMap { (ws: WebSocketFrame) =>
        ws match {
          case Text(t, _) => F.delay(println(t))
          case f => F.delay(println(s"Unknown type: $f"))
        }
      }
      WebSocketBuilder[F].build(toClient, fromClient)

1 个答案:

答案 0 :(得分:0)

您可以使用MVar与Websocket进行线程安全通信。

以下是使用Cats IO效果的示例:

final class WebSocketServer(implicit timer: Timer[IO]) extends Http4sDsl[IO] {

  implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global)

  def start: IO[ExitCode] = {
    BlazeServerBuilder[IO]
      .bindHttp(8080)
      .withWebSockets(true)
      .withHttpApp(routes.orNotFound)
      .resource
      .use(_ => IO.never)
      .as(ExitCode.Success)
  }

  private[this] val routes: HttpRoutes[IO] = HttpRoutes.of[IO] {
    case GET -> Root / "ws" => {
      for {
        channel <- cats.effect.concurrent.MVar[IO].empty[List[WebSocketFrame]]
        webSocket <- {
          WebSocketBuilder[IO].build(
            send = fs2.Stream
              .eval(channel.take)
              .flatMap(fs2.Stream.emits(_))
              .repeat,
            receive = stream => {
              stream.evalMap {
                case Text(data, _)   => channel.put(List(Text("pong")))
                case unknown         => IO(println(s"Unknown type: $unknown"))
              }
            }
          )
        }
      } yield webSocket
    }
  }
}

如果要将消息发送回客户端,则必须将其放入MVar。

channel.put(List(Text("pong")))

有趣的部分是重复流,该流正在MVar中轮询新消息,以将新消息发送回WebSocket的客户端。

fs2.Stream.eval(channel.take).flatMap(fs2.Stream.emits(_).repeat