What?
BigPipe 是 Facebook 工程师蒋长浩于 2010 年的 Velocity 大会上分享的一个议题,它的提出主要是为了解决重数据页面的加载速度问题,这在业界引起了巨大的反响。
BigPipe 是一个重新设计的基础动态网页体系,它将传统的网页分解成了一个个 pagelet。先向客户端输出页面的基础框架(钢筋房),然后再按需加载页面所有的数据(装修),最终完成整个页面的渲染。虽然说这是一个重构的基础动态网页体系,但是我们不需要改变现有的通信机制(B/S),因为它完全是基于 PHP 和 Javascript 来实现的(如今 Node 也可以实现了)。
Why?
在知道为什么需要 BigPipe 之前,我们先来了解传统的动态网页体系,这是一个十几年的老家伙了,它的服务质量已经无法满足今天用户的需求了。在传统的体系下,用户请求的生命周期如下:
- 浏览器向服务器发送一个请求
- 服务器解析请求,从存储中读取数据,然后制定一个 HTML 文本,并通过一个 HTTP 响应将其发送到浏览器
- HTTP 响应通过网络传输到服务器
- 浏览器解析来自服务器的响应,使用 HTML 文本构建了一个 DOM,并下载引用的 CSS 文件和 Javascript 文件
- CSS 文件下载完之后,浏览器会解析它们,并将它们应用到 DOM
- Javascript 文件下载完之后,浏览器会解析并执行它们
传统的模式面临着一个很大的问题——无法并行请求。为了解决这个问题,Ajax 应运而生。虽然说 Ajax 解决了并行请求的问题,但是 Ajax 在庞大的数据面前有很致命的缺点:服务器负载高、浏览器资源浪费以及用户体验不友好。而 BigPipe 很好地规避了 Ajax 的缺点。
“怎么让客户机跑得更快?”是一个永恒的话题。可以看到 BigPipe 与传统模式相比性能大大提升了,最显著的是用户感知延迟的时间缩短了一半,使用 BigPipe 来构建复杂的数据页面也自然而然成为了新的趋势。
How?
BigPipe 到底是怎样运作的呢?
为了充分利用 browser 和 server 之间的并行性,BigPipe 首先将网页划分成多个可调用的模块—— pagelets。正如微处理器的流水生产线划分的指令一样,BigPipe 也将页面的生成过程划分成几个阶段:
- 解析请求:服务器解析和 http 请求的完整性检查
- 获取数据:服务器从存储层获取数据
- 生成标记:服务器生成响应的 html 标记
- 网络传输:响应从服务器传输到浏览器
- 加载 CSS:浏览器加载页面引用的 CSS 文件
- 构建 DOM 和应用 CSS:浏览器先构建好 DOM tree,然后再应用样式
- 加载 Javascript:浏览器加载页面引用的 Javascript 文件
- 执行 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 的最佳实践。