Open Source RDBMS - Seamless, Scalable, Stable and Free

한국어 | Login |Register

Current Events
Join our developers event to win one of the valuable prizes!
posted 3 years ago
viewed 17536 times
Share this article

Faster Web Page Loading with Facebook BigPipe

We all know that visitors perceive a Web site to be fast if it displays something out very quickly. To achieve this, many webmasters do the following trick: they first load the caption of the site where the logo and the main menu reside, then load the rest content using AJAX (Asynchronous JavaScript and XML). The first part comes out almost instantly which makes an impression that "the page is loaded, now wait a few more moments while your browser renders it".

This is a nice trick which works well, though it is not always SEF (Search Engine Friendly). But is there a better way? Perhaps, there is, as Facebook introduced its new technology called BigPipe. In this article I will explain about Facebook's BigPipe which allows its 600 million user base enjoy the fast experience as if there is only one user using the service. Maybe you will be able to reproduce this technology for your own Web service.

Before We Start

From the client's perspective, the Web environment is getting harder to understand as Content-Rich Web services become popular by adopting AJAX-based asynchronous processing. There is no doubt that the Web environment will become chaotic if HTML5 becomes popular without any preparation, which seems likely since HTML5, with various client APIs, is already finding its niche. I believe a whole new age of Web environment is on its way.

The more complex the Web becomes, the more important it is to have servers and clients that offer better performance. In response to such demands, there are already many Web optimization methods available [1], and in fact, many sites are enjoying great benefits by applying these methods.

Facebook, which is competing fiercely with Google over the initiative in the future Web service, disclosed a new Web service technology called BigPipe which provides a much better user-perceived speed on its SNS. Let's look into it deeper and see how the user-perceived speed could be enhanced, which is what this technology is all about.

What is the motivation for this technology?

For an SNS such as Facebook, the major contents must be processed based on personalized data. This means that there is always an inevitable limit to any improvement in performance that is achieved only by improving the speed of the server, such as the cache application and system structure. Facebook found the solution to this limit on performance in a new transfer format, and parallel processing between the server and the client. The solution is particularly focused on reducing the delay in showing the page that is requested.

Problems related to the existing dynamic Web service system

When the Web server is overloaded while generating a page, in most cases the browser becomes idle and does nothing. When the server finishes generating the page and sends it to the browser at once, it cause a bottleneck effect, both in traffic and in performance. Therefore, the server that has already completed transfer cannot be helpful any more. By overlapping the time taken for the server to generate pages and for the browser to render, you can not only reduce the end-to-end latency but also the initial response time for the Web page, consequently reducing overall loading time. The problem in which a single slow query holds back the entire loading process can be addressed as well.

Why BigPipe?

As mentioned earlier, BigPipe is a technology that combines existing Web technologies to allow the parallel processing to generate Web pages and render browsers simultaneously (just as AJAX did). It is named because its mechanism is similar to Pipelining which is common for microprocessors. For this reason, Facebook calls BigPipe technology a Web page pipeline for better performance.

Something you need to know first

In order to understand the BigPipe technology, it is necessary to understand Chunked Encoding, which is supported in version 1.1 of HTTP protocol, and the Script Flush method which is based on Chunked Encoding.

Chunked Encoding

Chunked Encoding is an encoding method of transfer supported in HTTP 1.1 and higher, which allows the server to ignore the header value and divide the response result from HTTP into a series of chunks, and then transfer each chunk of the result. The size of each chunk is transferred along with the content. With this feature, the server can send a response to the Web browser in the middle of a process (Flush transfer) so that the user can see at least partial content.

The figure below shows an example of HTTP responses with a Chunked Encoding format which named Transfer-Encoding from header values as chunked. The sizes of individual chunked text which are transferred sequentially are expressed in hexadecimal instead of the Content-Length header value. You can also see it is ended with 0.

HTTP/1.1 200 OK

Content-Type: text/plain

Transfer-Encoding: chunked

25

This is the data in the first chunk

1C

and this is the second one

3

con

8

sequence

0

With the Java Servlet development environment, if a flushBuffer method of HttpServletResponse class is called in the manner shown below, it will be transferred in the fashion as shown above.

PrintWriter writer = response.getWriter();

String chunkedHtml = "<div>Hello, World</div>";

writer.println(chunkedHtml);

response.flushBuffer();

Flush and gzip compression

