初试Gevent – 高性能的Python并发框架

Gevent是一个基于greenlet的Python的并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。

于greenlet、eventlet相比,性能略低,但是它封装的API非常完善,最赞的是提供了一个monkey类,可以将现有基于Python线程直接转化为greenlet,相当于proxy了一下(打了patch)。

今天有空就迫不及待的试一下效果。

1、安装

Gevent依赖libevent和greenlet,需要分别安装。

#libevent 1.4.x
sudo apt-get install libevent-dev

#python_dev
sudo apt-get install python-dev

#easy_install
wget -q http://peak.telecommunity.com/dist/ez_setup.py
sudo python ./ez_setup.py

#greenlet
wget http://pypi.python.org/packages/source/g/greenlet/greenlet-0.3.1.tar.gz#md5=8d75d7f3f659e915e286e1b0fa0e1c4d
tar -xzvf greenlet-0.3.1.tar.gz
cd greenlet-0.3.1/
sudo python setup.py install

#gevent
wget http://pypi.python.org/packages/source/g/gevent/gevent-0.13.6.tar.gz#md5=7c836ce2315d44ba0af6134efbcd38c9
tar -xzvf gevent-0.13.6.tar.gz
cd gevent-0.13.6/
sudo python setup.py install

至此,安装完毕。

2、测试代码:XML-RPC

这里必须使用支持线程的XML-RPC,否则无法发挥gevent的优势!

传统版本:
需要说明的是,这个并很多资料描述的非单线程,而是一个select版本,所以某些时候比线程版本性能好。

from SocketServer import ThreadingMixIn
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler

from SocketServer import TCPServer

TCPServer.request_queue_size = 10000

#Logic function
def add(a, b):
    return a + b

#Logic function 2
def gen(n):
    return '0' * n

#create server
server = SimpleXMLRPCServer(('', 8080), SimpleXMLRPCRequestHandler,False)
server.register_function(add, "add")
server.register_function(gen, "gen")
server.serve_forever()

线程版本:

from SocketServer import ThreadingMixIn
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler

#Threaded XML-RPC
class TXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer): pass

#Logic function
def add(a, b):
    return a + b

#Logic function 2
def gen(n):
    return "0" * n

#create server
server = TXMLRPCServer(('', 8080), SimpleXMLRPCRequestHandler)
server.register_function(add, "add")
server.register_function(gen, "gen")
server.serve_forever()

3、测试客户端

from xmlrpclib import ServerProxy

#Execute RPC
server = ServerProxy("http://localhost:8080")
#print server.add(3,5)
print server.gen(2048)

4、gevent的monkey包装后的XML-RPC

monkey是非入侵式的patch,只需要显示调用你需要patch的东西就行了,别看我用了三行,其实可以patch_all()的

from SocketServer import ThreadingMixIn
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
from gevent import monkey

#Threaded XML-RPC && Monkey Patch
monkey.patch_socket() #Just 2 line!
monkey.patch_thread() #Just 3 line!
monkey.patch_select() #Just 3 line!
class TXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer): pass

#Logic function
def add(a, b):
    return a + b

#Logic function 2
def gen(n):
    return "0" * n

#create server
server = TXMLRPCServer(('', 8080), SimpleXMLRPCRequestHandler)
server.register_function(add, "add")
server.register_function(gen, "gen")
server.serve_forever()

5、测试结果

现在只有一台机器,下午去实验室两台机器跑了以后,放上结果。对gevent还是比较寄希望的,希望不要太差。。

客户端的特殊配置:
echo -e '1024\t65535' | sudo tee /proc/sys/net/ipv4/ip_local_port_range
echo 1 | sudo tee /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 | sudo tee /proc/sys/net/ipv4/tcp_syncookies
ulimit -n 10240

服务器端的特殊配置:
echo “10152 65535″ > /proc/sys/net/ipv4/ip_local_port_range
echo 1 | sudo tee /proc/sys/net/ipv4/tcp_tw_recycle
sysctl -w fs.file-max=128000
sysctl -w net.ipv4.tcp_keepalive_time=300
sysctl -w net.core.somaxconn=250000
sysctl -w net.ipv4.tcp_max_syn_backlog=2500
sysctl -w net.core.netdev_max_backlog=2500
ulimit -n 10240

然后说让大家比较失望的结果:测试效果非常失败,经常出现异常情况,根据我的分析是默认的XML-RPC没有backlog(或者默认太低),导致压力一大,就会fail accept,从而导致RESET(connection refused)。
所以说对monkey的patch不要抱太大希望,他是和原代码密切相关的。

补充:已经找到修改默认backlog的方法,如下:

from SocketServer import TCPServer
#修改这个全局变量即可
TCPServer.request_queue_size = 5000

当然测试数据说明,不要过分迷恋monkey,那只是个传说~

测试数据:
c=500 n=50000
默认:2845/s, 8M
多线程:1966/s, 51M
gevent:1888/s, 11M

c=1000 n=100000
默认:3096/s, 8M
多线程:1895/s, 52M
gevent:1936/s, 11M

c=5000 n=500000
默认:3009/s, 8M
多线程:失败,无法创建新线程
gevent:1988/s, 11M

c=10000 n=1000000
默认:2883/s, 8M
多线程:失败,无法创建新线程
gevent:1992/s, 20M

monkey的优点就是:省内存,我是和线程的相比。
我仔细的分析了一下,XML-RPC使用CPU的比例还是很大的,相比较于直接http的计算,xmlrpc还是属于cpu密集型。
在这种CPU占用很高,需要反复争夺微greenlet的情况下,gevent并不具有优势。
或者从另一种角度说,测试机不够强大,喂不饱gevent(可以看到,随着并发线程升高,gevent的性能不降反升,而默认的则在不断下降)

6 thoughts on “初试Gevent – 高性能的Python并发框架

  1. 杨晓伟

    作为新手一枚,看了您的大作,有几点疑问。

    测试数据中的各项指标c ,n与 20M具体指什么内容。
    还有如何同时开启这么多客户端呢?
    如何获得这些测试指标呢?

    Reply
  2. coder4 Post author

    c:并发线程数
    n:总测试数量
    20MB:内存占用,top之后RES一项

    Reply
  3. ruifengyun

    我这边也大量的在用gevent,特别适合做涉及到io的并发。 我现在一些服务器端的开发,转到golang了,你可以试试,真心不错 呵呵

    Reply
  4. mz

    需要解析XML本身就属于CPU bound了

    Gevent只是单线程,适合IO bound场景,自然是性能不行了

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *