我想为以下场景流式传输分块服务器发送事件:
订阅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")
}
}
答案 0 :(得分:1)
一种方法是使用Source.actorRef
和BroadcastHub.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 ...
}