小测几种python web server的性能

http://blog.csdn.net/raptor/article/details/8038476python

由于换了nginx就再也不使用mod_wsgi来跑web.py应用了,如今用的是gevent-wsgi,效果还不错。但仍是想试试别的,好比传说中超级猛的meinheld什么的。nginx

软硬件环境

硬件:web

一台04年初购置的IBM X235服务器,CPU为Xeon 2.4G两颗,内存1G,100M网卡。sql

软件:数据库

Ubuntu Server 10.04 LTS
Apache 2.2.14
Nginx 0.7.65
Python 2.6.5
web.py 0.37
mako 0.7.2
sqlalchemy 0.7.8
gevent 0.13.7
gunicorn 0.14.6
meinheld 0.4.15编程

测试代码

有五个版本,分别是:flask

最基本的WSGI helloworld服务器

[python]  view plain copy
 
  1. def application(environ, start_response):  
  2.     status = '200 OK'  
  3.     resp = "Hello world!"  
  4.     resp_headers = [('Content-type','text/plain;charset=utf-8')]  
  5.     start_response(status, resp_headers)  
  6.     return [resp]  

最基本的web.py your IP多线程

[python]  view plain copy
 
  1. class yourip:  
  2.     def GET(self):  
  3.         return "Your IP is : %s" % web.ctx.ip  

加了mako模板的web.py并发

[python]  view plain copy
 
  1. class onlymako(BaseHandler):  
  2.     @expose("yourip")  
  3.     def GET(self):  
  4.         return dict(yourip=web.ctx.ip)  

加了数据库的web.py

[python]  view plain copy
 
  1. class onlydb:  
  2.     def GET(self):  
  3.         created = web.ctx.provider.once_access(web.ctx.ip)  
  4.         return "You have logged : %s" % created  

和同时加了数据库和mako模板的web.py —— 这也是最接近实际应用的状况

[python]  view plain copy
 
  1. class makodb(BaseHandler):  
  2.     @expose("index")  
  3.     def GET(self):  
  4.         created = web.ctx.provider.once_access(web.ctx.ip)  
  5.         return dict(created=created)  

测试目标

五种运行环境:

web.py的测试环境app.run
web.py+gevent-WSGI
gunicorn默认(sync)
gunicorn+gevent
gunicorn+meinheld

gunicorn使用 -w 4 参数,经实际比较过,这个参数并不能有效增长rps,但在高并发测试状况下能够在必定程度上减小失败率。

测试方式

ApacheBench 2.3(Ubuntu Desktop 12.04)

RPS测试参数为 -n 200 -c 22 测五次取rps平均值。

之因此用这样比较小的参数,是由于再高了,其中某些测试就通不过了。

测试结果

RPS测试 WSGI helloworld Web.py YourIP Web.py+Mako Web.py
+SQLAlchemy
(SQLite)
Web.py+Mako
+SQLAlchemy
(SQLite)

Web.py
+SQLAlchemy
(Postgresql)

Web.py+Mako
+SQLAlchemy
(Postgresql)
Web.py app.run 130 93 75* 45* 59 40
web.py+gevent 422 130 82 54 74 53
gunicorn default 854 439 136 93 66 90 62
gunicorn+gevent 695 291 115 74 56 78 56
gunicorn+meinheld 3565 682 160 84 65 98 64

其中加了“*”的数字发生了数据库错误,仅供参考。

另外,用一样的参数测了一下Apache2处理静态文件的rps为:1780,nginx为:2951。

===附加的分割线===

再附加一个在个人Atom小服务器上的部分测试结果:

Nginx处理静态文件的rps为:4000左右。

WSGI hello world测试:gunicorn+meinheld约为:3800。gunicorn+gevent约为:1100。gunicorn default约为:1400。

your IP测试:gunicorn+meinheld约为:1000。gunicorn+gevent约为:450。gunicorn default约为:700。

相比之下这个Atom小机器比IBM服务器猛好多。硬件发展速度真是太快了。

附软硬件环境:

Atom D525 1.6G双核,2GRAM,FreeBSD 9,Nginx 1.2.2,Python 2.7.3,其它同IBM服务器。

而且在这个Atom机器上 -w 参数对rps有明显贡献…看来是由于老服务器的问题才效果不明显,只是不肯定是硬件问题仍是OS问题。

结论

meinheld的确猛,处理最简单的WSGI比Apache2处理静态文件还要猛一倍,跟Nginx至关甚至更强。

app.run的rps比预想的要好不少,只是并发数真不行,-c 参数略加大就会出现大量失败测试。

gevent已经足够好了,不过竟然比gunicorn的sync模式还慢真是有点不科学啊。

gunicorn的好处在于管理方便,而且能够灵活使用各类work_class,特别奇怪的一点是竟然sync模式也这么快。

web.py的性能影响仍是很明显的。

