Gevent: 优点,缺点,以及不优美的地方

标签: gevent 美的 地方 | 发表时间:2011-06-10 15:05 | 作者:(author unknown) jin
出处:http://simple-is-better.com/

我不想用很多时间去描述Gevent是什么,我想它官网上的一句总结足矣:

“Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。”

接下来我将阐述在Mixpanel中一个内部项目使用Gevent的经验。 为了这篇文章我还动手写了几个性能小测试。(Whipped up这里的意思让我迷惑哎- -)

优点

首先Gevent最明显的特征就是它惊人的性能,尤其是当与传统线程解决方案对比的时候。

在这一点上,当负载超过一定程度的时候,异步I/O的性能会大大的优于基于独立线程的同步I/O这几乎是常识了。

同时Gevent提供了看上去非常像传统的基于线程模型编程的接口,但是在隐藏在下面做的是异步I/O。

更妙的是,它使得这一切透明。(此处意思是你可以不用关心其如何实现,Gevent会自动帮你转换)

你可以继续使用这些普通的Python模块,比如用urllib2去处理HTTP请求,它会用Gevent替换那些普通的阻塞的Socket操作。

当然也有一些需要注意的问题,我稍后会阐述。

接下来,见证性能奇迹的时候到了。

从上图看出

忽略其他因素,Gevent性能是线程方案的4倍左右(在这个测试中对比的是Paste,译者注:这是Python另一个基于线程的网络库)

在线程方案中,错误数随着并发连接数的增长线性上升(这些错误都是超时,我完全可以增加超时限制,但是从用户的角度来看漫长的等待和失败其实是一个妈生的)。

Gevent则是直到10,000个并发连接的时候都没有任何错误,或者说能处理至少5,000并发连接。

在这2种情况下,每秒实际完成的请求数都非常的稳定,至少直到Gevent在10,000个并发连接测试崩溃之前是如此。这一点让我感到非常的惊讶。我原本猜想的是RPS(每秒完成请求数)会随着并发的增多而下降。

线程模型在10,000并发连接测试中完全失败。我完全可以让它正常的工作(比如用一些资源优化技巧应该能做到),不过我纯粹是出于玩蛋来做这个测试的,所以没有在这上面花太多功夫。

如果这类东西能勾引起你的兴趣,我们正在招聘,你懂的。(没错,我就在内容里面混点广告宣传下。)

测试方法

这是我用来测试的Python代码:

#!/usr/bin/env python 
 
import sys 
 
def serve_page(env, start_response): 
    paragraph = '''
        Lorem ipsum dolor sit amet,
        consectetur adipisicing elit,
        sed do eiusmod tempor incididunt ut labore et
        dolore magna aliqua. Ut enim adminim veniam,
        quis nostrud exercitation ullamco laboris nisi ut aliquip
        ex ea commodo consequat.
        Duis aute irure dolor in reprehenderit in
        voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        Excepteur sint occaecat cupidatat non proident,
        sunt in culpa qui officia deserunt mollit anim id est laborum.
    ''' 
    page = '''
        \<html\>
            \<head\>
                \<title\>Static Page\</title\>
            \</head\>
            \<body\>
                \
<h1\>Static Content\</h1\>
                %s
            \</body\>
        \</html\>
    ''' % (paragraph * 10,) 
 
    start_response('200 OK', [('Content-Type', 'text/html')]) 
    return [page] 
 
if __name__ == '__main__': 
    def usage(): 
        print 'usage:', sys.argv[0], 'gevent|threaded CONCURRENCY' 
        sys.exit(1) 
 
    if len(sys.argv) != 3 
        or sys.argv[1] not in ['gevent', 'threaded']: 
        usage() 
 
    try: 
        concurrency = int(sys.argv[2]) 
    except ValueError: 
        usage() 
 
    if sys.argv[1] == 'gevent': 
        from gevent import wsgi 
        wsgi.WSGIServer( 
            ('127.0.0.1', 10001), 
            serve_page, 
            log=None, 
            spawn=concurrency 
        ).serve_forever() 
    else: 
        from paste import httpserver 
        httpserver.serve( 
            serve_page, 
            host='127.0.0.1', 
            port='10001', 
            use_threadpool=True, 
            threadpool_workers=concurrency 
        )  

