转载自[http://bigtoy4boy.com/blog/2009/05/performance-tuning-of-nignx-and-twisted/]
最近这两天一直在测试自己用python+twisted写的一个http服务器的效率,几度从心理崩溃的边缘闯了过来,残喘留下点墨迹,警醒后来者。
这个服务程序没有什么业务逻辑,就是负责保持一大堆连接,等待服务器状态被更改了以后就通知所有的保持连接,返回一个信号值。如何维持这么多连接是这个应用的关键。之前选择了几种方案,感觉太消耗服务器资源,动不动就内存吃光了,又懒得用C从头写一个服务程序(太耗时间没有必要),所以选择了python的一个socket框架twisted。
这个程序一开始用ab(Apache Benchmark)简单的压,没有发现什么问题,服务器内存和cpu占用率都不高,所以没在意就直接上线运行了,实际运行时好在流量不是太大,所以没有出什么问题,但实际监控到的数据是400左右的保持连接就要占用40%的cpu,这个数值有点高。如果这样计算的话,一台机器没有办法承载多少连接数。于是在测试环境开始第一次压力测试,希望能重现实际运营时的那个种状态。
还是ab,并发500请求我的服务程序,发现有很多连接并不是预期时间返回的(每个连接最少保持25秒),很多连接是超出了很长时间才返回。调试程序,在子线程的部分输出一些调试信息,发现同时只有少数线程可以被执行,而大部分的线程都挂起了,印象中twisted有线程池的概念,找到了一个这样的参数 reactor.suggestThreadPoolSize(),默认值是10,也就是同时只有10个线程可以被处理,将这个值改大到我希望的并发数值以上,问题消失了。这个修改有用!
继续ab,并发到1000,作为代理服务器的nginx报错,它停止响应请求了,这是为什么呢?nginx默认配置下是和系统下ulimit nofile数值相关的,突破的方法可以在nginx.conf配置文件中加一句话
worker_rlimit_nofile 51200;
生效了,为此我犯懒没有修改系统默认的ulimit -n的值,现在还是默认1024。
还是ab,并发到2000,总是感觉ab并没有达到这样的并发数量,而是一点点增加连接数,另外服务器端cpu占用率也没有实测的那次那种很高的占用率,为了更真实的模拟,我用python写了一个模拟并发的程序代替ab
同时通过查询资料确认twisted默认的reactor是selec()方法,该方法目前限定只能同时处理1024个请求,突破这种方法需要切换到epoll模式,在程序开头写如下的代码,激活epoll的reactor
from twisted.internet import epollreactor
epollreactor.install()
自己写的压力测试脚本继续测,1000貌似没有问题,2000出现了问题,因为我的测试脚本是1千1千的往上加,加到2000的时候,服务基本停止响应了,有大量的连接在等待,服务器出现很多请求超时。什么原因呢?是不是ulimit的问题呢?很有可能,因为启动服务程序的用户ulimit -n才1024,只允许打开1024个文件句柄,nginx也许有其它的招数来绕过这个限制,毕竟它用root用户启动的,想干什么不行呀,但我这个程序是普通用户,我的程序包括twisted也不会像nginx这么优秀,自己什么都搞定了,所以要修改我这个用户默认的nofile值,加大!修改/etc/security/limits.conf文件,增加对应用户nofile的值,我设定的是65535,六万多的并发足够了。重新登陆用户重启服务程序后,缓慢的问题消失了,但新的问题又出现了!
超过1000多并发后我写的程序主动停止响应,查询对应进程的/proc/xxx/fd目录下的句柄数,锁定在1000出头的一个数字,直接telnet服务端口,输入任何内容就主动断开连接了。怀疑twisted框架是不是有那个参数没有设置好,类似SuggestThreadPoolSize的问题呢?寻找,一直在寻找,都找到twisted源代码里面去了,无果。貌似大家都没有这个问题。
给twisted社区的邮件列表发信,询问是否有限制或其它问题,等待之余怀疑的心态重新编译了一个新的python版本。原来的是2.4.3,更新到2.5.4后居然问题消失了!不禁问自己,这是为什么呢?
很快得到了社区大侠的回复,说这是python的bug,不巧就是2.4.3这个版本有bug,2.4.2没有,2.4.4也没有,我真应该去买彩票了。
一切都过去了,目前测试已经超过了4000保持连接,服务器状态良好,内存、cpu占用率都不高,之前实测cpu占用率高也许是thread pool设定问题以及select()模式的综合体现,目前都消失了,基本上维持20%的cpu占用率,python也是脚本语言,你也不能太苛刻的要求它嘛。
总结:
- 性能是压出来的,怀疑一切,尽情的压,总能找到瓶颈点,总能解决并提高
- 社区力量很好,但不能什么事情都不走脑子就往上面问,详细描述问题会带来非常准确的回答
- 崩溃边缘可以喘喘气,换换心情,换个角度也许问题就能解决了,切忌钻牛角尖