2015-06-10 4 views
1

Я использую Spray для запроса конечной точки REST, которая будет возвращать большое количество данных с несколькими элементами, которые необходимо обработать. Данные представляют собой ряд объектов json. Есть ли способ преобразовать ответ в поток этих объектов, который не требует, чтобы я прочитал весь ответ в памяти?Использование спрея с «chunked response»

Чтение документов содержит упоминание о "chunked responses", которое, кажется, соответствует строкам того, что я хочу. Как это использовать в трубопроводе спрей-клиентом?

ответ

1

Я только что реализовал что-то подобное сегодня, благодаря отличной статье, найденной в http://boldradius.com/blog-post/VGy_4CcAACcAxg-S/streaming-play-enumerators-through-spray-using-chunked-responses.

По существу, вы хотите, чтобы получить RequestContext в одном из определений маршрута и получить ссылку на его «ответчика». Это Актер, с помощью которого Spray отправляет ответы обратно клиенту, который отправил исходный запрос.

Чтобы отправить обратно ответный ответ, вы должны сообщить, что ответ начинается, а затем посылать куски по одному, а затем, наконец, сигнализировать о завершении ответа. Вы делаете это через классы ChunkedResponseStart, MessageChunk и ChunkedMessageEnd из пакета spray.http.

По сути то, что я в конечном итоге делает посылает ответ в виде ряда из этих классов, как это:

0) связкой импорта положить в класс со своими маршрутами в и объекта дела:

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, который будет держать данные ответа в массиве JSON под названием «MyJsonData» в данном случае):

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 и счетчиками и поэтому я не включенными большими кусками кода здесь, потому что они очень тесно связаны с этими механизмами, которые не могут быть пригодны для ваших нужд. Точка вызова «withAck» заключается в том, что это приведет к тому, что ответчик запросит сообщение подтверждения, когда сеть сообщит, что это нормально, чтобы принимать больше кусков. В идеале вы создадите свой код, чтобы дождаться возврата сообщения Ack в будущем, прежде чем отправлять больше кусков.

Надеюсь, что вышеизложенное может дать вам стартер по меньшей мере десять, и, как я уже сказал, эти концепции очень хорошо объясняются в статье, в которой я связан!

Thanks, Duncan