BigPipe using Play, RxJava, and Hystrix
Reactive Frontend - an implementation of BigPipe using Play, RxJava, and Hystrix.Tweet
Reactive Frontend - an implementation of BigPipe using Play, RxJava, and Hystrix.Tweet
There are several ways to get this template.
play-bigpipe-with-rxjava-hystrixin the Lightbend Activator UI.
Already have Lightbend Activator (get it
here)? Launch the UI then
play-bigpipe-with-rxjava-hystrix in the list of
play-bigpipe-with-rxjava-hystrixproject as a zip archive
If you haven't installed Activator, you can get the code
by downloading the template bundle
In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:
Or from a command line:
This will start Lightbend Activator and open this template in your browser.
C:\Users\typesafe\play-bigpipe-with-rxjava-hystrix> activator ui
play-bigpipe-with-rxjava-hystrixproject from the command line
If you have Lightbend Activator, use its command line mode
to create a new project from this template.
activator new PROJECTNAME play-bigpipe-with-rxjava-hystrix on the command line.
The creator of this template maintains it at https://github.com/knutwalker/play-bigpipe-with-rxjava-hystrix#master.
We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.
This is an implementation of the BigPipe pattern using Play, backed by Hystrix.
You can navigate this tutorial to see some of the new features using the arrows above, or you can jump to any particular feature at any time by clicking the title of the current part of the tutorial, which will open a drop down.
BigPipe is a technique described by Facebook for speeding up webpage performance, in particular the perceived latency.
BigPipe was developed to overcome the problems of the existing web page serving systems.
This is not what we want in a reactive web application. People expect web applications to react immediately. The server shouldn't have to wait until all HTML snippets are available and the browser shouldn't have to wait for the server.
With BigPipe, the server's generation time overlaps with the browser's rendering time. The browser receives almost immediately the first bytes and can start rendering the page and download the assets. Whenever the server pushes some new page content to the browser, it can react accordingly to it.
To make excellent use of this technique, the web page should consist of several modules that can operate independently of each other. In the traditional model, pages have to wait till all modules are loaded completly. With BigPipe, the modules can operate asynchronously and push new content to the browser whenever they have it available.
This tutorial represents a fancy new social media site, that heavily features kittens to satisfy the need of the internet.
http://localhost:9000/blocking. You will see a web page with some social media items. You may also notice, that the web page took some time to load.this application and open your browser at
There are some modules on this page, two in the sidebar and one in the news stream. The numbers and values of these modules are randomly generated and each module simulates network latency and failures, thus taking a variable amount of time to generate their number, or they might be failing.
The idea is, that the modules, or rather their individual commands, are provided by some kind of micro-service architecture. Each one could be fed by a different data source, profile view counts could be supplied by Redis, while comments could be delivered by Cassandra. It doesn't really matter where the data comes from, only that the application is not a monolithic block but a distributed network of services.
The modules and their commands are implemented using Hystrix by Netflix. Hystrix is a fault tolerance library, that provides useful mechanics for isolating each module. It allows you to write simple code in a traditional synchronous manner while providing latency and fault tolerance, concurrent execution, and realtime monitoring for you.
Browse to commands.scala. These are all the commands, one for each value of the five parts of all modules. The commands are fairly simple, most of their code actually comes from BogusHelper.scala. This is a trait, that
RuntimeExceptions in order to simulate an unreliable network.
HystrixCommands can have a fallback to allow for graceful degradation. Whether or not to use a fallback and what it should return is driven by the business logic. Every command in Kittens Haven implements a fallback, which is mostly some static default value. The fallback is triggered for all types af failure, such as
run() failure, timeouts, thread pool rejection, or circuit-breaker short-circuiting. The default timeout for any HystrixCommand in one seconds, which is raised for most commands in Kittens Haven, purely for demonstration purpose.
HystrixCommands can be executed in a synchronous, asynchronous, or reactive manner. Kittens Haven uses the reactive execution, which returns an
rx.Observable[T] from RxJava. Browse to PageletFactory.scala at line 30 to see where it is used. The HystrixCommands'
observe() method returns an Observable. The view of this single command is mapped over the observable and then rendered into a Pagelet. You can read more about Pagelets later.
RxJava and its Observables fit nicely into the model of composing single modules reactively. Multiple Observables can be
Hystrix is not a requirement for BigPipe, neither is RxJava (or Play, really, for that matter). You can also use Akka or any other toolkit, that enables fault and latency tolerance and reactive execution of simple commands.
A Pagelet is the single unit of processing for the BigPipe technique. It represents a snippet of HTML, together with an ID of the element into which the HTML snippet should be injected.
As said before, the individual Pagelets are streamed out of order — they appear in the order they're available in, not in the order they're needed in. To put them in the right place, a little Coffescript snippet takes the HTML and puts it into place.
Pagelets itself consist of two script tags. One contains the HTML and has a type of
text/x-html-stream. If a browser encounters a
script tag, they should download it and insert its content in the DOM. Despite that, only tags with the type set to
script tag just calls the injection snippet, so that the content is injected in the DOM. Browse to pagelet.scala.html for the template view.
Several Pagelets can make up a Pageletmodule. Such a module consists of a controller and a layout template. This template is not a regular HTML template but a streamable template. You can read more about these later on; for now, assume they're regular templates.
For example, the NewKittens module consists only of the NewKittens/sayHello Pagelet. Its layout template is at body.scala.pagelet. There is a placeholder, the
div#newest-kitten. This is where the content of the Pagelet will be injected.
Browse to NewKittens.scala for the module controller. Most of the implementation is provided by the CommandStreamController trait, so that modules are really just a definition of the above — a list of Pagelets, each consisting of:
CommandStreamController trait defines some helper methods to create Pagelet factories out of commands or required resources. It also implements the Play action by merging the modules' Observables together and transform them into an Enumerator.
Plays Enumerators ans RxJavas Observables are very similar in what they're achieving to solve. Both provide a push based alternative to enumerate several values asynchronously, instead of the synchronous, pull-based iteration with Iterators. Here, Observables were used, because that's what was already provided by Hystrix and they offer all composing functionality that is needed. Alternatively, one could use Enumerators from the beginning and have them composed (e.g.
Enumerator.interleave instead of
Enumerator.andThen instead of
A PageletFactory is just a function that produces a Pagelet with an additional method to do so in a traditional, blocking manner. This blocking behaviour is only used for demonstration purpose, it is not needed for BigPipe and this abstraction can be removed in production code.
Controllers define one additional thing, the
layout. This it the streaming template that is used, when the module is viewed as a standalone page. By providing this, you can easily text and develop the modules independent from each other, without having to keep the big picture around all the time.
In order to stream the Pagelets to the browser, the templates have to be made asynchronous as well. The default HTML templates are rendered into an
Html, that is finally sent to the browser. What is needed are templates, that would instead render into an
Observable[Html], so that we can still use the view separation with BigPipe.
Browse to build.sbt. You'll see a configuration option called
TwirlKeys.templateFormats += ("pagelet" -> "pagelet.PageletObservableFormat")
This defines a custom template format and registers the file ending
.pagelet for it. You have already encountered such a template, it doesn't look different from a regular template.
Browse to PageletObservableFormat. This is the Format definition for the streamable templates. It mirrors the HtmlFormat but works with parts of
PageletObservables instead of
Browse to PageletObservable. This is the Appendable definition for the streamable templates. Instead of appending text to some StringBuffer, it concatenates Observables into one single Observable. The usage of
concat) is important here, since templates are order sensitive — you don't want to have your HTML layout scattered all over the place. The
++ operator ensures, that each part of the template appears in the order it is defined. Note, that this is different from the Pagelets itself, which are encouraged to appear out of order.
Browse to streamed.scala.pagelet for an example of a module page. Here,
body are the modules' Pagelets. Everything that appears before
@body will be sent to the browser immediately. The body itself will be streamed as the Pagelets arrive and finally, the transfer is finished with closing the
html tags. Therefore,
@body should always appear at the very last position, right before the closing
</body>, so that as much as possible is sent to the browser immediately. In other words —
@body is the part that would normally block, but doesn't, thanks to BigPipe.
Now, all pieces are laid out to implement BigPipe.
Observable[T] => Observable[Html]
merged together and turned into a
PageletObservableas its body, providing the outline of the module
PageletObservableis transformed into an
Enumerator[Html]is streamed to the browser, using Plays
The main page is implemented by the Aggregator.scala, which is really simple. It just concatenates all Pageletes from all modules and defines the global template.
Open the browser at http://localhost:9000/. and compare it with the traditional blocking version of http://localhost:9000/blocking. You will notice, that the layout of the page appears almost immediately and you can see how some numbers suddenly pop into place, that would have otherwise staled the HTML generation on the server side. Additionally, you can see the result in the network section of your browsers developer tools, e.g.:
With the traditional model, the browser had to wait several seconds before it could do anything. With BigPipe, the first bytes were received after only 6 milliseconds, and the browser could download additional resources while the server was still busy, generating the Pagelets.
Also, if you refresh the page several times, you can see the fallback strategy being applied. Every time, there is a 0 count of something, the underlying service failed. But this failure did not affect the overall page in any way, only its own segment. This outage might have been due to a tripped circuit, a timeout, an exception or other things, but Hystrix allows the page to survive this fairly easily and the error does not propagate the complete system.
Another aspect of Hystrix is the Dashboard. The default distribution of the Dashboard and its companion – the Metrics Event Stream – is via Servlets/JEE. This is not suitable for Play, so this tutorial has them both integrated into this Play app.
HystrixMetricsStream.scala is a Play implementation of the
hystrix-metrics-event-stream module. It is fully compliant with the default implementation, so you can use this stream to push Hystrix metrics to your own dashboard or Turbine.
http://localhost:9000/dashboard is an integrated/hosted version of the dashboard, providing useful insights into your commands.
Here are some suggestions or improvements that could be made.
HystrixObservableCommand, so that one can implement the commands completely asynchronous and non-blocking as well.