情景模拟:
当网页越来越大,相应的请求数据,css,js等也越来越多,页面的加载速度就变的越来越慢慢,通过研究数据表明,用户能够容忍的大概为2.5s,如果达到3到4s还是白页的话,那么用户基本也就失去耐心。
目前我们的处理方法基本基于ajax来减少加载时间。
页面完整加载流程如下:
1) 用户访问网页,浏览器发送一个HTTP 请求到网络服务器
2) 服务器解析这个请求,然后从存储层去数据,接着生成一个html 文件内容,并在一个HTTP Response 中把它传送给客户端
3) HTTP response 在网络中传输
4) 浏览器解析这个Response ,创建一个DOM 树,然后下载所需的CSS 和JS文件
5) 下载完CSS 文件后,浏览器解析他们并且应用在相应的内容上
6) 下载完JS 后,浏览器解析和执行他们
可以看出,这些都是按照严格的顺序来执行的,当其中有一个操作没有执行结束,后面的操作就不会之执行,即操作之间不能重叠。这样就易造成性能的瓶颈。(这样就造成性能的瓶颈:服务器生成一个页面的内容时,浏览器是空闲的,显示空白内容;而当浏览器加载渲染页面内容时,服务器又是空闲的,时间与性能的浪费由此产生。)
解决方案:
bigpipe 是有Facebook发明的一种页面加载技术,即一种减少http请求的,首屏快速加载的异步加载页面方案;其核心思想是并行,服务器生成的数据和浏览器渲染数据的并行。
php实现原理:利用ob_flush()与flush()将缓冲区的内容提前输出,浏览器可提早加载这部分的内容,无需等待所有输出完成再加载。将页面内容划分为一个个小块,输出一个后在输出下一个,使用户尽早看到页面内容,优化用户体验。
详细内容:
bigPipe提出分块的概念,即根据页面内容位置的不同,将整个页面分成不同的块儿– 称为pagelet。
将众多pagelet加载的不同阶段像流水线一样在浏览器和服务器上执行,这样就做到了浏览器和服务器的并行化,从而达到重叠服务器端运行时间和浏览器端运行时间的目的。使用BigPipe 不仅可以节省时间,使加载的时间缩短,而且可以同过pagelet的分步输出,使一部分的页面内容更快的输出,从而获得更好的用户体验。BigPipe 中,用户提出页面访问请求后,页面的完整加载流程如下:
1) Request parsing:服务器解析和检查http request
2)Datafetching:服务器从存储层获取数据
3) Markup generation:服务器生成html 标记
4) Network transport : 网络传输response
5) CSS downloading:浏览器下载CSS
6) DOM tree construction and CSS styling:浏览器生成DOM 树,并且使用CSS
7) JavaScript downloading: 浏览器下载页面引用的JS 文件
8) JavaScript execution: 浏览器执行页面JS代码
初略看一下也许会好奇这个和刚开始说的那个没什么区别,但这8个流程属于一个pagelet,而多个pagelet 的不同操作阶段就可以像流水线一样进行执行了。
与ajax比较
1) AJAX 的核心是XMLHttpRequest,客户端需要异步的向服务器端发送请求,然后将传送过来的内容动态添加到网页上。如此实现存在一些缺陷,即发送往返请求需要耗费时间,而BigPipe 技术使浏览器并不需要发送XMLHttpRequest 请求,这样就节省时间损耗。
2) 使用AJAX时,浏览器和服务器的工作顺序执行。服务器必须等待浏览器的请求,这样就会造成服务器的空闲。浏览器工作时,服务器在等待,而服务器工作时,浏览器在等待,这也是一种性能的浪费。使用BigPipe,浏览器和服务器可以并行同时工作,服务器不需要等待浏览器的请求,而是一直处于加载页面内容的工作阶段,这就会使效率得到更大的提高。
3) 减少浏览器发送到请求。对一个5亿用户的网站来说,减少了使用AJAX额外带来的请求,会减少服务器的负载,同样会带来很大的性能提升。
缺点
不利于seo,针对爬虫需要通过ua判断,爬虫还是用传统模式,用户就用bigpipe
注意事项:
1.如果使用nginx作为web服务器,那么nginx可能会缓冲php的输出。即便是调用了flush方法,相应内容也会被nginx缓冲,而不会输出到浏览器。
2.某些浏览器也会有缓冲,如在接收的数据小于一定值的时候,不会对代码进行渲染。
补充:
php文件最终在浏览器上显示,走过3个缓冲阶段
即:php buffer=》web server buffer=》浏览器buffer
1)php buffer
php运行的结果先放入缓冲区(buffer),只有当缓冲区满了或者php运行完毕,才将数据输出去。
所以这边通过ob_flush将缓冲区的内容强制输出。
2)web server buffer
目前我们接触的也就apache和nginx
apache buffer
当php的数据给apache服务器时,也会想放入缓冲区(当缓冲区数据满或者执行完毕时,才执行数据)
这边我们可以在php层使用flush()来强制将缓冲区数据输出
(旧版本Apache可能需要关闭GZip。)
nginx buffer
nginx使用fastcgi缓冲区来缓存数据,fastcgi是强制将buffer打开的,无法关闭。
应对方法:通过apache+php实现bigpipe,nginx放在代理服务器的角色上,并使用proxy_buffering关闭代理缓存
3)浏览器buffer
这里研究得不深,不过这里指的不是讲图片,样式,头信息和js等缓存,而是和上面一样,将内容想放入缓存区中,当请求完毕和缓存写满后在输出。
这一层清楚方法未知,不过影响不是很大,一般主流的不超过4k。
实现代码
见附件
直接将txt换成php运行即可、
aaa
这边只是用php简单的实现其大概思想,但这样并不支持多线程,但对于小网站来说这样串行加载已经可以达到加载要求了。
对于大型网站,那么就需要考虑多线程执行这些pagelet。由于php不支持线程,所以可以使用的方式大致有curl,stream_select和php5新增的stream_socket_client函数或者用PHP 的扩展模块paaactnl模块中的pcntl_fork()函数来生成子进程。
——————————————————————————————
相关参考
http://www.searchtb.com/2011/04/an-introduction-to-bigpipe.html(bigpipe详细介绍以及相关bigpipe参考链接)
http://taobaofed.org/blog/2016/03/25/seller-bigpipe-coding/(bigpipe编程实践)
http://blog.sina.com.cn/s/blog_4dd475390102ebe6.html(bigpipe注意事项)
http://www.bo56.com/%E4%BB%8Ephp%E7%9A%84%E7%BC%93%E5%86%B2%E5%8C%BA%E8%AF%B4%E8%B5%B7/(从php缓存去说起)