目标是从数据库中流式传输数据,对此数据块执行一些计算(此计算返回某个案例类的Future),并将此数据作为分块响应发送给用户。目前,我能够流式传输数据并发送响应,而无需执行任何计算。但是,我无法执行此计算,然后流式传输结果。
这是我实施的路线。
def streamingDB1 =
path("streaming-db1") {
get {
val src = Source.fromPublisher(db.stream(getRds))
complete(src)
}
}
函数getRds返回映射到case类的表的行(使用slick)。现在考虑函数compute,它将每行作为输入并返回另一个案例类的Future。像
这样的东西def compute(x: Tweet) : Future[TweetNew] = ?
如何在变量 src 上实现此功能,并将此计算的分块响应(作为流)发送给用户。
答案 0 :(得分:7)
您可以使用mapAsync
转换来源:
val src =
Source.fromPublisher(db.stream(getRds))
.mapAsync(parallelism = 3)(compute)
complete(src)
根据需要调整并行度。
请注意,您可能需要配置Slick documentation中提到的一些设置:
注意:某些数据库系统可能需要以某种方式设置会话参数以支持流式传输,而不会在客户端的内存中同时缓存所有数据。例如,PostgreSQL需要
.withStatementParameters(rsType = ResultSetType.ForwardOnly, rsConcurrency = ResultSetConcurrency.ReadOnly, fetchSize = n)
(具有所需的页面大小n
)和.transactionally
才能进行正确的流式传输。
因此,如果您正在使用PostgreSQL,那么您的Source
可能如下所示:
val src =
Source.fromPublisher(
db.stream(
getRds.withStatementParameters(
rsType = ResultSetType.ForwardOnly,
rsConcurrency = ResultSetConcurrency.ReadOnly,
fetchSize = 10
).transactionally
)
).mapAsync(parallelism = 3)(compute)
答案 1 :(得分:1)
你需要有一种方法来编组TweetNew,如果你发送一个长度为0的块,客户端可能会关闭连接。
此代码适用于curl:
case class TweetNew(str: String)
def compute(string: String) : Future[TweetNew] = Future {
TweetNew(string)
}
val route = path("hello") {
get {
val byteString: Source[ByteString, NotUsed] = Source.apply(List("t1", "t2", "t3"))
.mapAsync(2)(compute)
.map(tweet => ByteString(tweet.str + "\n"))
complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, byteString))
}
}