Python 和 PHP 的 Web 服务器性能测试
- 非狐外传 - python.cn(jobs, news)这几天一直在玩虚拟机,测了几种Python和PHP的Web服务器的性能,顺便记录下来. 宿主:MacBook Pro MC700. 操作系统:Mac OS X 10.6.8. CPU: 2.3GHz Intel Core i5(双核). 虚拟机:Virtual Box. 操作系统:Linux version 2.6.32-5-686 (Debian 2.6.32-35).
from gevent.pywsgi import WSGIServer
import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
def application(env, start_response):
sum = 0
for i in xrange(10):
r = randint(1, 1000000 - 10)
rs = con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
for j in rs.rows:
sum += j[0]
start_response('200 OK', [('Content-Type','text/plain')])
return ['%6f' % sum]
if __name__ == '__main__':
WSGIServer(('', 8089), application).serve_forever()
启动占用6.8MB内存,测试完占用8.3MB。import gevent
from gevent.pywsgi import WSGIServer
import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
def query(r):
return con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
def application(env, start_response):
sum = 0
queries = []
for i in xrange(10):
r = randint(1, 1000000 - 10)
queries.append(gevent.spawn(query, r))
for q in queries:
rs = q.get()
for j in rs.rows:
sum += j[0]
start_response('200 OK', [('Content-Type','text/plain')])
return ['%6f' % sum]
if __name__ == '__main__':
WSGIServer(('', 8088), application).serve_forever()
结果为149 QPS,无失败请求。看来在并发足够多时,异步没有帮助,反而因为要执行更多代码而变慢了。from gevent.pywsgi import WSGIServer
import MySQLdb
from random import randint
con = MySQLdb.connect(user='root', passwd='123', db='test')
cu = con.cursor()
cu.execute('SET GLOBAL query_cache_size = 0;')
def application(env, start_response):
sum = 0
for i in xrange(10):
r = randint(1, 1000000 - 10)
cu.execute('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
for j in cu.fetchall():
sum += j[0]
start_response('200 OK', [('Content-Type','text/plain')])
return ['%6f' % sum]
if __name__ == '__main__':
WSGIServer(('', 8087), application).serve_forever()
结果为122 QPS,无失败请求。这个库已经有1年多没更新了,看来已经不够给力了。import tornado.ioloop
import tornado.web
import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
class MainHandler(tornado.web.RequestHandler):
def get(self):
sum = 0
for i in xrange(10):
r = randint(1, 1000000 - 10)
rs = con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
for j in rs.rows:
sum += j[0]
self.write('%6f' % sum)
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
启动占用10MB内存,测试完占用14MB。import gevent
import tornado.ioloop
import tornado.web
import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
def query(r):
return con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
class MainHandler(tornado.web.RequestHandler):
def get(self):
sum = 0
queries = []
for i in xrange(10):
r = randint(1, 1000000 - 10)
queries.append(gevent.spawn(query, r))
for q in queries:
rs = q.get()
for j in rs.rows:
sum += j[0]
self.write('%6f' % sum)
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
这次变得非常慢,突然想到应该是数据库并发多了10倍的原因,于是把并发数降到10,结果跑到了158 QPS。import gevent
import tornado.ioloop
import tornado.web
import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
def query(r):
return con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
self.queries = queries = []
for i in xrange(10):
r = randint(1, 1000000 - 10)
queries.append(gevent.spawn(query, r))
tornado.ioloop.IOLoop.instance().add_callback(self.output) # 或者add_timeout(time.time() + 0.1, self.output)
def output(self):
sum = 0
for q in self.queries:
rs = q.get()
for j in rs.rows:
sum += j[0]
self.write('%6f' % sum)
self.finish()
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
速度稍微提升到了164 QPS,但并发还是不高。from gevent import monkey
monkey.patch_socket()
这次飙到了178 QPS,而且可以100并发了,但是原理我还搞不懂。import amysql
from random import randint
con = amysql.Connection()
con.connect ('127.0.0.1', 3306, 'root', '123', 'test')
con.query('SET GLOBAL query_cache_size = 0;')
from eurasia.web import httpserver, mainloop
def handler(httpfile):
sum = 0
for i in xrange(10):
r = randint(1, 1000000 - 10)
rs = con.query('SELECT value FROM test WHERE id >= %s LIMIT 10;', (r,))
for j in rs.rows:
sum += j[0]
httpfile['Content-Type'] = 'text/plain'
httpfile.write('%6f' % sum)
httpfile.close()
httpd = httpserver(('', 8080), handler)
httpd.start()
mainloop()
结果为145 QPS,无失败请求。此时重测了一下Tornado,为149 QPS。<?php
$sum = 0;
$db = new mysqli('p:127.0.0.1', 'root', '123', 'test'); // p:是使用持久连接,因为没法像Python那样重用连接
for ($i = 0; $i < 10; ++$i) {
$r = rand(1, 1000000 - 10);
$result = $db->query("SELECT value FROM test WHERE id >= $r LIMIT 10;");
while ($row = $result->fetch_row()) {
$sum += $row[0];
}
$result->close();
}
printf('%6f', $sum);
?>
启动占用5.3MB内存,测试完占用5.3MB。<?php
$sum = 0;
$db = new mysqli('p:127.0.0.1', 'root', '123', 'test');
$stmt = $db->prepare('SELECT value FROM test WHERE id >= ? LIMIT 10;');
for ($i = 0; $i < 10; ++$i) {
$r = rand(1, 1000000 - 10);
$stmt->bind_param('i', $r);
$stmt->execute();
$stmt->bind_result($value);
while ($stmt->fetch()) {
$sum += $value;
}
}
$stmt->close();
printf('%6f', $sum);
?>
结果为155 QPS,无失败请求,性能一跃成为最好的了。