在客户端,我在下面这些参数下使用Apache Bench:

  • -c NUM: 这里的NUM是并发连接数。在每一个测试中这个数与服务器命令行中使用的那个数是匹配的。(译者注:这里指脚本运行时需要提供的第二个参数)
  • -n 100000: 所有的测试都需要完成100,000个请求。在上面的图中,错误率并没有统计,而是实际100,000请求中失败的请求数。
  • -r: 如果请求失败,自动重试

所有的测试包括服务端和客户端都是运行在一个低配置并且只有512MB内存的VPS上。

我最初以为我需要用一些方法来限制线程方案到一个CPU上,但事实证明就算这VPS号称是“四核”,你也就只能让一个核心到100%。就是这么蛋疼。

负载测试中所做的Linux优化

  • 当Linux处理超过500连接每秒的时候我遇到了一大堆的问题。
    基本上这些问题都是因为所有连接都是从一个IP到另一个相同的IP(127.0.0.1 <-> 127.0.0.1)。
    换句话说,你可能在生产环境中不会遇到这些问题,但几乎可以肯定的是这些问题一定会出现在测试环境中(除非你在后端跑一个单向代理)。
  • 增加客户端端口范围

    echo -e ’1024\t65535′ | sudo tee /proc/sys/net/ipv4/ip_local_port_range

    这一步将会使得客户端的连接有更多的可用的端口。没有这个的话你会很快的用尽所有端口(然后连接就处于TIME_WAIT状态)。

  • 启用TIME_WAIT复用

    echo 1 | sudo tee /proc/sys/net/ipv4/tcp_tw_recycle

    这也会优化停留在TIME_WAIT的连接,当然这种优化至少需要每秒含有同样IP对连接超过一定数量的时候才会起作用。同时另一个叫做tcp_tw_reuse的参数也能起到同样的作用,但我不需要用到它。

  • 关闭同步标签

    echo 1 | sudo tee /proc/sys/net/ipv4/tcp_syncookies

    当你看到”possible SYN flooding on port 10001. Sending cookies.”这种信息的时候,你可能需要关闭同步标签(tcp_syncookies)。在你生产环境的服务器上不要做这样的事情,这样做会导致连接重置,只是测试的话还是没问题的。

  • 如果用到了连接追踪,关闭iptables

    你将会很快的填满你netfiler表。当然咯,你可以尝试增加/proc/sys/net/netfilter/nf_conntrack_max中的数值,但是我想最简单的还是在测试的时候关闭防火墙更好吧。

  • 提高文件描述符限制

    至少在Ubuntu上,默认每一个普通用户的文件描述符限制数是4096。所以咯,如果你想测试超过4000并发连接的时候,你需要调高这个数值。最简单的方法就是你测试之前在/etc/security/limits.conf中增加一行类似于”* hard nofile 16384″的东西,然后运行ulimit -n 16384这条shell命令。

缺点

当然所有的事情不会这么好对吧?没错。事实上,如果有更完整的文档的话,很多我在用Gevent的问题会被解决得更好。(译者对于这类句子毫无抵抗力,凑合着看吧╮(╯_╰)╭)

文档

简单的说,这货一般般。我大概读了比文档更多的Gevent源码(这样很有用!)。事实上最好的文档就是源码目录下的那些示例代码。如果你有问题,认真的瞄瞄看它们先。同时我也花了很多时间用Google去搜索邮件列表的存单。

兼容性

这里我特别想提到eventlet。回想起来,这是有一定道理的,它会导致一些匪夷所思的故障。我们用了一些eventlet在MongoDB客户端(译者注:一种高性能文档型数据库)代码上。当我使用Gevent的时候,它根本不能在服务器上运行。

呃,使用顺序错误