模板的影响也很明显——mako已经算是很快的模板了,真不知道用那些KID之类很慢的模板会是什么效果。

数据库的影响更大,不过pgsql的性能竟然能跟sqlite差很少,真不知道是sqlite太慢仍是pgsql太快…

原本前一阵我刚据说OpenResty这货的时候仍是颇有兴趣的,可是如今看到meinheld的表现之后,以为仍是算了吧,毕竟用OpenResty还要去学Lua…如今要学的东西太多,能用Python搞定的事情就不去麻烦Lua了。

 

 

上次的测试见《小测几种python web server的性能》。

前两天参加了PyCon2012上海站。虽然今年的PyCon被各类吐槽,但仍是有点收获的。好比ShellXu的元编程,赖总的state/message,沈大侠谈的pypy等。

回来就想测一下用pypy跑web应用看看性能如何。顺便也对上次的测试范围做了点扩大化。

PyCon上谈到的Pyramid我虽然没用过,可是前身Pylons和Turbogears我是用过的,只是如今对这种重量级的东西兴趣不大。

轻量级的框架除了上次测试用到的web.py之外,bottle和flask也是很热的东西,尤为是flask,可是由于它对Werkzeug的依赖令我不是很喜欢——我不喜欢名字很差念的东西,除了Werkzeug之外还有像Django这种。

不过此次仍是都拿来测了。软硬件环境与上次测试相同。测试代码功能都是 your IP。bottle和flask的测试代码由令狐虫(http://ch-linghu.me/blog/)友情提供,特此鸣谢。

Server都是用的gunicorn default(sync),单进程。没有特别精确统计,取近似平均值。

RPS测试 WSGI helloworld Web.py YourIP Bottle Your IP Flask Your IP
python 850 440 580 400
pypy(*) 1100 800 1000 600

就这个结果来看,pypy的做用仍是比较明显的。另外flask看上去也通常啊,虽然号称扩展能力强,不知道扩下去性能是否是会影响更大。但bottle的确是很不错的样子。

(*)固然由于pypy是JIT方式运行的,因此有一个“预热”的过程。请求量太小的时候不但不会更快,反而会慢不少。基本上在“冷”状态下,pypy环境下的RPS只有python环境的一半不到。须要有连续大量请求以后才能达到“热”状态。上面的RPS数据都是“热”状态的结果。

达到“热”状态的过程也各不相同。标准WSGI在几百个请求以后便可达到,bottle大概须要一千多个请求,web.py须要将近一万个请求,而flask要超过一万。

另外,就测试的软硬件环境来讲,gunicorn default(sync)单进程最高只能达到130个并发左右,4进程能够达到270个并发左右。由于换用不一样的框架结果都是如此,固然标准WSGI会略高一些(单进程170左右),因此主要应该仍是gunicorn决定。

换用gunicorn+gevent测试的话,全部框架上到单进程1000并发没问题,只是RPS会有相应降低而已。这个时候就体现出gevent相比sync的优点所在了——虽然低并发时sync速度比gevent还快,可是在高并发下,sync直接死掉(无响应直至超时),gevent只是速度慢了而已。

一样换用gunicorn+meinheld,最高并发也只能到260而已,只比sync模式高一倍,但仍是远低于gevent。并且meinheld的表现比较奇怪,一开始还能够接受1000并发的,而后就愈来愈少。另外,它的表现也与gevent不一样,gevent在高并发下是一开始就按比较低RPS工做,而meinheld则是仍然以高RPS工做,但到后面就会爆掉(客户端显示链接被服务端中断)。

经令狐分析,可能的缘由是这样的:

sync从字面上理解应该是使用线程阻塞模式,因此在低并发下能够利用到服务器的多核,因此RPS比gevent要高。可是在高并发下,由于线程过多,致使线程切换成本太高失去响应。

meinheld应该也是使用了多线程(同时使用greenlet的协程),因此性能最好,可是在高并发下一样会面临与sync同样的多线程问题。

gevent则胜在轻量,虽然没有多线程利用多核的优点,但一样能够用多进程来利用,并且在高并发的时候,处理不了的请求是在排队中,因此不会爆掉或死掉,只是RPS降低而已。固然,在极端高并发的状况下,请求队列是有可能溢出的,但那已是比sync/meinheld高得多得多的并发了。

不过以前对meinheld过于乐观的见解是须要改变了,由于在一样高并发的状况下,nginx毫无压力,虽然RPS降低很多,但仍然是远高于gevent,更不用说已经趴下的sync和meinheld……

最后,在测试中还发现了bottle的一个性能问题:404错误的响应比正常页面慢不少。令狐所以对测试程序做了改进,加了一个自定义404页面,这样404的响应就和正常页面差很少了。这一点在使用bottle时须要做为一个注意事项加以考虑。

相关文章
相关标签/搜索