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

前言
2006初,我接到了公司分配的一个遗留项目,让我负责一个基于C/S的系统的服务器端。其实是系统是基于HTTP协议的,因为负责客户端的同事对于服务器端编程不甚了解,虽然使用PHP对熟悉C++的他来说是驾轻就熟,但是在进一步实现更多的功能和更高的性能上就捉襟见肘了。项目是在非常突然的情况下交给我的,因为该同事在客户端上有更多的事情要做。我在分析了他的数据库结构和PHP源代码之后,决定按照与客户端的通讯协议重写他的服务器端。为了能应付老板苛刻的时间限制,我打算使用正在学习的Ruby on Rails。后来,项目在功能上非常顺利地交付了。
两年过去了,随着客户端数量的不断增加、客户端功能的增加、与服务器端交互数据的增加、老板对功能的要求不断增加,我在这个项目上走了不少弯路,尤其是在部署——或者说是架构——方面。
我遇到的最大的问题就在于并发链接数上。服务器与客户端的每次交互的数据量并不大,但内容无法缓存。起初用的是Nginx/Apache+Mongrel 的部署方式,但当遇到大量并发请求时,常常会遇到Mongrel进程死掉的情况。而客户端的用户在无法登录客户端的时候,经常会反复尝试,加重了服务器的负担、导致最后所有的Mongrel进程都挂掉。
最后,经过不懈努力,在现有的3台低端服务器上,可以满足每天500万次的请求。在这里,我将我的一些心得和研究成果总结出来,与大家分享。
简介
Ruby on Rails的部署方案基本上都是由两层结构组成,前端做请求的分发,后端以多个Ruby进程接受并处理请求,主要的差别便是其中的通讯协议,比如使用 FastCGI 或者是HTTP。这里讨论的局限于一台服务器,所以我不称之为架构方案,而仅仅是部署配置方案。
前端的选择
前端也有很多种不同的选择,我们来看一下开源领域主流的选择:
Apache
Apache功能十分强大,稳定性也十分好,是全球市场占有率最高的Web服务器。与它搭配的Rails部署方案也有很多,如:

Apache+mod_ruby:类似于mod_php、mod_python,将Ruby解释器作为一个模块加载到Apache中。(mod_ruby以及停止更新,所以并不流行)
Apache+mod_fastcgi或Apache+mod_fcgid,通过FastCGI协议与Rails进程通讯。(mod_fastcgi由于设计有缺陷因此仅推荐使用mod_fcgid)
Apache2.2 + mod_proxy_balancer:使用反向代理负载均衡器,通过HTTP与后端的Ruby Web服务器通讯,如Mongrel/Thin/Ebb等。
Apache2 + Passenger (mod_rails) :新兴的模块方式,部署方式十分简单,大致的方式和FastCGI差不多,只不过与后台进程间的通讯协议使用了它自己的协议。

Apache的一大问题就是Apache的性能和一些轻量级新秀Web服务器相比差很多,原因在于Apache到目前为止仅有prefork(进程)模式和worker(线程)模式的MPM是稳定的,这两种工作方式每服务一个链接,就需要创建一个进程或线程,而新兴的轻量级Web服务器,则都很好地利用内核的事件机制提高性能,极大地减少了线程或进程数量,而Apache的event(事件)模式的MPM依然在开发中。例如,Apache的请求处理速度、并发处理能力都比Lighttpd慢3-5倍,内存消耗和CPU消耗也高出很多。另一个问题是,mod_proxy_balancer虽然功能强大,但分发性能不高,比HAproxy或Nginx差很多。
基于这些问题,所以Apache+FastCGI/mod_proxy_balancer的部署方案并不流行,然而新兴的Passenger由于部署方式出奇简单,使得Apache可能会重新在Rails部署上再次占据一席之地。
Lighttpd
轻量级高性能Web服务器,服务静态文件的性能非常高。目前的稳定版本Lighttpd1.4中的反向代理模块有所缺陷,此模块会在1.5中全部重写并提供更好的负载均衡算法,可以对基于FastCGI、HTTP、AJP、SCGI的后端进行请求分发。Lighttpd+FastCGI是目前非常流行的一种部署方式,国内著名的JavaEye便采用的这种方式,JavaEye 负责人之一Robbin对Lighttpd+FastCGI的方式推崇备至。
Nginx
Nginx也是一个轻量级高性能的Web服务器/反向代理负载均衡器。Nginx也支持FastCGI,但不像Lighttpd 1.5支持对FastCGI后端的负载均衡调度。Nginx+Mongrel的方式受到很多人推荐。
HAproxy
HAproxy是一个纯粹的反向代理均衡器,非常小巧,基于事件机制。它不仅可以做HTTP反向代理,还可以作TCP的转发,所以同样可以对 FastCGI做负载均衡。在做HTTP反向代的同时可以对请求作一些修改控制。在ThoughtWorks推出的Rubyworks这个Rails应用套件中用到了它,原因是它能很方便地限制到后端服务器的链接数量。但HAproxy不是Web服务器,它不能处理静态文件。
Swiftiply
Swiftiply是一个负载均衡反向代理,但它与后端服务器的通讯方式很特殊——它开放一个端口让后端服务器主动链接到Swiftiply,这样可以与后端服务器建立一个持久链接,而且无须重启就可以非常容易地增加新的后端。Swiftiply使用了Ruby的EventMachine 实现了实践驱动机制。支持它的协议的后端有Swiftiplied Mongrel和Thin。

