Mongrel导致Swap抖动
原文地址:http://www.blogkid.net/archives/2390.html
51放假我没能消停,放在Linode(Linode服务介绍)的服务器出状况了。看起来负载太高(load average 长期高于3),观察发现CPU频繁处于IOWAIT状态。而在Linode后台的统计图显示,IORate持续高于3k。要知道,Linode默认IORate高于300时就会发邮件报警。
开始我还没意识到问题的严重性。直到去查询服务器上Rails应用的日志,发现处理一个请求最长居然要70秒(!)——平时只要300ms左右。一下懵了,赶紧查问题。iostat显示对swap的读写非常多,同一时刻等待io阻塞的进程达20个。考虑到服务器不只跑着 nginx/mongrel/mysql还跑着一个sphinx,我改了一下程序,减少了对sphinx的查询。情况有所好转,但依然不稳定:IORate会突然冲高,之后回落。
只得在Linode发一个support ticket,描述情况后,那边的技术支持向我要free -m的输出和vmstat 的输出。提交了输出后,那边邮件回复,咬定我的服务器发生了swap抖动。全文如下:
You are quite a bit into swap, and it appears your Linode is swap thrashing. You’re either going to want to tweak the app(s) that are consuming RAM or upgrade to a larger Linode plan. As a general rule, we don’t recommend swap sizes larger than 256MB.
我的RAM有540M,设置了1G的Swap。从我些许经验来看,应对每天3w左右的请求(Rails处理的结果为200的动态请求有1.2w),540M内存怎么也够了。所以犯不着升级服务器。新难道是Swap太大?按说内存的2倍也不该是问题。我尝试着把Swap设置到300M,重启服务器,暂时稳定了一会,又开始“抖”。一咬牙,设置为256M,再次重启,居然稳定了。Linode的技术支持很赞,据说还支持中文。
我重新在程序里开了对sphinx的查询,io情况没有明显变化。
今天是54,上午可以休息。早上起来忍不住看看服务器,居然又在“抖”了。通过查看后台统计图,发现从凌晨一点左右就开始不正常,而这时应该是访问最少服务器压力最小的时候(当然,不排除爬虫来得比较猛烈)。此时的Rails应用,处理一个请求大概需要10秒。我很快想到mongrel的锁机制可能有问题。Rails本身不是线程安全的,于是Mongrel在处理请求时,在调用Rails的分发器之前就加了锁,直到Rails处理完这个请求。这个锁的粒度很粗。
引用Shining Ray的结论:假设一个Mongrel当前的请求被阻塞在Rails的代码中(比如一个较长的查询),后续的请求就会被阻塞,假如阻塞的时间足够长,导致队列中请求满了,那么接下来就是出现大量时间花费在上下文切换和锁的争用上(来自《浅析Ruby On Rails部署方案》)。之前在用apache+mongrel的时候,都会把并发数量设为1,这样可以规避掉锁的问题;但换用nginx以后,并未做相应设置。引起Swap抖动的元凶,原来是Mongrel那只粗大的锁。
想到这茬,我马上停掉mongrel换用了基于EventMachine的thin。IO瞬间降下来了,请看图片:
Mongrel的作者已经不再维护Mongrel,这次我遇到的问题,可以算是Mongrel的硬伤。
参考资料:http://docs.google.com/Doc?id=ddcvzh74_28f9xppqfh



1 Response to “Mongrel导致Swap抖动”
[...] 好多好多上线时,人多,缓存也还未生成,服务器一下顶不住了。让我下决心把我的Linode升级到一个更高的配置。有心的朋友们会问,难道Linode服务器如此脆弱不堪,开新网站就会挂掉?非也,非也。其实是因为这台540M内存的Linode上,跑着的服务太多:mysql、memcached、sphinx、nginx以及5个php-cgi进程和8个thin服务器。因此540M内存是怎么也嫌少的,一旦一个请求处理慢了,就很容易使CPU长时间处在上下文切换上,甚至引起Swap抖动。而只要把内存增加一点,情况就会大为好转。 [...]