BigPipe:高性能的“流水线技术”网页
原文地址:http://www.facebook.com/note.php?note_id=389414033919
译文地址:http://isd.tencent.com/?p=2419
作者:蒋长浩
Facebook的网站速度做为最关键的公司任务之一。在2009年,我们成功地实现了Facebook网站速度提升两倍 。而正是我们的工程师团队的几个关键的创新使它成为可能。在本文中,我将向大家介绍我们的秘密武器之一,我们称之为BigPipe的伟大底层技术。
BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。虽然BigPipe是对现有的服务网络基础过程的重新设计,但它却不需要改变现有的网络浏览器或服务器,它完全使用PHP和JavaScript来实现。
动机
为了更好的了解BigPipe,我们需要了解一下现有的动态Web服务系统,它的历史可以追溯到万维网的初期,但现在与初期相比却并没有多少改变。现代网站有着远远高于10年前的动态效果和互动性,但传统的网页服务系统早已无法跟上当今互联网速度的要求。在传统的模式,用户请求的生命周期如下:
1. 浏览器发送一个HTTP请求到Web服务器。
2. Web服务器解析请求,然后读取数据存储层,制定一个HTML文件,并用一个HTTP响应把它发送到客户端。
3. HTTP响应通过互联网传送到浏览器。
4. 浏览器解析Web服务器的响应,使用HTML文件构建了一个的DOM树,并且下载引用的CSS和JavaScript文件。
5. CSS资源下载后,浏览器解析它们,并将它们应用到DOM树。
6. JavaScript资源下载后,浏览器解析并执行它们。
传统模式在现代网站中效率是非常低下的,因为很多系统的操作顺序,不能互相重叠。一些如延时加载JavaScript、并行下载等优化技术已被网络社区广泛采用,以此来克服的一些限制。然而,这些优化却很少涉及Web服务器和浏览器的执行顺序造成的瓶颈。当Web服务器正忙生成一个页面,浏览器处于闲置状态,浪费其周期无所事事。当Web服务器完成生成页面,并将其发送到浏览器,浏览器则成为性能瓶颈并且Web服务器对其无从帮助。重叠Web服务器的生成时间与浏览器的渲染时间,我们不仅可以减少最终的时间延迟,也能使网页更早显示用户可见区域给用户,从而大大减少用户对延迟的感知。
Web服务器的产生时间和浏览器的渲染时间重叠,是特别有用的,如Facebook这样内容丰富的网站。一个典型的Facebook的网页包含许多来源不同的数据资料:好友名单,好友动态,广告等。在传统的网页呈现模式的用户将不得不等到这些查询数据都返回并生成最终文件,然后将其发送到用户的电脑。任何一个查询延迟都将拖慢整个最终文件的生成。
BigPipe如何工作
要利用该Web服务器和浏览器之间的并行性,BigPipe首先分解网页成多个可调用的Pagelets。正如流水线微处理器划分一个指令的生命周期为(如“取指令”,“指令解码”,“执行”,“写回寄存器”等)多个阶段,BigPipe的页面生成过程分为以下几个阶段:
1. 请求解析:Web服务器解析和完整性检查的HTTP请求。
2. 数据获取:Web服务器从存储层获取数据。
3. 标记生成:Web服务器生成的响应的HTML标记。
4. 网络传输:响应从Web服务器传送到浏览器。
5. CSS的下载:浏览器下载网页的CSS的要求。
6. DOM树结构和CSS样式:浏览器构造的DOM文档树,然后应用它的CSS规则。
7. JavaScript中下载:浏览器下载网页中JavaScript引用的资源。
8. JavaScript执行:浏览器的网页执行JavaScript代码。
前三个阶段执行,由Web服务器,最后四个阶段是由浏览器执行。每个Pagelet必须经过所有这些阶段顺序,但BigPipe在不同的阶段使几个Pagelets同时执行。
(Facebook主页的Pagelets,每个矩形对应一个Pagelet。)
上面的图片使用Facebook主页为例子来说明如何将网页是分解成Pagelets。该主页包括几个Pagelets:“作者Pagelet”,“导航Pagelet”,“新闻动态Pagelet”,“请求框Pagelet”,“广告pagelet”,“朋友推荐”和“联系”等他们是相互独立的。当“导航Pagelet”显示给用户,“新闻动态Pagelet”仍然可以在服务器上正在生成。
在BigPipe,一个用户请求的生命周期是这样的:在浏览器发送一个HTTP请求到Web服务器。在收到的HTTP请求,并在上面进行一些全面的检查,网站服务器立即发回一个未关闭的HTML文件,其中包括一个HTML 标签和标签的开始标签。标签包括BigPipe的JavaScript库来解析Pagelet以后收到的答复。在标签,有一个模板,它指定了页面的逻辑结构和Pagelets占位符。例如:
渲染后的第一个反应到客户端,Web服务器继续一个接一个生成Pagelets只要一个Pagelet生成,他将立即刷新到客户端在一个JSON编码的对象,包括所有的CSS,JavaScript的pagelet,它的HTML内容,以及一些元数据所需的资源。例如:
在客户端在收到Pagelet通过“onPageletArrive”发出的指令,BigPipe的JavaScript库将首先下载它的CSS资源;在CSS资源被下载完成后,BigPipe将在Pagelet的标记HTML显示它的innerHTML。多个Pagelets的CSS可在同一时间下载,它们可以根据其各自CSS的下载完成情况来确认显示顺序。在BigPipe中,JavaScript资源的优先级低于CSS和页面内容。因此,BigPipe不会在所有Pagelets显示出来之前下载任何Pagelet中的JavaScript。然后,所有Pagelets的JavaScript异步下载。最后Pagelet的JavaScript初始化代码根据其各自的下载完成情况来确定执行顺序。
这种高度并行系统的最终结果是,多个Pageletsr的不同执行阶段同时进行。例如,浏览器可以正在下载三个Pagelets CSS的资源,同时已经显示另一Pagelet内容,与此同时,服务器也在生成新的Pagelet。从用户的角度来看,页面是逐步呈现的。最开始的网页内容会更快的显示,这大大减少了用户的对页面延时的感知。如果您要自己亲眼看到区别,你可以尝试以下连结:传统模式和BigPipe。第一个链接是传统模式单一模式显示页面。第二个链接是BigPipe管道模式的页面。如果您的浏览器版本比较老,网速也很慢,浏览器缓存不佳,哪么两页之间的加截时间差别将更加明显。
性能测试结果
下图是传统模式和BigPipe性能数据比较图,数据是75%用户对一个页面中最重要的内容(例如:新闻动态被认为是在Facebook主页上最重要的内容)的感知延迟时间。收集数据方式是加载Facebook主页50次并且禁用浏览器缓存。该图显示BigPipe使用户在大多数浏览器中感受到的延迟减少了一半。
(Facebook主页的延迟时间对比)
值得一提的是BigPipe是从微处理器的流水线中得到启发。然而,他们的流水线过程之间存在一些差异。例如,虽然大多数阶段BigPipe只能操作一次Pagelet,但有时多个Pagelets的CSS和JavaScript下载却可以同时运作,这类似于超标量微处理器。BigPipe另一个重要区别是,我们实现了从并行编程引入的“障碍”概念,所有的Pagelets要完成一个特定阶段,如多个Pagelet显示区,它们都可以进行进一步JavaScript下载和执行。
在Facebook,我们鼓励创造性思考。我们不断的尝试创新技术,以使我们的网站更快。
作者蒋长浩目前是Facebook的研究科学家,他致力于研究使网站更快的各种创新。
相关文章:

