4
回答
Python WSGI服务器大乱斗(Rev.2)
终于搞明白,存储TCO原来是这样算的>>>   

最近在物色高性能的python web服务器,一来是毕业设计验收的时候唬老师用,二来是公司项目有这个需求,说不定以后还能用上我的测试数据和设计方案。一般来说,web服务器常见的有nginx,apache,lighttpd等,我是个nginx控,所以一开始就把apache,lighttpd给抛弃了,采用nginx作为前端。当然,前端固然很重要,但更加重要的是后端的处理服务器,以不同模式为基础构建的web app服务器决定着整个系统的响应快慢。通过参考Nicholas Piel写的《Benchmark of Python WSGI Servers》我圈定了以下几个服务器(模块):mod_wsgi for nginxfapws3, geventuwsgi,以及陪同测试的django自带FastCGI prefork/threaded模式。mod_wsgi代表nginx原配python wsgi协议支持,fapws3是Fast Asynchronous Python Web Server的简称,使用processor/thread模型,gevent是走的lightweight threads路线,uwsgi采用的是nginx的模块+自身socket多processor/thread模型,之所以没选择callback/generator类型的服务器是因为都还不成熟,在Nicholas的那篇文章里面可以看出他们丢包率以及性能都不尽人意。首先,介绍硬件情况:公司一台年久失修的服务器,P4 2G单核,512M内存,1.5G Swap,Ubuntu 8.10 Desktop,上面还开了个Nagios监控器和个FireFox,性能绝对是你能想象出的底线。原本服务器上跑了个Apache2,因为很久没有使用apache2了,实在是懒得去翻document找如何配置,就又装了个nginx 0.8.35,今天升级到了0.8.36,性能上2者基本毫无区别,36版只是针对35的bug进行了修复,所以对结果没有实质上的影响。django 1.1+flup作陪同的FastCGI prefork/threaded模式测试,测试软件采用Apache Benchmark Version 2.3。

首先是Django的配置,生成一个新的项目,直接访问其默认页面,项目启动参数分别为

python manage.py runfcgi method=threaded host=127.0.0.1 port=3033 daemonize=false
python manage.py runfcgi method=prefork host=127.0.0.1 port=3033 daemonize=false
python manage.py runfcgi method=threaded host=127.0.0.1 port=3033 daemonize=false minspare=50 maxspare=50

