浅析Ruby on Rails部署方案(三)

阅读更多
测试结果
HAproxy和Lighttpd的测试需要调整很多参数,我们先看看无需调整参数的几种方案的测试结果,我同样除了原始数据的折线图,还会给出B样条的图以更清晰地看到结果:

从图上我们看到Nginx+Mongrel的方式性能很稳定,但在并发量小于150的情况下与其他方案相比还是差很多的。
Nginx+Thin/Evented Mongrel两种方式的性能则相差不大,毕竟都是使用的EventMachine。
Swiftiply的两种方式都体现出了很高的性能,尤其是Swiftiply+Swiftiplied Mongrel令人影响深刻,即使在500的并发量左右,性能依然保持稳定。
Apache+Passenger模式在低并发(<=300)的情况下效率很高,但随着并发量上升,Apache的两种工作模式本身的效率下降得就很快,Prefork模式在大于450的情况下降到了0(我认为是出现了TCP错误导致AB测试中断)。
这个测试中,除了Nginx,其他的搭配方式Swiftiply和Passenger,应该每次都只给后端一个请求,同时也应该是建立的持久链接。
HAproxy/Mongrel的测试结果

通过对HAproxy的maxconn参数的调整,得到了如上图的测试结果,我们不难发现在maxconn=1的情况下是效率最高的,这印证了前面对后端缺点的分析。因为在限制了每次只有一个链接之后,Mongrel的进程减少了锁和上下文切换的开销,所以得到了很稳定的性能,另外我们还可以注意到,随着maxconn越来越大,最后达到50的时候,这时候Mongrel进程中对于锁和上下文切换的开销也趋于平稳,情况变成了和 Nginx/Mongrel的情况一样。
通过对比,我们可以发现,利用HAproxy限制到Mongrel的链接数为1,可以比Nginx+Mongrel的方式性能提高23%~28%,不过相比Passenger/Swiftiply方式而言,还是差一点点,但性能依然十分稳定。
这也是为什么ThoughtWorks的Rubyworks采用了HAproxy的缘故,我觉得在Rubyworks推出的当时是非常正确的选择,因为当时没有其他选择。

HAproxy/Thin的测试结果

由于Thin没有锁和上下文切换的开销,因此限制maxconn在测试结果上不如对Mongrel的效果那么明显。但基本上也可以看出来,将链接到后端的连接数限制在较小的范围内会对性能和稳定性有更好的帮助。
Lighttpd/Mongrel的测试结果

Lighttpd+Mongrel的图像与HAproxy+Mongrel的非常不同,尤其是在限制到后端的链接效果上。我在测试结果中发现了大量的504Gatewaytimeout错误返回,从而导致了响应速度的降低。随着链接限制参数的变大,504错误越来越少。我们可以发现在并发量小于 100的时候,其效果还是和HAproxy类似的。当链接限制大到一定程度,其效果和Nginx+Mongrel的方式也是一样的。
Lighttpd/Thin的测试结果

Lighttpd搭配Thin的情况同样出现了Mongrel一样的情况,在max-pool-size参数为1的时候出现了大量的504错误返回,并随着这个参数的增加错误数减少。
Lighttpd+Thin的方式在不限制连接数的情况下性能要大大优于Nginx+Thin和HAproxy+Thin的方式。
Lighttpd/FastCGI的测试结果
通过Socket方式

通过TCP方式

我们可以发现Lighttpd的FastCGI方式走TCP或Socket在性能方面没有太大差别,但限制链接数容易造成Lighttpd直接给出错误返回,随着连接数限制的放宽,错误数也减少,响应速度也变得稳定。
根据前面对后端的测试,无论何种后端都应该在低链接的情况下有更好的表现,Lighttpd为何在限制了连接数之后反而有更过504错误呢,我认为是 Lighttpd未实现像Apache和HAproxy的双重链接池的功能,同时Lighttpd的默认的超时时间又只有10秒钟。在Apache和 HAproxy的情况中,当链接未进入某个后端服务器的等待队列时,会等待Timeout时间,而进入了后端等待队列之后会重新等待TTL的时间,这样就避免了快达到Timeout的时候进入等待队列没多久就被放弃的情况。当然Apache和HAproxy的Timeout和TTL参数也需要根据系统的性能和要求小心调整。而对于Lighttpd则可以考虑适当增大Timeout时间。

