What?

BigPipe 是 Facebook 工程师蒋长浩于 2010 年的 Velocity 大会上分享的一个议题,它的提出主要是为了解决重数据页面的加载速度问题,这在业界引起了巨大的反响。

BigPipe 是一个重新设计的基础动态网页体系,它将传统的网页分解成了一个个 pagelet。先向客户端输出页面的基础框架(钢筋房),然后再按需加载页面所有的数据(装修),最终完成整个页面的渲染。虽然说这是一个重构的基础动态网页体系,但是我们不需要改变现有的通信机制(B/S),因为它完全是基于 PHP 和 Javascript 来实现的(如今 Node 也可以实现了)。

Why?

在知道为什么需要 BigPipe 之前,我们先来了解传统的动态网页体系,这是一个十几年的老家伙了,它的服务质量已经无法满足今天用户的需求了。在传统的体系下,用户请求的生命周期如下:

  1. 浏览器向服务器发送一个请求
  2. 服务器解析请求,从存储中读取数据,然后制定一个 HTML 文本,并通过一个 HTTP 响应将其发送到浏览器
  3. HTTP 响应通过网络传输到服务器
  4. 浏览器解析来自服务器的响应,使用 HTML 文本构建了一个 DOM,并下载引用的 CSS 文件和 Javascript 文件
  5. CSS 文件下载完之后,浏览器会解析它们,并将它们应用到 DOM
  6. Javascript 文件下载完之后,浏览器会解析并执行它们

传统的模式面临着一个很大的问题——无法并行请求。为了解决这个问题,Ajax 应运而生。虽然说 Ajax 解决了并行请求的问题,但是 Ajax 在庞大的数据面前有很致命的缺点:服务器负载高、浏览器资源浪费以及用户体验不友好。而 BigPipe 很好地规避了 Ajax 的缺点。

Performance Comparation

“怎么让客户机跑得更快?”是一个永恒的话题。可以看到 BigPipe 与传统模式相比性能大大提升了,最显著的是用户感知延迟的时间缩短了一半,使用 BigPipe 来构建复杂的数据页面也自然而然成为了新的趋势。

How?

BigPipe 到底是怎样运作的呢?

为了充分利用 browser 和 server 之间的并行性,BigPipe 首先将网页划分成多个可调用的模块—— pagelets。正如微处理器的流水生产线划分的指令一样,BigPipe 也将页面的生成过程划分成几个阶段:

  1. 解析请求:服务器解析和 http 请求的完整性检查
  2. 获取数据:服务器从存储层获取数据
  3. 生成标记:服务器生成响应的 html 标记
  4. 网络传输:响应从服务器传输到浏览器
  5. 加载 CSS:浏览器加载页面引用的 CSS 文件
  6. 构建 DOM 和应用 CSS:浏览器先构建好 DOM tree,然后再应用样式
  7. 加载 Javascript:浏览器加载页面引用的 Javascript 文件
  8. 执行 Javascript:浏览器逐行解析执行 Javascript 代码

可以看到,前三步工作交给服务器,后面四步交给浏览器。每个 pagelet 都必须要经历这 8 个阶段,看起来工作似乎很繁重,但 BigPipe 可以让多个 pagelet 在不同的阶段同时执行,例如浏览器可以在加载一个 pagelet 所需要的 CSS 同时,服务器正在生成新的 pagelet,并且另一个 pagelet 的内容也正在逐步显示。这里要注意的一点是:CSS 的优先级是高于 Javascript 的,BigPipe 不会在所有的 CSS 加载完毕之前加载任何的 Javascript 文件,这样保证了页面框架的完整性。

下面用 BigPipe 来构建一个页面:

<!DOCTYPE html>
<html>
<head>
  <title>Hello BigPipe</title>
  <script>
    // 一个将html标记插入到特定的pagelet的函数
    function inject(id, content) {
      var elem = document.getElementById(id);
      if (!elem) {
        return;
      }
      elem.innerHTML = content;
    }
  </script>
</head>
<body>
  <!-- 页面布局框架,每个div都是一个pagelet -->
  <div class="main">
    <div id="head1" class="module"></div>
    <div id="head2" class="module"></div>
    <div class="left borderless">
      <div class="left1 module"></div>
      <div class="left2 module"></div>
    </div>
    <div class="right borderless">
      <div class="right1 module"></div>
      <div class="right2 module"></div>
    </div>
    <div id="foot" class="module"></div>
  </div>

  <!-- 持续的数据输出和渲染 -->
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('head1', '<div class="content">Header</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('left1', '<div class="content">Category Chooser</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('left2', '<div class="content">My eBay</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('right2', '<div class="content">Stats</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('head2', '<div class="content">Rebates</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('foot', '<div class="content">Footer</div>');
  </script>
  <?php
    ob_flush();
    flush();
    sleep(1);
  ?>
  <script>
    inject('right1', '<div class="content">Ad Rec</div>');
  </script>
</body>
</html>

BigPipe 真正将网页布局和数据渲染分离了,它让用户感知到页面是活的。其中还涉及很多细节,这比单纯用模板去渲染页面要复杂多了。我们还需要大量的商业应用实践经验才能找到 BigPipe 的最佳实践。

Reference