在你导入Gevent或者说至少在你调用Monkey.path_all()之前启动监听进程。我不知道为什么,但这是我从邮件列表中学到的,另一点则是Gevent修改了Python内部Socket的实现。当你启动一个监听进程,所有已经打开的文件描述符会被关闭,因此在子进程中,Socket会以未修改过的形式重新创建出来,当然啦,这就会运行异常。Gevent需要处理这类的异常,或者说至少提供一个兼容的守护进程函数。

Monkey Pathing,抽风咩?

这么说吧,当你执行monkey.path_all()的时候,很多操作会被打上补丁修改掉。我不是很好这口,但是这样使得普通Python模块能够很好的继续运行下去。奇怪的是,这丫的不是所有的东西都打上了这种补丁。我瞄了很久想去找出为毛的Signals模块不能运行,直到我发现是Gevent.signal的问题。如果你想给函数打补丁,为毛的不全部打上咩?

这问题同样适用于Gevent.queue与标准的Python queue模块。总之,当你需要用到Gevent特定的API去替换标准模块/类/函数的时候,它需要更清晰(就像简单的list一样)。

不优美的地方

Gevent不能支持多进程。这是比其他问题更加蛋疼的部署问题, 这意味着如果你要完全用到多核,你需要在多个端口上运行多个监听进程。然后捏,你可能需要运行类似于Nginx的东西去在这些服务监听进程中分发请求(如果你服务需要处理HTTP请求的话)。

说真的,多进程能力的缺乏意味着为了使用中可用你又要在服务器上多加上一层东西。(译者注:真蛋疼的句子,不就是多一层Nginx或者HA么)

在使用Gevent客户端负载测试中,这真是一个大问题。我最终是实现了一个使用共享内存的多进程负载的客户端去统计以及打印状态。这需花费更多的工作量在上面。(如果有人需要做同样的事情,联系我,我会给你这个客户端脚本程序)

最后

如果你已经看到这里,你会发现我用了2个章节去阐述Gevent的缺点。不要被这些东西蒙蔽了你的眼睛。我相信Gevent对于Python网络编程来说是一种很伟大的解决方案。诚然它有各种各样的问题,但是大多数问题也仅仅是文档的缺失罢了,撑死了多用点时间嘛。

我们内部正在使用Gevent。事实上我们的服务非常的高效,以至于在我们所用的那种规格的VPS上,很容易在用光计算资源(包括CPU和内存)之前用光带宽资源。

# 这是一篇翻译的文章,原文见http://code.mixpanel.com/gevent-the-good-the-bad-the-ugly/

# 原文中由于有大量中英对照,考虑到适合阅读性,去掉了原文部分。

# 作者:In the Milky way

# 来源:In the Milky way


在微博上关注: 新浪, 腾讯   投稿

最新招聘

更多>>

相关 [gevent 美的 地方] 推荐:

Gevent: 优点,缺点,以及不优美的地方

- jin - python.cn(jobs, news)
我不想用很多时间去描述Gevent是什么,我想它官网上的一句总结足矣:. “Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API. 接下来我将阐述在Mixpanel中一个内部项目使用Gevent的经验. 为了这篇文章我还动手写了几个性能小测试.

python: gevent轻松实现并行下载多个文件

- frank28_nfls - 阿福的技术BLOG
利用协程,在一个函数中从头写到尾就能实现并行,实在是太爽了. 相比之下,twisted中眼花缭乱的回调,学习曲线实在是太陡峭了.

Flask, Tornado, GEvent, 以及它们的结合的性能比较

- Ken - python.cn(jobs, news)
英文: http://blog.wensheng.com/2011/10/performance-of-flask-tornado-gevent-and.html. 我在选一个python的互联网框架, 本来已经定下来用Tornado了.  但我还听到很多人推荐Flask的简单性和灵活性, 还有gevent的高性能, 所以决定也试试它们以及它们和Tornado的结合.

国内风景最美的地方? - 知乎