后端的选择
后端Rails的运行方式可以通过FastCGI或者是Ruby应用服务器的方式,CGI和mod_ruby已经不推荐。可以选的Ruby服务器有有:
WEBrick
WEBrick是Ruby标准库中自带的默认HTTP服务器,完全使用Ruby写成,性能较差。利用了Ruby的线程来服务并发的链接。有人写了利用事件机制来提高性能的补丁,由于应用少,不在讨论范围。
Mongrel
目前最为成熟Ruby应用服务器。使用了C++写的HTTP头解析器,所以具有较好的性能,它同样利用了Ruby的线程机制来服务并发的链接。 Mongrel的优点是稳定,兼容性好,很多平台上都可以使用,包括JRuby。Swiftiply小组还写了一个利用EventMachine的 Mongrel修改版,称之为Evented Mongrel,使用单线程、事件驱动方式。
Thin
利用事件驱动机制的Ruby应用服务器,并借用了Mongrel的HTTP头解析器,事件机制的部分同样是利用了EventMachine。相比Mongrel来说,稳定性和兼容性略差。例如,我在CentOS 3.2的平台上编译后启动便崩溃。
Ebb
又一个高性能的Ruby应用服务器,有多种工作模式,可以使用事件驱动机制,也可以使用线程模式。据说线程模式服务Merb(另一个Ruby的Web应用框架)可以达到更好的性能。和Thin一样,由于刚出现不久,还不是非常稳定。它要求Glibc 2以上版本,所以老版本的Linux无法使用,另外我在自己的Ubuntu 8.04上编译的版本在运行中无法正常接受链接,所以在本文中没有涉及到Ebb的测试。
性能测试
为了了解各种前、后端程序搭配的方式各自的性能如何,必须做大量的测试。下面我将我为公司做的这个项目所进行的一系列测试的结果展示出来,并做简单的分析。
我利用了Apache Benchmark(ab),对服务器端压力最大的身份验证部分进行压力测试。
注意:这些测试都相对比较简单和宽松,很多因素、细节没有考虑进去,只是作为一个参考。而且,对于不同的Rails应用、应用中的不同部分,都应该做独立的测试,来找到最合适的部署方法。

后端的测试
首先,我们先测试我们所使用的后端在同一台机器上的性能,由于前端通常通过内部环路或UNIX套接字与后端通讯,所以我没有从另外一台机器上进行测试。由于ab不能直接测试FastCGI,为了能了解FastCGI的性能,我通过Nginx来与FastCGI通讯,分别测试TCP和Unix套接字的性能,将这个测试结果做个参考。

测试机器为ThinkPad T43,Intel Pentium M 1.86G,1.5G RAM,系统为Ubuntu 8.04,内核版本为2.6.24-16。
被测试的对象的版本为:

Mongrel 1.1.3,以及Evented Mongrel
Thin 0.8.1(EventMachine 0.10.0)
fcgi 0.8.7(Nginx 0.6.29)

以上全部使用默认配置。测试方法是从1开始,以10为步长,到500的数值,作为并发链接数,每次测试1000个请求,得到的ab的报告中取每秒的请求数作为测试的结果。
由于Ebb没有能在我的系统上成功运行起来,所以尚未得到它的数据。其中Thin还支持KeepAlive,所以单独进行了测试,由于这个服务器的应用主要是动态内容,所以后面的测试都不测KeepAlive。
测试结果如下,左边的图是点和线,为了不让画面太乱,能清晰地了解的性能情况,我取了B样条,如右图:

我们从图上,在结合一些现有的知识,可以印证一些概念:
测试的前半部分FastCGI比HTTP快的原因是:

由于FastCGI是二进制协议,避免了HTTP协议复杂的格式解析、生成的消耗——这部分内容通常由更高效的前端服务器来完成。
FastCGI与前端服务器之间建立的是持久链接,避免了大量的打开关闭套接字的操作。

我们也看到了起初支持KeepAlive的Thin处理请求的速度也很快,同样应该是得益于持久链接,减少了打开关闭套接字的操作而造成的。
两个FastCGI的测试,虽然TCP套接字的额外开销要比UNIX套接字高一点,但基本是接近的,因为这两者性能的差异与Rails应用本身的处理速度相比是很微小的。所以FastCGI无论是走TCP还是走Unix套接字性能都是接近的。
然而,当FastCGI接受的并发链接数量不断上升时,请求的处理速度不断降低,这是因为FastCGI的处理请求方式是阻塞的,每次只处理一个请求(可以从fcgi包中的代码中看出来),随着并发链接数量上升,维持链接的开销越来越大。同样的情况也发生在使用了KeepAlive的Thin中。
测试中,Mongrel的响应速度非常稳定。虽然性能在前250并发量的测试中占下风,但是随着并发链接数的上升,它利用线程的好处逐渐显现出来。Thin虽然使用了单线程,由于是基于事件驱动,与Mongrel性能相差不多,但稳定性则低了。
Evented Mongrel的性能令人印象深刻,在后半部的测试中占据了上风。同样使用了EventMachine的Thin,性能却不如它,我觉得可能在于:一、 Mongrel不支持KeepAlive,Thin支持,所以需要作额外的考虑;二、可能是Mongrel的HTTP头解析器性能更好。关于第一个因素,我将最大持久链接数设为了0,最后的测试结果是比Mongrel还要低。至于进一步研究为什么,就不在本文讨论了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值