分析和小结
我先来将本案中的各种部署方式的性能排个名次,以下是后各种方案在并发量>=10的情况下的平均值(去掉出现0值的情况):

前端
后端
平均每秒响应数
最大链接限制

Lighttpd
FastCGI/TCP
215.33
64

Lighttpd
FastCGI/Socket
214.65
64

Lighttpd
Thin
196.16
64

Swiftiply
Swiftiplied Mongrel
191.33
N/A

HAproxy
Thin
188.73
10

Swiftiply
Thin(Swiftiplied)
178.96
N/A

Apache2.2/Prefork
Passenger
173.02
N/A

HAproxy
Mongrel
170.4
1

Apache2.2/Worker
Passenger
163.9
N/A

Lighttpd
Mongrel
149.85
64

Nginx
Evented Mongrel
149.78
N/A

Nginx
Thin
143.88
N/A

Nginx
Mongrel
138.86
N/A

在这个表中,Lighttpd的三种方案占据了前三位,Lighttpd+FastCGI是性能最高的部署方式。这种方式比另一种流行方案Nginx+Mongrel的方式性能提升了高达50%!
FastCGI的好处在此体现出来:

二进制协议,无需HTTP的解析
与前端可以建立持久链接
没有锁和上下文切换的开销

另外Lighttpd相对于Nginx的优势在于请求和响应的接收缓冲区很大,省去多次接收和发送的开销。
Lighttpd+Thin的方式的性能列第三位,这点似乎出乎意料之外,但实际上是因为Lighttpd 1.5支持对HTTP后端建立HTTP KeepAlive链接。在对后端单独的测试中,小并发下的Thin的KeepAlive测试性能并不比FastCGI差,同时Thin实现了非阻塞 IO,而FastCGI则是阻塞式的。相反,HAproxy和Nginx则都不支持HTTP KeepAlive。
而Swiftiply的方式也显示出了强劲的性能,应该是得益于它的“让后端主动连接到Swiftiply”的这种特殊的结构。
当前备受关注的Passenger的部署方式在本案中并没有显示出特别的性能上的优势,不过如果将并发链接数放在300以内,则 Apache2.2/Prefork + Passenger的部署方式的平均每秒响应数上升为204.03,这样看来,倘若为Apache进行一些优化配置,依然不失为一种高效的部署方案。而同时Passenger又是最容易配置的一种方案,能达到这种效果已经非常令人满意。
HAproxy + Mongrel并限制链接数为1,则是一种稳定、保守的部署方式,虽然在这里性能不出众,但是稳定性非常好。
最后,与Nginx相关的三种方案都排在了该榜的末尾,由于Nginx的反向代理负载均衡缺少一些高级的特性以及Rails本身的特性而导致其不适合单独应用在Rails程序的部署上:

缺少到后台服务器端的链接数限制的能力,这导致了Mongrel在接受大量请求时将时间消耗在上下文切换和锁的争用上。
缺乏建立到后台服务器端的持久链接的能力,这导致了在链接的打开、建立、关闭上花费了额外的开销。

延伸
Windows操作系统
由于*nix操作系统中,可以替代Apache的选择很多,而Windows下基本上只能使用Apache2.2+Mongrel的方式了,想使用Thin的朋友要记得单独安装EventMachine 0.8.1 ,目前最新的Windows安装包。
我给出一个比较合理的Apache2.2的配置,最简化的配置文件如下:

ThreadsPerChild 1024
MaxRequestsPerChild 0

ServerRoot "C:/Apache2"
Listen 80
KeepAlive off
KeepAliveTimeout 15
Timeout 30
MaxKeepAliveRequests 1024

LoadModule log_config_module modules/mod_log_config.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule status_module modules/mod_status.so

ServerAdmin [email protected]
ServerName shiningray.cn
DocumentRoot "D:/wwwroot/rails/public"
ErrorLog logs/error.log
LogLevel warn


    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

你可能感兴趣的:(Rails,Ruby,lighttpd,nginx,Apache)