选择Google App Engine框架:慎用Django

GAE开发者或许会注意到这点,查看控制台的访问记录时经常会发现红色或黄色标记,表明该访问使用了较多的CPU时间。

对用户而言,0.5秒以内的响应时间(含网络延迟)比较完美,1秒以内尚可接受,2秒以上就会觉得很慢了。

而我使用的cpedialog, 初次访问首页将花费1.9秒的CPU时间和0.7秒的API CPU时间(约1.6秒响应时间,不含网络延迟),紧接着的访问只需0.35秒的CPU时间和0.17秒的API CPU时间(约0.2秒响应时间),隔几分钟后访问则需要1.1秒的CPU时间和0.17秒的API CPU时间(约0.8~1.2秒响应时间)。

由于这个blog我基本不使用,所以只有约5%的概率可以让用户感觉不错,这个性能确实很糟糕。

实际上这和GAE的构架有关。当服务器接到访问时,会去执行该url对应的handler,并将其全局变量缓存起来。再次访问时,如果缓存还存在,那么就使用该缓存,无需重做所有初始化工作。这就大大节省了CPU时间。

而这个缓存的存在时间是不确定的,大约为1分钟。也就是说,假如访问者都不是同时访问的,那么如果网站每天的访问量不到1440,则每个人初次访问的感受都会很慢。

而Django这个庞大的东西就更夸张了,它的启动时间约为1~4秒。如果不是一个访问量很大的网站的话,那么带给用户的感受会是极差的。

如果不介意占用过多的CPU时间(实际上就是你的money),用计划任务每几十秒访问一个小页面,可以让Django实例停留在缓存中。

目前仍无其他办法解决该问题,所以小站还是慎用Django比较好。

话说回来,我这网站的访问量(目前约800页/天)估计是维持不了Django的,所以可能得自己从头设计,或者找个性能不错的小框架算了…

顺便做了个测试,比较了7种方案实现hello world所需的CPU时间(单位:毫秒)。
其中CGI是指直接用print输出。
WSGI是指构造一个WSGI的hello world函数。
Django 1.1使用了app-engine-patch 1.1RC的例子,稍微修改了一下,删去了一些用不到的模块和设置,且直接用HttpResponse()方法输出字符串(未使用模板)。
web.py则使用web.application对象的cgirun()方法,并也缓存了web.application对象。
uliweb则只是缓存了application对象。但uliweb和pytz都包含很多文件,我也没研究出zipimport的方法,应该会很影响速度。
Tornado未经过修改,template模块实际上用不到,但我不知道咋去掉,所以也会影响些性能。

  未缓存 已缓存
CGI 96 3
WSGI 96 4
webapp 96 4
Django 1.1 1340 10
web.py 0.32 155 6
uliweb 0.0.1a1 427 4
Tornado 0.2 233 5

从结果来看,webapp的表现非常不错,只有约0.1秒的启动时间。但别忘了cpedialog也是采用webapp,启动速度却很慢。原因就是 webapp必须在启动时将所有页面用到的handler都import进来,而有些框架却是惰性加载,减少了启动时间。此外,cpedialog还有大 量数据库和memcache操作,也很费时。

而web.py这种小型的框架启动时间也达到了233毫秒,再加上数据库操作和渲染模板,几乎不可能保证0.2秒的响应时间了。不过删掉db、 debugerror、template、form、session、browser、test和webopenid的话(注意在 application.py里也得去除template),启动时间可以降低到155毫秒,较为接近webapp了,而且还支持惰性加载。

但如果访问量较大的话(保证每分钟至少1次页面访问),使用Django也不错。因为在都已缓存的情况下,各种方案的差距都不大,重点则在自己的代码和数据库操作等时间了,框架则不太影响性能。

值得一提的是,Tornado是唯一会根据内容生成Etag头的(查看源码发现是使用SHA-1):如果第2次访问该网址使用的If-None- Match与将要生成的Etag相同,服务器就会直接返回304状态码,这就减少了传输时间,对于较为静态的页面能节省很多传输时间和流量。经我测 试,IE 7、Firefox和Chrome都会接收到304,而IE 6不会去缓存。

综合看起来,似乎没有一个完美的,如果能把webapp改成惰性加载,我应该会选它的。

考虑到Django在已缓存的情况下还算快(约0.01秒),如果用计划任务每30秒访问一个hello world页面,一天也就2880次访问,不到30秒的CPU时间(约合0.0008美元),似乎很划算。但目前计划任务的最短时间间隔是1分钟,可能会 在缓存刚过期时才访问。

尚不清楚使用sleep()函数能否延长缓存时间,实在不行的话,只好用任务队列了,这个的速率可以到每秒10次。

另外,对于特别小型的额外页面(我经常会做些小测试和小服务之类的),似乎也没必要放在Django框架下,直接用app.yaml分配,再用webapp处理比较好。对于xmpp和incoming email来说更是如此。

不过对于框架我还有些特殊的要求,希望能把对appspot.com的域名的HTTP访问重定向到apps域名上去,此外还能对部分url自动进行unquote操作(就是为了解析中文网址),而且还要能重用main()函数。

对于前者,webapp可以通过创建多个全局WSGIApplication对象,或者封装WSGIApplication对象来做到,后者则用Decorator封装handler即可。

web.py的处理应该和webapp类似,uliweb似乎很麻烦,而Django我还得研究研究。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。