If you test the code above only using Tomcat without a Web server on any local PC, you will see that the Flush transfer works properly in the Chunked Encoding format. However, testing the same source in an environment where Apache and Tomcat are linked does not produce the desired result in most cases. This is because the gzip module set in Apache by default performs the buffering process internally. In Apache version 2.x, editing with the buffer size option at the module_deflate module that is responsible for processing gzip did not properly work because of a bug. But fortunately, this bug has been resolved in Apache version 2.2.8. Therefore, each chunk can be Flush-transferred, regardless of how big it is, when it's compressed with gzip. Consequently, for the Chunked Encoding method transfer used in Apache before version 2.2.8, the easiest solution would be to set the module_deflate not to gzip on the specified URL.

Web browser control via Script Flush

If you Flush-transfer HTML tags sequentially by using the Chunked Encoding method, the screen will be rendered sequentially. Alternatively, if you Flush-transfer a JavaScript function between <script> tags as shown in the example below, the Web browser can be controlled by the server. In other words, the server can both generate the response result and control the Web browser simultaneously. In the example below, a script code is transferred in order to show the task progress on the Web browser immediately when the server processes a certain task.

21

<script>doProgress(10);</script>

21

<script>doProgress(50);</script>

22

<script>doProgress(100);</script>

The example above can be described in more detail as follows:

Description Example source
After receiving the HTTP request from the Web server, it would immediately Flush-transfer the HTML layout that is unrelated to the contents and the JavaScript codes. (However, the </body></html> tag, which means "unclosed" should be ignored at this time) <html>

<head>

<script>

function doProgress(value) {

var progressbar = document.getElementById(“progessbar”);

progressbar.innerHTML = value;

}

</script>

</head>

<body>

<div id=”progressbar”></div>
Transfer the JavaScript codes in the middle of the server process through the Flush method when needed (Repeat). <script>

doProgress(10);

</script>
The HTTP response ends by Flushing a tag that completes the HTML after all Flushing is done. </body>

</html>

Now let's get to the sweet part of this article.

BigPipe

BigPipe is a technology that allows parallel processing between the page generation of the Web server and the rendering process of a browser by using the Script Flush method described above. It divides a page into small pieces to enable this type of parallel processing, so that it independently processes the entire Life Cycle from creating each HTML to displaying on the screen through Flush transfer to the browser, and it is named "Pagelet” (which means a page that is smaller than a normal one such as Applet and Servlet).

Pagelet

Pagelet is a small piece of the page which is an independent section of the page for browsers, and is a result of an individual Thread in parallel processing to generate the page for Web servers. See these actual Facebook pages as an example of how how Web pages are broken into small Pagelets.

facebook-bigpipe-pagelets.jpg

In the figure above, the bolded boxes represent Pagelet sections, and each of them has an independent Life Cycle. While the menu on the left side of the page is displayed to the user, the "News Feed" page may still be being generated from the server at the moment.

Life Cycle

In BigPipe, the Life Cycle of a user's requests are as follows:

1. The browser sends an HTTP request to the server.

2. After receiving the HTTP request and identifying the effectiveness, the server immediately sends an unclosed HTML document (</body></html> tags are taken out) to a browser by using Flush transfer, which is the Chunked Encoding method. The <head> tag includes the JavaScript library that can workwith the JavaScript code by using Script Flush. The <body> tag consists of a type of template, mostly by using <div> tags to define the logical structure of the divided pages and the possessing structure of Pagelet. For example:

<html>

<head>…</head>

<body>

<div id=”left_column”>

<div id=”pagelet_navigation”></div>

</div>

<div id=”middle_column”>

<div id=”pagelet_composer”></div>

<div id=”pagelet_stream”></div>

</div>

<div id=”right_column”>

<div id=”pagelet_pymk”></div>

<div id=”pagelet_ads”></div>

<div id=”pagelet_connect”></div>

</div>

3. After flushing the first response to the client, the Web server continues to generate Pagelets one by one. As soon as a Pagelet is generated, its response is transferred to the client immediately as a JSON-encoded object that includes all the CSS, the JavaScript resources needed for the Pagelet, and its HTML content, as well as some meta data.

<script>

big_pipe.onPageletArrive( {

id: “pagelet_composer",

content: <HTML>,

css: [..], js: [..],

})

</script>

As mentioned earlier, with the Script Flush method, it is able to control transferring to the browsers and rendering the screen at the same time. One can easily infer that the ‘big_pipe.onPageletArrive’ JavaScript function will be responsible for the implementation.

