小測幾種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時須要做爲一個注意事項加以考慮。

相關文章
相關標籤/搜索