Scala 使用Akka Http转换流数据并发送分块响应
其目的是从数据库流式传输数据,对该数据块执行一些计算(该计算返回某个案例类的未来),并将该数据作为分块响应发送给用户。目前,我能够流式传输数据并发送响应,而无需执行任何计算。但是,我无法执行此计算,然后流式传输结果 这就是我所实施的路线Scala 使用Akka Http转换流数据并发送分块响应,scala,akka,slick,akka-stream,akka-http,Scala,Akka,Slick,Akka Stream,Akka Http,其目的是从数据库流式传输数据,对该数据块执行一些计算(该计算返回某个案例类的未来),并将该数据作为分块响应发送给用户。目前,我能够流式传输数据并发送响应,而无需执行任何计算。但是,我无法执行此计算,然后流式传输结果 这就是我所实施的路线 def streamingDB1 = path("streaming-db1") { get { val src = Source.fromPublisher(db.stream(getRds)) complete(src) } } 函
def streamingDB1 =
path("streaming-db1") {
get {
val src = Source.fromPublisher(db.stream(getRds))
complete(src)
}
}
函数getRds返回映射到case类的表的行(使用slick)。现在考虑函数计算,它将每一行作为输入并返回另一个case类的未来。差不多
def compute(x: Tweet) : Future[TweetNew] = ?
如何在变量src上实现此函数,并将此计算的分块响应(作为流)发送给用户 您可以使用以下方法转换源: 根据需要调整平行度级别
请注意,您可能需要配置以下中提到的一些设置: 注意:某些数据库系统可能需要以某种方式设置会话参数,以支持流式传输,而无需在客户端的内存中一次性缓存所有数据。例如,PostgreSQL既需要
.with语句参数(rsType=ResultSetType.ForwardOnly,rsConcurrency=ResultSetConcurrency.ReadOnly,fetchSize=n)
(具有所需的页面大小n
)又需要。事务性,以实现正确的流式处理
因此,如果您使用的是PostgreSQL,那么您的源代码可能如下所示:
val src =
Source.fromPublisher(
db.stream(
getRds.withStatementParameters(
rsType = ResultSetType.ForwardOnly,
rsConcurrency = ResultSetConcurrency.ReadOnly,
fetchSize = 10
).transactionally
)
).mapAsync(parallelism = 3)(compute)
您需要有一种新的方法,并且如果您发送长度为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))
}
}
这是行不通的。我运行curl命令以命中端点。但是连接被关闭。@user3294786听起来好像之前没有正确地等待数据closing@StanislavPalatnik很可能是在这里发生了什么事;我建议添加一个.log()阶段,以查看元素是否按预期发送(而不仅仅是完成),确保将akka.loglevel=DEBUG设置为well@user3294786:您将此标记为答案。那么,它现在对你起作用了吗?@StanislavPalatnik是的,它起作用了。我在另一个数据集上尝试了这个方法,效果非常好。需要调试之前失败的原因。“如果发送长度为0的区块,客户端可能会关闭连接。”--我相信Akka足够聪明,可以为您从流中筛选出这些区块,因为它们不能表示为HTTP区块。
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))
}
}