4. At the client side, upon receiving a Pagelet response via “onPageletArrive” call, BigPipe’s JavaScript library first downloads its CSS resources; after the CSS resources are downloaded, BigPipe displays the Pagelet by setting its corresponding placeholder div’s innerHTML to the Pagelet’s HTML markup. The CSS of Multiple Pagelets can be downloaded simultaneously. In addition, Pagelets can be displayed without any obvious order, depending on whose CSS download finishes earlier.

5. BigPipe won’t start downloading JavaScript for any Pagelet until all Pagelets in the page have been displayed. Still, Pagelets are asynchronously downloaded and executed. JavaScript resource is given a lower priority than CSS and page content because it is faster to display Pagelets on the page through CCS and HTML and then executing JavaScript and download. This is the case not only for BigPipe, but also for normal Web page serving.

6. Once all Pagelet generation and response transfer is done at the server, the tags below are sent last to complete the page structure.

</body>

</html>

Parallel processing between the Web server and the browser

Because parallel processing is carried out between the BigPipe server and the browser, each Pagelet is executed simultaneously in different stages. For example, the browser can be downloading CSS resources for three Pagelets while rendering the content for another Pagelet, and at the same time the server is still generating the response for yet another Pagelet. From the user’s perspective, the page is rendered sequentially. The initial page content becomes visible much earlier, which dramatically reduces the latency of the page.

The figure below demonstrates briefly the manner in which the Web server and the browser interact to generate Pagelets and display them on the screen through parallel processing.

pagelet-generation-flow.png

Performance comparison

Facebook has been kind enough to provide URLs that enable us to see how much better BigPipe is compared to the previous approach in terms of performance.

  1. BigPipe model URL : http://www.facebook.com/home.php?big_pipe=pipeline
  2. Traditional model URL : http://www.facebook.com/home.php?big_pipe=singleflush

The comparison between the two pages shows that BigPipe starts rendering sufficiently faster for anyone to perceive the difference.

The graph below shows performance data comparing the user-perceived latency in viewing the most important content in a page, namely the news feed, in the traditional model and in BigPipe.  The graph shows that in most browsers, BigPipe reduces user-perceived latency by half.

facebook-bigpipe-performance.png

Comparison with alternatives

There is another approach to reducing latency after the request from users, which is lazy loading based on AJAX. Instead of loading the whole page at once, this approach loads the basic sections first and then receives the rest through AJAX upon page rendering, This has some resemblance to BigPipe, in that contents are loaded in pieces. Yet unlike BigPipe, which needs only one HTTP connection for the whole process, this AJAX scatter-loading approach requires an HTTP connection for each scattered loading.

The table below presents a summary comparison of the three ways of page loading, including the traditional way.

page-loading-comparison.png

Conclusion

I would like to close this by introducing some issues that are noteworthy when applying the BigPipe technology in actual service.

Parallel processing at the server

In BigPipe, parallel processing between the server and the browser requires Pagelet generation at the server in parallel. But if parallel processing is difficult due to the structure, or if there is a bottleneck when pulling data along with DB queries, sequential processing instead of parallel processing can help reduce initial rendering time, because as long as you make sure that the sequence is right, you can display Pagelet results on the screen right away.

Independency of Pagelets

In BigPipe, as the name suggests, Pagelets are treated as independent single pages. In each Pagelet, not only HTML but also CCS and JavaScript, are loaded separately. But just as CCS and JavaScript are integrated and compressed into one or more files to reduce the Web traffic, BigPipe can be operated in a way in which CCS and JavaScript loaded in HTML are flushed initially, and only HTML is processed in Pagelets.

Limitations of BigPipe

BigPipe is certainly a very useful technology that can enable a remarkable reduction in Web page latency. Yet it has also restrictions and limitations.

  1. It cannot accommodate Single Flush-based technologies such as UI layout solutions, including Sitemesh and Tiles.
  2. It can cause higher debugging costs during Web development, since transferring HTML in the form of Script Flush requires encoding into JSON. (“View Source” on a normal page will show JSON encodings.)
  3. The performance improvement offered by BigPipe may be minimal for pages with the ability to process sufficient cache.

References

  1. Steve Souders "Optimizing Websites: Essential Instruction for UI Developers"
  2. http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919
  3. http://www.w3.org/Protocols/
  4. http://apache.mirrors.tds.net/httpd/CHANGES_2.2
  5. http://en.wikipedia.org/wiki/Pipeline_(computing)



comments powered by Disqus