使用喷雾" chunked response"

时间:2015-06-10 01:38:02

标签: spray chunked spray-client

我正在使用Spray查询REST端点,该端点将返回大量数据,其中包含应处理的多个项目。数据是一系列json对象。有没有办法将响应转换为这些对象的流,不需要我将整个响应读入内存?

阅读文档时提到"chunked responses",这似乎与我想要的一致。如何在喷涂客户端管道中使用它?

1 个答案:

答案 0 :(得分:2)

由于http://boldradius.com/blog-post/VGy_4CcAACcAxg-S/streaming-play-enumerators-through-spray-using-chunked-responses上发现的优秀文章,我今天刚刚实施了类似的内容。

基本上,您要做的是在您的一个Route定义中获取RequestContext,并获取对其“responder”Actor的引用。这是一个Actor,Spray将响应发送回发送原始请求的客户端。

要发回分块响应,您必须发出响应正在开始的信号,然后逐个发送块,然后最终发出响应已完成的信号。您可以通过spray.http包中的ChunkedResponseStart,MessageChunk和ChunkedMessageEnd类来完成此操作。

基本上我最终要做的是将响应发送为一系列这样的类:

0)使用Routes输入的一大堆导入和一个case对象:

import akka.actor.{Actor, ActorRef}
import spray.http._
import akka.actor.ActorRef
import akka.util.Timeout
import akka.pattern.ask
import spray.http.HttpData
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
import akka.actor.{ActorContext, ActorRefFactory, Props}
import spray.http.{HttpData, ContentType}
import spray.routing.RequestContext
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import spray.json.RootJsonFormat
import spray.http.MediaTypes._

object Messages {
    case object Ack
}

1)从你的路线中获取requestContext:

path ("asdf") {
  get { requestContext => {
    ... further code here for sending chunked response ...
  }
}

2)启动响应(作为JSON信封,在这种情况下将把响应数据保存在名为“myJsonData”的JSON数组中):

responder.forward(ChunkedResponseStart(HttpResponse(entity = HttpEntity(`application/json`, """{"myJsonData": ["""))).withAck(Ack))

3)迭代你的结果数组,将它们的JSONified版本作为JSON数组中的元素发送到响应中,逗号分隔,直到发送最终元素 - 然后不需要尾随逗号:

requestContext.responder.forward(MessageChunk(HttpData(myArray.toJson).withAck(Ack))

if (!lastElement) { // however you work this out in your code!
requestContext.responder.forward(MessageChunk(HttpData(",").withAck(Ack))
}

4)当没有什么可以发送时,关闭JSON信封:

responder.forward(MessageChunk("]}").withAck(Ack))

并发出响应的结束信号:

responder.forward(ChunkedMessageEnd().withAck(Ack))

在我的解决方案中,我一直在使用Play Iteratees和Enumerators,所以我没有在这里包含大块代码,因为它们与这些可能不适合您需求的机制非常相关。 “withAck”调用的要点是,当网络发出可以接受更多块的信号时,这将导致响应者询问确认消息。理想情况下,您可以制作代码,以便在将来发送更多块之前等待Ack消息的返回。

我希望上面的内容可能会给你一个至少10个起点,正如我所说,这些概念在我链接的文章中得到了很好的解释!

谢谢, 邓肯