- -
中国绝对有不比瑞士差的山水风景. 要去的朋友,如果是周末抽两三天,到了成都可以报团,飞猪美团携程都有,自然到茶店子就会来接你,不要报价格最低的 ,找个稍微贵一点的有好的旅游体验. 如果是自驾,到了成都往兰州开,或者反过来也行. 若尔盖大草原,阿坝大草原,川西雪山和森林,比如毕棚沟,海螺沟,四姑娘山,松坪沟,达古冰山奶子沟之类,人都很少,到四川的都去峨眉山,乐山大佛和九寨沟了,最多加上一个稻城,稻城现在人也多了,其实这些人少的地方现在恰恰是最值得去的地方.

离幸福不远的地方

- 秋叶濛濛 - A Geek&#39;s Page
离幸福不远的地方,我想就是这儿了. 它有一个很好听的名字,叫风花雪月……. 大约两年前,我来过一次云南,走的时候我就说我还会回来. 印象中的云南是松坡将军驻守过的云南,是小凤仙那句 “萍水相逢成一梦”. 印象中的云南是王小波来过的云南,是他“常常夜里爬起来,借着月光用蓝墨水笔在一面镜子上写呀写,写了涂,涂了写,直到整面镜子变成蓝色.

在小地方做大事情

- xf - 左岸读书_blog
注:这是一种选择,无关对错,存乎一心. 要毕业了,你在网上发帖:“留在北京还是去二线城市. “北京”切换成“上海”、“深圳”、“广州”也同样成立. 这是时代给我们的不大不小不轻不重的命题. 回答千千万,让我们看几个经典的. “北方人只适合北方,北方过得去的城市只有北京. “文化氛围好,亚洲第一的图书馆就在这里,随时可以附庸风雅.

这是小JJ......进去的地方

- 夜の猫 - 新闻跟帖局
美国《连线》杂志报道,本周,500多名“速度狂人”齐聚美国犹他州盐湖城西部的巴纳维亚盐滩赛道,参加久负盛名的竞速比赛“巴纳维亚速度周”. 巴纳维亚速度周是最纯粹的赛车比赛,选手只需以所能达到的最快速度驾驶赛车从地点A飞奔到地点B即可. 关注关注他的微博fu3972 [网易江苏省苏州市网友]:2011-08-18 09:12:17 发表网易山东省日照市网友 [rzguoge] 的原贴:1.

19有什么特别的地方?

- fuckgfw - Matrix67: My Blog
    今天在网上看到一个神奇的东西:把从 1/19 到 18/19 的所有分数展开成小数,得到一个 18 × 18 的数字方阵. 这个数字方阵有什么特别的地方呢.     答案是,它是一个幻方——每一行、每一列和两条对角线上的数字之和都是 81 (注:严格意义上说它不算幻方,因为有相同数字).     当然,相信大家像我一样,看到上图之后第一件事情就是验证把 19 换成 7 能否得到一个 6 阶幻方.

那个地方,每个人都有

- 白肥 - 牟春光
早上五点半起床,天还没亮,看窗外,三元桥上车辆稀少. 煮了咖啡喝,早上又恢复喝咖啡了. 本来开始写字,却看了纪录片《皮克斯的故事》. 不得不说,大早上看完这个,非常美好. 沃尔特·迪斯尼说:“我们每拍一部电影,都不去考虑观众是成人还是孩子,我们考虑的是人内心那个干净、柔软的地方,每个人内心都有这么一个地方,世俗生活经常将那里遮盖住,我们的电影就是要让人重新感受到这个地方.

计算 一个点 附近的地方

- - 码蜂笔记
随着地理位置的应用普及,越来越多类似计算 一个点 附近的酒店的需求. 比如,显示当前位置2000米范围内的 7 天酒店. 一般来说,在系统里都有一张表,存储了每个 7 天酒店的位置信息,表结构可以简化为三个字段. tb_location(ID, lng, lat),其中 lng:表示经度;lat:表示纬度,这两个字段都是数值类型的,且建了索引.