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))
  }
}