超硬核全套Java视频教程(学习路线+免费视频+配套资料)
文内福利,扫码免费领取Hello,各位锋迷们,我是小千。很多学习Java的小伙伴都在找的全套免费java视频教程,这里全都有,资料齐全,拿来吧你!零基础学Java的学习路线图是怎样的?ÿ…

手机触屏滑动图片切换插件swiper.js
今天给大家分享一款手机触屏滑动图片切换插件swiper.js是一款swiper手机触屏滑动图片幻灯片,适合各种尺寸。效果图如下: 在线预览 源码下载 实现的代码。 html代码: <div style"max-width: 640px; margin: 0 auto;"><di…

Nginx防盗链,Nginx访问控制, Nginx解析php相关配置, Nginx代理
2019独角兽企业重金招聘Python工程师标准>>> Nginx防盗链 Nginx防盗链配置需要与不记录日志和过期时间结合在一起,因为都用到了location。 打开配置文件,注释掉一部分内容(红框上方),添加内容(红…

web高性能开发系列随笔
在BlogJava里写了一些关于高性能WEB开发的随笔,因为都是跟前端技术相关(html,http,js,css等),所以也贴到博客园来,吸收下人气。 1、 HTTP服务器. 2、性能测试工具推荐 3、 图片篇. 4、 如何加载JS,JS应该放在什么位置. 5、…

《Effective C++》第8章 定制new和delete-读书笔记
章节回顾: 《Effective C》第1章 让自己习惯C-读书笔记 《Effective C》第2章 构造/析构/赋值运算(1)-读书笔记 《Effective C》第2章 构造/析构/赋值运算(2)-读书笔记 《Effective C》第3章 资源管理(1&am…

观点:AI 与自动化是矛盾的
作者:cerebralab.com译者:张雨佳原文标题:AI and automation are at odds想象一下,我们生活在一个完美、和谐的地方,所有人在某一天同意让电脑代替人类驾驶汽车,而不是像现在逐步推进自动驾驶。那么&#x…

kaggle之数据分析从业者用户画像分析
数据为kaggle社区发布的数据分析从业者问卷调查分析报告,其中涵盖了关于该行业不同维度的问题及调查结果。本文的目的为提取有用的数据,进行描述性展示。帮助新从业的人员更全方位地了解这个行业。 参考学习视频:http://www.tianshansoft.com/ 数据集&am…

mysql读写分离(MySQL Proxy 安装和使用)
一、必备软件: 1、LUA 可以去LUA的官方下载:www.lua.org 2、MySQL Proxy 这里有好多二进制版本。 http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/ 或者去MYSQL官方下载源代码。 3、测试过程中取消了B和C的REPLICATION。这样SQL语句…

微软副总裁、Kubernetes 头号贡献者的他,给云原生带来奇迹!
我们做了一个街头采访,调查路人眼中的程序员到底是怎样的?提到程序员,大家似乎都有刻板印象:总是格子衬衫牛仔裤双肩包打扮,总是埋头敲代码,加班是常态……谁说程序员呆板木讷,只会埋头敲一行行…

查询Oracle中字段名带.的数据
SDE中的TT_L线层会有SHAPE.LEN这样的字段,使用: SQL>select shape.len from tt_l; 或 SQL>select t.shape.len from tt_l t; 是查询不出来的。 需要这样查询: SQL>select t."SHAPE"."LEN" from tt_l t; 转载于:…

再谈session共享
之前一篇已经写过了《springboot中redis的使用和分布式session共享问题》,但是示例不完全,本文加以完善。 使用spring-session-data-redis解决session共享,而不需要再引入其他jar即可 集成简单,上手迅速。 项目结构 1.pngpom <…

使用MySQL Proxy解决MySQL主从同步延迟
MySQL的主从同步机制非常方便的解决了高并发读的应用需求,给Web方 面开发带来了极大的便利。但这种方式有个比较大的缺陷在于MySQL的同步机制是依赖Slave主动向Master发请求来获取数据的,而且由于服务器负 载、网络拥堵等方面的原因,Master与…

Python 操作 MongoDB 数据库!
作者 |黄伟呢来源 |数据分析与统计学之美MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。先来看看MySQL与MongoDB 概念区别:今天的重点,就是要为大家讲述如何使用Python操…

Linux下用汇编输出Hello, world
下列是Intel汇编语法实现的 Hello, world!程序。 ;; hello.asm ;; nasm -f elf hello.asm; will output hello.o ;; ld -s -o hello hello.o;; section, same to segment segment .data ; 数据段声明, 下列代码将放在数据段中msg db "Hello, world!", 0xA ; 要…

利用bigpipe机制实现页面模块的异步渲染 chunked技术
bigpipe基于HTTP/1.1 支持的chunked编码,可以由浏览器接收到服务器发送的chunked块后,立即解析该块代码。因为chunked编码使消息主体成块发送,每块有自己的大小指示器,在所有的块之后会紧接着一个可选的包含实体头域的尾部。这种编…

hibernate 全面学习【lazy策略 】
2019独角兽企业重金招聘Python工程师标准>>> lazy策略可以用在: * <class>标签上:可以取值true/false * <property>标签上,可以取值true/false,这个特性需要类增强 * <set>/<list>等集合上…

深度学习发现古人类遗址,AI 考古比胡八一更高效
作者 |神经星星来源 |HyperAI超神经By 超神经内容一览:伊利诺伊州立大学人类学专业考古方向的研究人员,将空间遥感技术和深度学习应用于古人类遗址的发掘和研究。关键词:考古 遥感 机器视觉考古,一直是个神秘又充满吸引力的话题。…

linux resource
1. centos repo https://centos.pkgs.org/转载于:https://www.cnblogs.com/gojoin/p/8241068.html

MySQL 水平分区方案Spock Proxy
Spock Proxy 是由实际项目产生的一个开源项目(Spock是Rails的应用,Speck Proxy应当可用于Rails之外的,例如PHP或.NET),基于MySQL Proxy开发,是MySQL Proxy的一个分支,支持range-based horizonta…

卷学历、卷加班……程序员拥有什么能力才能破局内卷?
全世界的天才程序员都在疯狂的造轮子,其它程序员只能被动的学习轮子,这简直就像一场绝地求生。程序员行业的内卷已是有目共睹,选择程序员作为职业的人越来越多;大厂对程序员的学历要求越来越高;程序员工作加班越来越严…

LeetCode - Maximum Depth of Binary Tree
递归求二叉树的最大深度。 /*** Definition for binary tree* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val x; }* }*/ public class Solution {public int maxDepth(TreeNode root) {if(root null)return 0…
2018年IT市场最大的技术趋势和热点预测
随着数字化,物联网(IoT)设备,区块链应用以及新的服务器战略日益普及,这可能会成为2018年最大的技术趋势。我们期待看到大量与边缘计算相关的新产品,即所谓的无服务器计算,智能家居,智…

哈希分布与一致性哈希算法简介
前言 在我们的日常web应用开发当中memcached可以算作是当今的标准开发配置了。相信memcache的基本原理大家也都了解过了,memcache虽然是分布式的应用服务,但分布的原则是由client端的api来决定的,api根据存储用的key以及已知的服务器列表&…

使用深度学习阅读和分类扫描文档
作者|小白来源|小白学视觉收集数据首先,我们要做的第一件事是创建一个简单的数据集,这样我们就可以测试我们工作流程的每一部分。理想情况下,我们的数据集将包含各种易读性和时间段的扫描文档,以及每个文档所属的高级主题。我找不…

无聊的时候,冷死了(六)
阁下长得真是天生励志!好久没有听到有人能把牛吹得这么清新脱俗了!你出生时就丑的躲起来了,连你父母都不敢见你,你还怕有人举报你?你拉着一头猪逛街,很幸福的样子,我经过满怀同情的说࿱…
Java EE 开发环境搭建
下载安装Java EE SDK 版本:Java Platform,Enterprise Edition 7 SDK (with JDK 7u45) 下载页面: http://www.oracle.com/technetwork/java/javaee/downloads/java-ee-7-sdk-with-jdk-u45-2066865.html 文件名:java_ee_sdk-7-jdk7-windows.exe…

memcacheq 服务安装与原理
memcacheQ是一个单纯的分布式消息队列服务。它的安装依赖于BerkeleyDB 和 libevent,所以要先安装这BerkeleyDB和libevent: 一,BerkeleyDB 下载软件包,http://download.oracle.com/berkeley-db/db-5.0.21.tar.gz解压缩后ÿ…

AI 帮忙找 Bug ,英特尔开源代码编程工具 ControlFlag
整理 | 孙胜出品 | CSDN近日,英特尔开源了自动代码调试工具 ControlFlag 源代码,ControlFlag 源码现在可通过 GitHub 获得。据了解,ControlFlag 可用来帮助更多开发者自主检测代码错误,主要利用 AI 自动识别软件和固件代码中的错误…

一次心惊肉跳的服务器误删文件的恢复过程
经历了两天不懈努力,终于恢复了一次误操作删除的生产服务器数据。对本次事故过程和解决办法记录在此,警醒自己,也提示别人莫犯此错。也希望遇到问题的朋友能找到一丝灵感解决问题。事故背景安排一个妹子在一台生产服务器上安装Oracle,妹子边研…

【vue】vue中ref用法
1.获取当前元素: 例子: <div class"pop pos-a" :style"{ left: pop_x px ,top: pop_y px}" ref"refName"><ul><li>编辑部门</li><li click"append()">添加子部门</li>&…