介绍bigpipe以及在django上的实现

关于BigPipe是在看一篇淘宝ued的官方博客上看到的,原文是说用nodejs做前后端分离的,只是稍微提了一下bigpipe。

感兴趣的同学也可以看一下那篇文章,http://ued.taobao.org/blog/2014/04/full-stack-development-with-nodejs/


于是百度之,发现bigpipe是由facebook最先提出,个人感觉是个非常有意思的想法。

关于bigpipe的介绍,网上有很多,这里简单说一下:

我们平常打开网页通常都是串行的,服务器收到请求后,开始各种渲染页面,等页面全部渲染好之后,再返回给浏览器,而在渲染过程中,浏览器则一直处于等待状态。

加入服务器有几个耗时的操作,总共需要花费10秒,则在这10秒钟内,浏览器属于一片空白,用户体验很不好。

而bigpipe则是服务器接受到请求之后,立马返回一段骨架html,但是不包括闭合的body和html标签,这时候response并没有结束,每当服务器端准备好一块数据,就立即flush给浏览器,浏览器在收到骨架html之后,就立即开始渲染,之后每得到一段数据都进行渲染。

这样的好处是,也许整个页面也需要10秒才能完全显示出来,但是浏览器在第一秒就开始有东西显示。


还有一点需要特别说明的是,bigpipe使用javascript渲染页面,也就是说返回的是一对script标签,里面是一段javascript代码,这样的好处是,渲染页面的时候不会被块位置束缚,并且服务器支持多线程处理的话,可以同时处理多块内容,哪块先处理好,就flush回浏览器,不用在意html代码的物理顺序。后面每段返回的pagelet

也许有人会有疑问,咋看起来,BigPipe和Ajax非常像,那他们有什么不同呢。

主要的不同点在于,Ajax每一块需要单独发送一个HTTP请求,建立连接的开销是比较大的,而BigPipe只有一个HTTP请求。所以Ajax相对于BigPipe来说,对服务器造成的压力更大。


网上关于BigPipe的实现有很多,php和node.js是用的比较多的。


下面说下在python django框架下,实现一个BigPipe的例子,也方便大家理解BigPipe的思想。

首先,我们创建一个骨架模板,这个就是服务器在接收请求后,立即返回的html:

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
	<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	
	<script type="text/javascript" src="/site_media/js/prototype.js"></script>
	<script type="text/javascript" src="/site_media/js/prototypepatch.js"></script>
	<script type="text/javascript" src="/site_media/js/bigpipe.js"></script>
		
	<title>{{title}}</title>
</head>
<body >
	<div id="content_0"></div>
	<div id="content_1"></div>
	<div id="content_2"></div>
	<div id="content_3"></div>
	<div id="content_4"></div>
	<div id="content_5"></div>
	<div id="content_6"></div>
	<div id="content_7"></div>
	<div id="content_8"></div>
	<div id="content_9"></div>


这段代码主要是有10个div,待会儿我们返回的内容就放在这些div里面,注意这里没有 </body>和</html>  这里引入了三个js文件,我们后面再说。


然后就是接收请求的django view:

import time
from django.http import StreamingHttpResponse
from django.template.loader import render_to_string

def test(request):
    return StreamingHttpResponse(stream_response_generator())

由于我们不立即返回整个请求,所以并不直接返回HttpResponse对象,而是返回StreamingHttpResponse对象,并且这里的stream_response_generator函数返回的是生成器。关于python里面的生成器,请百度关键字yield。

stream_response_generator方法如下:

def stream_response_generator():
    
    yield render_to_string('bigpipe.html', {"title":"BigPipe Test Page"})
    for x in range(0,10):
        is_last = False
        if x == 9: is_last = True
        pagelet = dict(id="content_%s" % x, get_html_content=x, 
                        get_css_resources="", get_js_resources="", 
                        is_last=is_last )
        yield render_to_string('pagelet.html',{'pagelet':pagelet})
        
        time.sleep(1)
    yield "</body></html>\n"

