【问题标题】:Play framework Scala: Create infinite source using scala akka streams and keep Server sent events connection open on server播放框架 Scala:使用 scala akka 流创建无限源并保持服务器发送事件连接在服务器上打开
【发布时间】:2019-03-06 06:35:45
【问题描述】:

我们需要为以下用例实现服务器发送事件

  1. 在服务器上进行一些处理后向 UI 发送通知。这个处理是基于一些逻辑的
  2. 从 RabbitMQ 读取消息并对其执行一些操作后向 UI 发送通知。

我们使用 Scala(2.11/2.12) 和 Play framework(2.6.x) 的技术集。 库:akka.stream.scaladsl.Source

我们从以下示例 https://github.com/playframework/play-scala-streaming-example 开始我们的概念证明,然后我们通过创建不同的源进行扩展。 我们尝试使用 Source.apply,soure.single 创建源代码。

但是,一旦源中的所有元素都被推送到 UI,我的事件流就关闭了。但我不希望事件流关闭。另外我不想使用一些计时器(Source.tick)或Source.repeat。

创建我的源时,集合假设有一些 x 元素,然后服务添加了 4 个元素。但是在 x 个元素之后,事件流被关闭,然后再次重新打开。

有什么方法可以让我的事件流是无限的并且只有我的会话被注销或者我们可以明确地关闭它才会关闭。

//KeepAlive 的代码(在 cmets 中询问)

   object NotficationUtil {

      var userNotificationMap = Map[Integer, Queue[String]]()

      def addUserNotification(userId: Integer, message: String) = {
        var queue = userNotificationMap.getOrElse(userId, Queue[String]())
        queue += message
        userNotificationMap.put(userId, queue)

      }

      def pushNotification(userId: Integer): Source[JsValue, _] = {
        var queue = userNotificationMap.getOrElse(userId, Queue[String]())
         Source.single(Json.toJson(queue.dequeueAll { x => true }))
      }
    }
    @Singleton
    class EventSourceController @Inject() (cc: ControllerComponents) extends AbstractController(cc) with FlowFactory{

      def pushNotifications(user_id:Integer) = Action {
      val stream = NotficationUtil.pushNotification(user_id)
       Ok.chunked(stream.keepAlive(50.second, ()=>Json.obj("data"->"heartbeat")) via EventSource.flow).as(ContentTypes.EVENT_STREAM)
     }

}

【问题讨论】:

    标签: scala akka-stream server-sent-events playframework-2.6


    【解决方案1】:

    使用下面的代码创建actorref和发布者

    val (ref, sourcePublisher)= Source.actorRef[T](Int.MaxValue, OverflowStrategy.fail).toMat(Sink.asPublisher(true))(Keep.both).run()
    

    并从该发布者创建您的来源

    val testsource = Source
          .fromPublisher[T](sourcePublisher)
    

    并将您的听众注册为

    Ok.chunked(
            testsource.keepAlive(
              50.seconds,
              () => Json.obj("data"->"heartbeat")) via EventSource.flow)
          .as(ContentTypes.EVENT_STREAM)
          .withHeaders("X-Accel-Buffering" -> "no", "Cache-Control" -> "no-cache")
    

    将您的 json 数据发送到 ref actor,数据将作为事件流通过此源流向前端。 希望对您有所帮助。

    【讨论】:

    • 非常感谢。您是否也评估了 ActorPublisher。我试图确定哪种方法更好。你有什么想法/建议。还有我们如何处理actorRef中的流关闭。你有没有为每个用户处理过这个actor ref流。
    • 不,我没有使用 ActorPublisher。当出现任何错误或您使用 close() 从前端显式关闭它时,流将关闭。随着流的关闭,演员也被终止。我已经使用了将近 6 个多月了,效果很好。
    • SSE 的安全性如何?我无法在我的 SSE 请求中将 JWT 令牌从客户端发送到服务器,并且我面临如何验证这是否是尝试建立流的正确用户的问题。有什么想法吗?同样在我们的部署拓扑中,我们有一个 NGINX 服务器,它拦截对该域的请求并将调用重定向到 apt 模块,在这种情况下 SSE 会工作吗?
    • stackoverflow.com/questions/6623232/…..... 它也可以与 NGINX 完美搭配。
    • @abhinavsingh : 你能分享一下上面代码 sn-p 的单元测试用例吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-08
    • 2016-05-16
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    相关资源
    最近更新 更多