Skip to content

Latest commit

 

History

History
96 lines (67 loc) · 5.38 KB

json-streaming-support.rst

File metadata and controls

96 lines (67 loc) · 5.38 KB

JSON Streaming

JSON Streaming is a term refering to streaming a (possibly infinite) stream of element as independent JSON objects onto one continious HTTP connection. The elements are most often separated using newlines, however do not have to be and concatenating elements side-by-side or emitting "very long" JSON array is also another use case.

In the below examples, we'll be refering to the User and Measurement case classes as our model, which are defined as:

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: models

And as always with spray-json, we provide our (Un)Marshaller instances as implicit values uding the jsonFormat## method to generate them statically:

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: formats

Responding with JSON Streams

In this example we implement an API representing an infinite stream of tweets, very much like Twitter's Streaming API.

Firstly, we'll need to get some additional marshalling infrastructure set up, that is able to marshal to and from an Akka Streams Source[T,_]. One such trait, containing the needed marshallers is SprayJsonSupport, which uses spray-json (a high performance json parser library), and is shipped as part of Akka HTTP in the akka-http-spray-json-experimental module. to and from Source[T,_] by using spray-json provided

Next we import our model's marshallers, generated by spray-json.

The last bit of setup, before we can render a streaming json response

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: spray-json-response-streaming

Customising response rendering mode

The mode in which a response is marshalled and then rendered to the HttpResponse from the provided Source[T,_] is customisable (thanks to conversions originating from Directives via EntityStreamingDirectives).

Since Marshalling is a potentially asynchronous operation in Akka HTTP (because transforming T to JsValue may potentially take a long time (depending on your definition of "long time"), we allow to run marshalling concurrently (up to parallelism concurrent marshallings) by using the renderAsync(parallelism) mode:

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: async-rendering

The renderAsync mode perserves ordering of the Source's elements, which may sometimes be a required property, for example when streaming a strictly ordered dataset. Sometimes the contept of strict-order does not apply to the data being streamed though, which allows us to explit this property and use renderAsyncUnordered(parallelism), which will concurrently marshall up to parallelism elements and emit the first which is marshalled onto the HttpResponse:

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: async-unordered-rendering

This allows us to _potentially_ render elements faster onto the HttpResponse, since it can avoid "head of line blocking", in case one element in front of the stream takes a long time to marshall, yet others after it are very quick to marshall.

Consuming JSON Streaming uploads

Sometimes the client may be sending in a streaming request, for example an embedded device initiated a connection with the server and is feeding it with one line of measurement data.

In this example, we want to consume this data in a streaming fashion from the request entity, and also apply back-pressure to the underlying TCP connection, if the server can not cope with the rate of incoming data (back-pressure will be applied automatically thanks to using Akka HTTP/Streams).

.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala
   :snippet: spray-json-request-streaming

Implementing custom (Un)Marshaller support for JSON streaming

While not provided by Akka HTTP directly, the infrastructure is extensible and by investigating how SprayJsonSupport is implemented it is certainly possible to provide the same infrastructure for other marshaller implementations (such as Play JSON, or Jackson directly for example). Such support traits will want to extend the JsonEntityStreamingSupport trait.

The following types that may need to be implemented by a custom framed-streaming support library are:

  • SourceRenderingMode which can customise how to render the begining / between-elements and ending of such stream (while writing a response, i.e. by calling complete(source)). Implementations for JSON are available in akka.http.scaladsl.server.JsonSourceRenderingMode.
  • FramingWithContentType which is needed to be able to split incoming ByteString chunks into frames of the higher-level data type format that is understood by the provided unmarshallers. In the case of JSON it means chunking up ByteStrings such that each emitted element corresponds to exactly one JSON object, this framing is implemented in JsonEntityStreamingSupport.