yield render_to_string('bigpipe.html', {"title":"BigPipe Test Page"}) 这句就是把骨架html返回。

之后每次都会返回一段pagelet,代码为:yield render_to_string('pagelet.html',{'pagelet':pagelet})。

之后休息1秒再返回下一段pagelet,这个是为了模拟服务器耗时操作。

最后返回</body>和</html>标签: yield "</body></html>\n"


这里用了一个叫pagelet.html的模板文件,如下:

<script type="text/javascript">
  BigPipe.onArrive({
  	id: '{{ pagelet.id }}', 
	innerHTML: '{% filter escapejs %}{{ pagelet.get_html_content }}{% endfilter %}', 
	css_files: ["/site_media/css/head.css", "/site_media/css/home.css"], 
	js_files: ["/site_media/js/utils.js"], 
	is_last: {{ pagelet.is_last|yesno:"true,false" }}
	});
</script>

这段javascript里面用到的BigPipe对象就是我们一开始在骨架HTML中引入的bigpipe.js中提供的,bigpipe.js依赖于prototype库。

bigpipe.js的github地址为:https://github.com/msroot/bigpipe

这里简单说下几个参数的意思:

id: 这个就是要把html放到哪个id下面,比如我们要放到id为content_0的div下面,这里就填content_0;

innerHTML: 就是具体的html代码;

css_files: 这段html代码依赖的css,bigpipe.js会先加载css文件,并且相同的css文件只会加载一次;

js_files: 这段html代码依赖的js文件,bigpipe.js会最后加载js文件,基本是等所有pagelet加载完才开始加载js文件;

is_last: 标识这个pagelet是否是最后一个。


下图是用firebug看到的整个请求过程:



网页上的0到9,基本是一个一个出来的,间隔约1秒,整个请求是10.04秒,其中等待响应时间只有26毫秒。

还要注意一点就是响应头中的 Transfer-Encoding:chunked。也就是告诉浏览器,这个是分段返回的。


还有一点,apache等http服务器会对返回进行一定的缓存,也就是等有一定数量的文本再返回,这样我们如果直接跑上面的代码,不会得到我们预期的结果,这里需要把apache的mod_deflate模块给disable掉,在配置文件中加上:

SetEnvIf Request_URI ^/mysite no-gzip=1
关于更多disable mod_deflate模块的信息,可以参考 http://stackoverflow.com/questions/1922934/how-to-disable-mod-deflate-in-apache2


至此,大概就可以了解了bigpipe的整个思想,以及在django上的实现。


最后总结一下:

BigPipe是个非常有意思的想法,并且已经在Facebook以及淘宝等大型公司使用了比较长时间。很可能是未来前端优化,提升用户体验的主要手段。

还有一点不足,由于要依赖于客户端javascript进行一部分html渲染的工作,所以服务器端返回的pagelet中的js代码要依赖于浏览器中javascript的实现,有些库用innerHTML表示需要填充的html,有些库用content等(一开始就吃了这个亏,还好可以看bigpipe.js的源代码)。所以貌似没有一个统一的标准,这样不方便写通用的库。


一些介绍BigPipe的文章:

http://www.searchtb.com/2011/04/an-introduction-to-bigpipe.html

http://huoding.com/2011/06/26/88

javascript的bigpipe:

https://github.com/msroot/bigpipe

一个django关于bigpipe的扩展,但是4年前就没更新了,可以看看源码,领会精神:

https://github.com/orygens/django-bigpipe

https://pypi.python.org/pypi/django-bigpipe

django关于bigpip的一些内容:

http://www.slideshare.net/gagedark/even-faster-django-27352247   (需要翻墙)

http://www.slideshare.net/slawdan/bigpipe1126adev  (也需要翻墙)

django返回stream content:

http://stackoverflow.com/questions/2922874/how-to-stream-an-httpresponse-with-django

Node.js的bigpipe实现:

https://bigpipe.io/

https://github.com/bigpipe/bigpipe.js


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值