为什么第三个不同,是因为参考了JavaEye上的一篇评测,采用静态进程池的话性能会比动态线程生成高2倍左右(评测原文
Nginx的FastCGI服务器参数采用默认参数

在ab -n 1000 -c 20的情况下,5次运行得到的Request Per Second分别如下:

nginx+fcgi+prefork 44.06 54.93 42.6 41.08 41.89
nginx+fcgi+thread 242.12 195.96 243.69 209.59 204.59
nginx+fcgi+prefork+spare 119.51 202.89 177.6 206.72 206.85

可见使用静态进程池之后进程模式的性能有大幅提高。本来下一个是Nginx+mod_wsgi的测试,很可惜的是,不知道是配置原因还是其他什么问题,在4个连接测试完之后mod_wsgi就死掉了,因此这次测试中只好放弃。
接着是fapws3,号称最快的Python WSGI服务器,我们来看看起到底有多快。
PS: 安装的时候需要安装libev-dev包,fapws3是基于libev技术实现的。
使用的Django Wrapper如下:

import fapws._evwsgi as evwsgi
from fapws import base
import time
import sys, os
sys.setcheckinterval(100000)
sys.path.append('/opt/www/djtest/')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from fapws.contrib import django_handler, views
import django

def start():
    evwsgi.start("0.0.0.0", 98)

    evwsgi.set_base_module(base)

    def generic(environ, start_response):
        res=django_handler.handler(environ, start_response)
        return [res]

    mediafile=views.Staticfile(django.__path__[0] + '/contrib/admin/media/', maxage=2629000)
    evwsgi.wsgi_cb(("/media/",mediafile))
    evwsgi.wsgi_cb(('',generic))

    evwsgi.set_debug(0)
    evwsgi.run()

if __name__=="__main__":
    start()

结果如下:

fapws 379.32 406.58 413.83 399.29 414.61
nginx+proxy+fapws 328.61 337.2 326.6 262.6 274.57

不错,非常不错,将近400的RPS,即便是被NGINX包了一层反向连接的情况下依然还有平均300上下的RPS表现,很对得起自己的称号,唯独美中不足的是Fapws不支持HTTP1.1,其官网也是说HTTP1.1是RoadMap中计划的,什么时候实现?别问我~

然后是uWSGI,这个WEB服务器有点特殊,它本身支持作为其他服务器的模块而存在如nginx,apache,cherokee等,但是其自身也是需要同时打开作为一个socket存在于系统之中,你可以开多个socket文件,这样就可以通过nginx实现单机分流,只不过谁知道有没有用捏。
安装方法参考nginx+mod_wsgi的方法安装,不同的是加入nginx模块后还需要编译一个对应本机python版本的uwsgi放入/usr/bin目录下然后用如下命令启动。
启动命令:

uwsgi -s /tmp/uwsgi.sock -C -x /usr/local/www/djtest/uwsgi.xml -d /var/log/uwsgi.log &

uwsgi.xml

/opt/www
    
        
    

uwsgi.py

import os, sys
sys.path.append('/opt/www/djtest/')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

结果不是很好,但也不是很挫。

nginx+uwsgi 294.78 248.21 304.98 296.5 304.43

最后是Gevent,这个时候我将nginx换成了0.8.36,好在对于结果实际上是没什么影响的,性能误差在2%以内。
安装Gevent费了不少精神,其官网貌似被墙,中英文资料特别少,首先得安装libevent1.4以上,ubuntu自带的1.3是不行的,编译会报错,还得用easy_install greenlet。安装完这2个后编译使用-I -L参数定位到你安装的libevent目录下lib目录和include目录,还需要将lib目录下的所有文件做个软连接到/usr/lib目录。编译好后,就可以开始使用了。
Django Wrapper代码如下:

from gevent import wsgi
from djtest.wsgi import application
wsgi.WSGIServer(('', 95), application, spawn=None).serve_forever()

结果如下:

gevent 330.18 330.36 329.76 320.66 277.37
nginx+proxy+gevent 287.86 286.69 288.22 280.37 288.28

令人惊讶的性能,但还是不及fapws。
最后是变态版的高压力测试,ab -n 10000 -c 500,估计是这台服务器的极限了。django的FastCGI模式没有加入测试,原因是太慢了,不想等,只测试了fapws,nginx+proxy+fapws,gevent,nginx+proxy+gevent,nginx+uwsgi,结果如下:

gevent 192.58
nginx+proxy+gevent 164.84
fapws 288.14
nginx+proxy+fapws 221.01
nginx+uwsgi 173.601

fapws再次胜出,不过可以看出性能也下降了不少。值得说明的是这次测试中各种服务器均出了一点状况,单用fapws会在ab测试完之后提示大量连接被关闭,无法Response,但ab那边显示0个failed connection,fapws+nginx则不会出现,uwsgi比较搞,若出现failed connection,ab报告中server name就是nginx,完整的通过测试就是空白,难道错误都是nginx一个人的事么?gevent的一个奇特现象是在nginx包了一层的情况下稳定性竟然还不如单用gevent,这点很是奇怪,nginx只做了个反向连接哎?这完全是不应该发生的呐。

总结:如果你是个冒险者,fapws绝对是你最佳的选择,性能没话说,高并发也不是那么糟(我觉得ab不是那么靠谱),美中不足的是其不支持HTTP1.1协议。如果你担心其稳定性,用nginx包一层,只要fapws坏了你就立刻能从访问页面上得到信息,当然包一层是以性能损失为代价的,这就看自己的选择了。如果你期望用一个nginx统一后端各种CGI语言的运行,uWSGI挺不错的,类似于php-fpm的启动模式,还可以做单机分流,配合nginx强大的高并发负载能力,无论在什么条件下均有优异的表现,蛋疼的是稳定性还有所欠佳,高并发时有的连接会死掉。GEVENT则是新技术模型(lightweight threads)下一个强大的实现,低压的时候接近fapws的性能,高压的时候单用依然可以达到和uwsgi类似的RPS,无奈的是gevent和nginx搭配起来用就有点杯具了,希望以后的版本不会出现这个问题。

2010/4/24 Update:很荣幸Gevent的作者看到了这篇文章提出了一点修改的建议,于是乎今天我又按照他所说的方法重新测试了一下,得到了新的结果。
ab -n 1000 -c 20

gevent 335.21 325.91 315.43 336.77 334.94
nginx+proxy+gevent 271.02 278.95 263.30 278.58 272.16

ab -n 10000 -c 500

gevent 209.71 242.30 291.20 244.55 265.79
nginx+proxy+gevent 158.14 158.25 158.39 174.02 158.18

结果太令人惊讶了,Gevent的性能和Fapws相差5%左右,但提供了完整的HTTP1.1支持,更好的高并发访问的稳定性,当然一旦你用上nginx做前端反向代理,性能还是会再下降30%左右。如果是生产环境的话在fapws没有完整的支持HTTP1.1之前Gevent绝对是最好的选择!

举报
renwofei423
发帖于7年前 4回/13K+阅
顶部