在Redis pub / sub和Akka Streams中使用SSE的最简单方法是什么?

时间:2018-02-28 11:50:31

标签: scala redis akka akka-stream akka-http

我想为以下场景流式传输分块服务器发送事件:

订阅Redis密钥,如果密钥更改,则使用Akka Streams流式传输新值。它应该只在有新值时才会流式传输。

据我了解,我需要Source。我猜这是订阅频道:

redis.subscriber.subscribe("My Channel") {
  case message @ PubSubMessage.Message(channel, messageBytes) => println(
    message.readAs[String]()
  )
  case PubSubMessage.Subscribe(channel, subscribedChannelsCount) => println(
    s"Successfully subscribed to $channel"
  )
}

在我的路线中,我需要从中创建Source,但老实说,我不知道该如何开始:

val route: Route =
  path("stream") {
   get {
     complete {
       val source: Source[ServerSentEvent, NotUsed] =
         Source
          .asSubscriber(??) // or fromPublisher???
      .map(_ => {
        ??
      })
      .map(toServerSentEvent)
      .keepAlive(1.second, () => ServerSentEvent.heartbeat)
      .log("stream")
     }
   }

2 个答案:

答案 0 :(得分:1)

一种方法是使用Source.actorRefBroadcastHub.sink

val (sseActor, sseSource) =
  Source.actorRef[String](10, akka.stream.OverflowStrategy.dropTail)
    .map(toServerSentEvent) // converts a String to a ServerSentEvent
    .keepAlive(1.second, () => ServerSentEvent.heartbeat)
    .toMat(BroadcastHub.sink[ServerSentEvent])(Keep.both)
    .run()

将具体化的ActorRef订阅到您的消息频道:发送给此actor的消息将向下游发出。如果没有下游需求,则使用指定的溢出策略将消息缓冲到一定数量(在此示例中,缓冲区大小为10)。请注意,这种方法没有背压。

redis.subscriber.subscribe("My Channel") {
  case message @ PubSubMessage.Message(channel, messageBytes) =>
    val strMsg = message.readAs[String]
    println(strMsg)
    sseActor ! strMsg

  case ...
}

另请注意,上面的示例使用Source.actorRef[String];根据需要调整类型和示例(例如,它可以是Source.actorRef[PubSubMessage.Message])。

您可以在路径中使用具体化的Source

path("stream") {
  get {
    complete(sseSource)
  }
}

答案 1 :(得分:1)

另一种方法可以是创建一个Source作为队列,并将该元素提供给订阅者回调中收到的队列

val queue =
  Source
    .queue[String](10, OverflowStrategy.dropHead) // drops the oldest element from the buffer to make space for the new element.
    .map(toServerSentEvent) // converts a String to a ServerSentEvent
    .keepAlive(1.second, () => ServerSentEvent.heartbeat)
    .to(Sink.ignore)
    .run()

并在订阅者中

    redis.subscriber.subscribe("My Channel") {
  case message @ PubSubMessage.Message(channel, messageBytes) =>
    val strMsg = message.readAs[String]
    println(strMsg)
    queue.offer(strMsg)

  case ...
}