在服务器端,我正在使用一个HTTP API,该API在页面中返回其结果。像这样,响应包含x个结果,如果大于0,我可以再次调用它以获取下一个x个结果。可以任意选择x,直到API的最大页面大小为止。
现在,我想通过WebSocket有效地流式传输完整的结果集,而又不会使其泛滥(施加了反压)。最初,我构建了整个结果集,然后从中创建了一个Source:
getEventsFuture().foreach { events =>
sender ! Flow.fromSinkAndSource(Sink.ignore, Source(events))
}
这有效,并且WebSocket客户端以其最大速度接收所有事件。最大的缺点是我必须先获取所有页面,然后才能开始将数据返回给客户端。理想情况下,我会使用较小的页面大小,并在连接后立即将结果返回给客户端,以获取过程中的下一页。
因此,我需要一个带有源的流,可以在实现流之后向其中添加数据。我尝试为此使用Source.actorRef
:
val events = Source.actorRef[Event](1000, OverflowStrategy.fail).mapMaterializedValue { outActor =>
sendEvents(outActor)
NotUsed
}
sender ! Flow.fromSinkAndSource(Sink.ignore, events)
从本质上讲,我采用了物化的actorRef并将所有事件发送给它。每次获取页面时,我都将结果转储给actor。现在,我对Source的初始化可能已经告诉您这并不总是有效。有时,当响应足够大且客户端消耗的速度没有其他时间快时,套接字连接就会关闭。我觉得OverflowStrategy.fail
是拒绝事件的正确策略,因为我不希望客户认为如果不是这种情况,他们会得到一切。
我没有预先为缓冲区设置任何合理的值,并且我不想设置Int.max或其他东西,因为我认为Akka在内部确实为缓冲区大小分配了全部内存。
我该如何解决?我希望像第一个示例一样,以适当的背压尽快将所有事件发送给客户端。
一旦获取了第一页,我确实知道总共会有多少个结果,所以我可以预先获取一个小页面并将缓冲区大小设置为完整的结果大小,但这似乎是一种解决方法。
答案 0 :(得分:0)
我发现unfoldAsync
非常适合此用例。
签名
def unfoldAsync[S, E](s: S)(f: S ⇒ Future[Option[(S, E)]]): Source[E, NotUsed]
说明
就像
unfold
一样,但是fold函数返回Future
,这将导致源代码完成或在完成时发出。退出,当有需求且返回状态返回时,将来以某些值完成
完成,当展开功能返回的未来以空值完成