rails3项目解析之1——系统架构

用rails3做目前的这个网站项目,已经有半年多了。我们这个团队应该算是比较早使用rails3做项目的,3.0正式版刚发布就开始尝试了,在项目开发期间针对很多问题也做了一些探索。谈不上经验,更称不上最佳实践,只是分享出来,经学见易,道家见淫,有需要的朋友各取所需。小公司小项目,适用于初中级用户,大牛们可一笑而过。 

1、网站需求 

财经资讯网站,向用户提供财经金融资讯,发布和宣传公司研发的各种金融产品,引导用户注册和购买产品。当前网站的内容来源是公司的资讯平台和行情数据库,通过http接口和oracle sql获取数据并展现,可能在中短期会有用户互动和用户原创内容(UGC)的需求。 

在可预期的未来,即2到3年内,预计流量将达到10-100万PV/天。因此在进行设计时,以该流量作为本架构能够承载的上限。如果网站真的有幸活到了几百万PV以上的流量,那肯定就不缺钱了,凡是钱能解决的问题,都不是什么大问题。 

2、架构设计 

根据预计流量,在可预期的时间阶段,结合该项目的运营要求和预算成本,网站主要以高可用(HA)和有限的水平扩展性(scale out)为核心架构理念,采用分布式无共享架构(distributed share nothing architecture),使用rails默认的cookie_store机制来持有和处理session,消除了有可能成为性能瓶颈的集中式session的缺点。而且在架构设计时,使得系统负载尽量平均分配到每台服务器上。 

单台服务器如同XX的承诺,永远都是靠不住的。单台服务器会死机、掉电、停转、拔线,所以在架构中尽最大可能避免每个功能组的单点故障,做到在服务器集群中,任意一台服务器失效,或几台不相关服务器同时失效,网站仍可正常运行。而且无单点故障的架构,服务器可随时重启,也有利于操作系统的内核升级和安全补丁等日常维护工作。经过这几个月的连续使用,各种原因的几次单点故障均没有影响到网站正常服务。 

在数据库的使用上,考虑到传统的关系型数据库已不太适应当前互联网应用的海量数据和高负载特点,因此mysql只起到关键数据存储作用,利用事务性和成熟性,保证网站数据的完整和安全。然后加入非关系型数据库redis和mongodb,作为数据冗余存储和计算中心,承载绝大部分的高负载数据请求,可有效减小mysql的压力。这样就不必费心配置复杂难用的可扩展mysql集群,使用单台mysql服务器即可承载较高的网站流量。而redis和mongodb天生就是为互联网应用设计的,它们的集群配置和水平扩展相对更为简单方便。听说现在已经有团队只使用mongodb来作为网站数据库,向他们的前卫和勇敢,致以我们团队深深的敬意。 



2.1 软硬件平台 
当前正在运行的硬件是6台dell 2U二手服务器,总价大约在1.6万,物美价廉,居家必备。目前使用良好。另外借公司发展东风,已有8台全新dell刀片进入机房,正准备把全部系统迁移至新服务器上。 

服务器操作系统使用ubuntu server 10.04.2 x64,正在测试11.04,如可用并有益,则有可能在新硬件上安装使用。11.04官方支持到2012年12月份,对人类来说已经足够。2012之后,所有服务器已不复存在。 

2.2 高可用方案。核心组件采用keepalived,使用master-backup机制来实施主备服务器的实时切换。 

2.3 负载均衡 
根据网站流量和实际需求,使用nginx作为七层交换,把前端进来的用户请求round-robin到后端的应用服务器。nginx支持容错转移,如果后端的某台应用服务器失效,nginx可把该台服务器暂时移出可用列表。 

同时,由于负载均衡服务器位于整个网站系统的最前端,一旦失效则整个网站立刻瘫痪,所以其重要性无与伦比。为保证高可用,使用keepalived实现双服务器的故障实时切换。 

2.4 应用层。使用ree+passenger+nginx作为rails web服务器,passenger易于管理维护,而且和ree配合较好。所有应用服务器地位均等,每台服务器均发布完整的项目代码,不在功能上做分布式,以利于维护。 

2.5 数据库。使用2台mysql,做master-master复制,配合keepalived实现高可用。 

2.6 缓存系统 
缓存系统分为一级缓存和二级缓存。一级缓存用于存储数据量不大,但对速度要求高的缓存数据。二级缓存用于存储对速度要求相对较低,但存储量巨大的数据。 

一级缓存使用内存数据库redis,优点是速度快,并发高。用于存储首页缓存数据,保存股票行情数据,以及配合redis-store作为rails默认页面缓存,等等。目前存储数据约2800条,使用内存100M。 

二级缓存使用文档型数据库mongodb,优点是查询功能强大,支持海量存储。用于存储部分资讯内容,提高页面响应速度。目前存储数据约10万条,数据文件大小为4G。 

2.7 文件系统。使用glusterfs,以其自身的机制可实现双机热备和单台服务器失效返回后文件的自动同步。用户上传的文件会自动地同时保存于2台glusterfs服务器上。对应用程序来说,它们只是将文件保存于本地某个指定目录,glusterfs对应用是透明的。而且任何一台服务器单独失效都不会对用户产生可察觉的影响,失效的服务器返回后,glusterfs会计算2台服务器所保存文件的差别,对改动过的文件进行同步。 

2.8 异步和定时执行。使用resque作为基础架构执行异步任务,以resque-scheduler执行定时任务。同样,也以双机互备来保证无丢失地产生和执行任务队列。经过这几个月的使用,除了解决了一些与其它系统交互时意外的队列堵塞问题,目前看来resque还是值得信任的。 

3、技术选型 

在技术路线上,团队拥有最大的自由度,因此我们可以按照自己的理念进行技术布局,而且可以大胆地使用最新的技术架构和解决方案,在完成公司开发任务的同时提高团队技术水平,紧跟业界技术潮流。 

3.1 网站使用rails 3开发,用到的主要组件和版本如下。未注明版本号的,为最新版本。 

Ruby代码

  1. ree 1.8.7 rails 3.0.8    # 基础平台  

  2. rake 0.9.2  gem 1.8.5  bundler 1.0.14    # 基础工具  

  3. mysql2 0.2.7  ruby-oci8  activerecord-oracle_enhanced-adapter    # 数据库驱动  

  4. nokigiri  yajl-ruby    # 解析器  

  5. authlogic  cancan    # 权限和验证  

  6. ckeditor  paperclip  rmagick    # 编辑器和图片  

  7. redis-store 1.0.0.rc1  mongoid 2.0.2    # nosql  

  8. resque  resque-scheduler  eventmachine    # 异步和定时任务  

  9. capistrano  capistrano-ext    # 代码发布  

  10. open-flash-chart  formtastic  rspec  spork    # 杂项  



3.2 数据库。使用mysql 5.1。因为5.5取消了在文件中配置replication,只能手动命令执行,个人感觉比较麻烦,不能做到服务器的无人值守。如果有同学找到了5.5自动配置的方案,还望赐教。谢谢。 

3.3 redis 2.2.8。因为官网说redis原生的cluster方案,有可能将在2011年6月才出RC版,所以目前我们使用redis的master-slave机制,自己写了一个监控脚本,配合keepalived,实现两台redis服务器之间的数据同步(replication)和容错转移(failover),以此来达成高可用。 

3.4 mongodb 1.8.1。mongodb自身有原生的replset方案来实现数据同步和容错转移,因此在mongodb的层面直接使用该方案,配置2台服务器即可实现高可用。 

3.5 glusterfs 3.2.0。使用原生的双机互备方案。 

4、项目管理 

第一期的开发从2010.11开始,到2011年元旦上线,大致为2个月的时间。上线后又经历了大概1个月,基本稳定达到目前的状态。纯代码约1万行。开发人员4名。开发平台有ubuntu desktop和windows 7,开发工具有aptana、netbeans、emacs等。对平台和工具不做要求,在dos下能把活干好,也行。 

4.1 源码管理。 使用 git ,采用所谓的“稳定分支模式”。有3个主要的分支:master、alpha、production。源码合并的顺序一般情况下是master -> alpha -> production。master用于日常开发,alpha用于发布测试版本,production用于发布生产环境的正式版本。如果有hotfix或者feature的需求,再另开其它临时分支。每个开发人员对所有分支都拥有全部读写权限,使用公钥认证的ssh访问源码库。 

4.2 发布管理。 使用 capistrano 作为发布工具,结合capistrano-ext的multistage功能对多个不同的发布环境进行管理。并且结合了bundler的capistrano模块对bundle gems进行发布时的自动安装管理,做到了测试版本和正式版本的一键化发布。在99%的情况下不需要登录服务器另外做配置或修改。 

4.3 项目管理。 使用 redmine 作为项目管理平台,可以和git库有机地结合起来。 

4.4 测试。 由于大部分功能都是调用其它平台或访问行情数据库,逻辑比较简单,因此 rspec 用得不太多,仅在支付接口等部分商业逻辑上使用。这是项目目前的一个缺陷,以后会着意加强测试方面的代码量。 

5、未来扩展 

5.1 负载均衡的性能取决于接受请求的那台服务器的性能,nginx的并发还是令人放心的。即使以后性能成为瓶颈了,可以用更好的服务器,或者换硬件交换机,直至F5。 

5.2 应用层的扩展比较简单,只需增加应用服务器节点即可。负载均衡的nginx可以设置权重以平衡负载。 

5.3 mysql不太好扩展,但如前面所言,把负载尽量分散到nosql上,在百万PV级别,mysql也就无需扩展了。实在要扩展,可以尝试做读写分离等方案,或等待几年后mysql搞定更漂亮的水平扩展方案。 

5.4 redis和mongodb都比较方便水平扩展,多加服务器,做集群配置,即可分散流量提高负载。 

5.5 glusterfs也具备水平扩展能力,再与nginx结合直接输出文件,可承载较大流量。 


项目基本架构就是如此,限于篇幅,很多地方都是一带而过。下一步我准备写如下内容,留作个人积累和公司文档,包括但不限于: 

1、keepalived的配置和使用,优缺点。 
2、rails 3的优点,个性化设置,存在的缺点和临时解决方案。 
3、redis和mongodb的主从复制架构,相关问题的解决方案,各自的特点和基础使用。 
4、glusterfs的配置和使用。 
5、resque系列组件的使用,异步和定时任务执行。 

6、测试和产品多环境下的capistrano一键发布系统。 

转自: http://www.iteye.com/topic/1058510 
此人绝对大牛了。。



为什么对全Mongo后台有顾虑? 
nosql的理念和mongodb数据库毕竟是新事物,诞生时间还不长,不象sql理论和mysql经过了长期的严格考验。而且mongodb现在没有太多的最佳实践,担心出了问题不能很快解决。所以即使我知道mongodb确实很稳定很不错,但仍然不太敢于把订单和用户等关键数据存放于此。另外现在mongodb还没有事实上的管理工具标准,使用上稍有不便。 

不过,仍然不能否认mongodb确实很优秀,特别是和mongoid配合起来,用得很顺手。 

所以我们现在的开发理念是把数据存储于mysql,mongodb里的数据只是mysql数据的复制和冗余,用这份冗余来以空间换时间。万一出现数据不一致,就以mysql为准向mongodb同步数据。这只是我们团队目前所采用的方式而已。



能不能给个网站连接,或者大概讲哈 投入运行后越到的问题。现在,很多人对Ruby 的运行效能有很大的疑问。 

因为rails3的架构更复杂,所以个人感觉要比rails2运行速度慢。从服务器的启动到页面的渲染,实测的时间都要更长。特别是rails3的页面渲染,网站首页内容如果比较多的话,渲染速度几乎不可接受。 

我不知道页面渲染的问题是rails3本身的问题,还是我们在哪儿没有设置好,一直没有很好地解决。目前用了ree官方推荐的GC优化参数,另外再对首页划分partial,做片段缓存,现在渲染时间在200ms左右。当然,我们还留了一部分没有做片段缓存的,是为了等着哪天老板问我们首页能不能再快一点,我们再把剩下的加上缓存,这样每次工作任务都会有显着的业绩。。。。。。 

其实无论是作为ruby,还是rails,当前的运行效能已经足够。在网站应用的层面,就算出现一些效率问题也可以从软件和硬件等各个层面解决。与rails开发和维护所节省的时间人力等关键成本相比,解决这些问题都是值得的。当然了,不推荐用rails做密集、实时或并行运算。用最合适的语言,做最适合的事情。



最近正在为定时任务而头疼。cron + rake的CPU占用率实在是个问题。看了LZ的文章决定试试resque去。 
BTW,eventmachine还可以做定时任务吗? 

我们当时选型的时候也考虑过cron模式,但使用cron来跑定时,与操作系统绑定太紧,不便于发布和维护。用resque-scheduler,所有定时任务都写在一个配置文件里,清晰易懂便于维护,而且发布后只需要kill掉rescue-scheduler的常驻rake再重启,即可刷新定时任务规则,方便快捷。 

不是用eventmachine做定时任务,它是底层组件。resque-scheduler要使用rufus-scheduler,rufus的定时有两种实现方式,一是plain方式,就是用sleep来做loop,二就是em方式,如果系统中安装了eventmachine,rufus会自动使用em模式。



在服务器版本的选择上比较好奇,为什么选择使用Ubuntu这个称霸个人桌面的服务器版? 
我对Ubuntu的印象还只停留在个人版(从7.1到8.04)。 
为什么不考虑使用Centos?个人感觉无论从版本更新,功能安全,还是使用习惯上都要更好些。而且多机负载均衡也好处理些。 
另外,Mysql自身的负载均衡也很完善,但是从你文中好像没看出来有很好的应用? 

服务器发行版选择ubuntu server,也算是一种个人爱好吧。作为rails的运行平台,无论是各种组件的安装还是问题的解决,相对都比较顺利。至少就我个人感觉,ubuntu似乎已经成为rails平台的标配,网上的教程和示例大多数都是以ubuntu为基础的。我们也曾经在公司遗留的centos上安装过rails,有一些问题还是很麻烦的。 

版本更新上,ubuntu已经不输于centos,可以说有过之而无不及。从功能安全上,少安装点不必要的包,iptable规则严格一点,时常自动更新内核,也就差不多了。至于使用习惯,我们团队还是都更习惯ubuntu的,毕竟开发桌面以ubuntu居多,都是同一家的孩子,desktop和server是一脉相承的。多机负载均衡我感觉centos和ubuntu没什么本质上的区别,不知道你所指是哪方面。 

你说的mysql的负载均衡,是指mysql ndb cluster吗。我们也评估过它,一是感觉太复杂,对我们的项目有点大材小用,二是我们看过一些网上的评价,很多用过的人还是持保留态度的。就算是mysql proxy之类的读写分离方案,仍然感觉有些复杂。似乎mysql原生的扩展方案都让人不是很舒服,所以干脆就只用replication做高可用了,实质上还是单机。前面我也说过,对我们的项目来说,结合nosql,单机也够用了。如果实在要扩展mysql,倒不如直接用handler_socket来得彻底。 

应该也是为了寻求尽量贴合项目实际的方案吧,要是公司告诉我们做个一千万PV的,我们也就不用现在这套方案了。




不错呀,看了受益很多,公司里面就我们team在做ruby on rails尝试,都要从头积累呀,虽然比较累,但是很有意思,风险上很小,都是针对公司内部的管理系统上做尝试,允许出错。另外,公司评论已经上mongodb了,15台服务器的规模,貌似在国内是最大的了吧?不过我们ror组还没用mongodb,语言换新的就很累了,数据库再换了就会疯掉的。 

希望能看到楼主更多的心得,我也愿意在ror使用中分享一些东西,希望对国内ror会有所促进。我们已经在跑的一台ror服务器是apache2.0.x+passenger,不是最优的搭配,以后会再调整,数据库是MySQL,前端就加了个memcached,因为不是做WEB,是做的API用的,每天接口调用在240万次左右,等负载高了肯定还得优化系统、加服务器呀。数据库方面读取都cache在memcached里面了,写操作是通过文件缓存来做的,比较简单:每个数据insert或者update等请求写一个文本文件,不用加锁,另外有进程定时扫描这些文件,入库后删之,所以MySQL负载很轻。


你们在front-end server和db-server这里都用keepalived来保证高可用性. 这个方案成熟么? 对技术人员的要求如何? 

keepalived本身是比较成熟的,在front-end用keepalived来做高可用,也算是比较成熟。不过个人感觉keepalived本身有一些局限性,比如服务器完全挂掉的时候keepalived很管用,但如果服务器正常,但本地的nginx服务无响应,就比较麻烦了。keepalived虽然也有检测本地服务的模块,但并不是一检测到本地服务失效就马上切换到另一台,不是我想要的效果。我研究了一段时间也没找到解决方案,不知道是我没找到,还是本来就如此。 

keepalvied配合mysql的master-master复制来做高可用,是我自己琢磨的。做完了之后在网上一搜,才发现已经有人这么做了。但这个方案并不是很成熟,主要是因为mysql的复制是有一段极小的时间间隔的,如果在mysql尚未同步完成时,keepalived就切换了主从,容易造成mysql的id重复。当然这也可以解决,比如设置自增id的步长即可,不过我不喜欢这样。但总之,keepalived配合mysql,不是很成熟。 

本来我是想通过修改activerecord来实现mysql master-master的自动服务转移,后来感觉比较麻烦,所以就没做,省了个事用keepalvied来实现。如果真要做可靠的mysql高可用,还是建议在rails端解决,或者是用更好的方法消除两台mysql数据同步的潜在冲突,比较好一些。其实最好的还是原生方案,象mongodb那样的,在数据库的层面就避免这个问题,这是最优的解决方案。 

对技术人员没什么太高要求,keepalived比较简单,文档也算充实,一般的技术人员都能看明白并且配置成功。



1、要说到“独立”安装这么一套系统,确实是有点复杂,但也不是特别困难。只要把安装文档写详细了,按照步骤来安装服务器,应该问题不大,无非就是一些apt-get之类的,再加上些配置。把配置文件备份好了直接还原就很快了。代码部署是用capistrano写的,几个命令就搞定,也不存在困难。比较麻烦的是真正理解这套系统,而且出了问题能知道在哪儿,这就看个人的造化了。大牛私奔总是比较头疼的事,所以我们团队的目标是把每一个人都培养成大牛,私奔几个都不怕。 

2、关于业务模式和流量的推测,看得出来你很有经验,只是有点抬举我们了。我们是个小网站,刚上线没多长时间,还没有开始大范围推广,流量很小。但是即使流量大了,我们也不太想采用静态页面这种方式。因为毕竟网站业务还没成型,需求变化很快。互联网业务的特点就是多变,有可能今天的静态页面,明天就要大范围改动和调整,或者忽然要求加上很多动态的内容。而且象金融资讯这种比较专业的网站,一般也到不了新浪那种有必要使用静态页面的程度。因此在相当长的一段时期内,我们还没有考虑静态页面。单纯的资讯页面,加上缓存,几台服务器支撑一下还是问题不大的。 

3、gluster确实目标比较远大。而且当初我们选用它,也是因为它不改动操作系统内核,所以再怎么折腾也不会对系统造成致命的影响。我们目前用的主要还是gluster的replication高可用的那部分功能,有一些小问题(比如没有文档中所说的自动同步),但目前看来还是可用的。至于以后会不会用到distributed和striped等横向扩展,到时再说。说不定到时有钱了就上硬件了。走一步看一步吧。 

4、你的架构不能说简陋,其实最简单的才是最有效的。我之所以做的这么复杂,也是因为我这人爱折腾,平时没事就升级个服务器重启下什么的,所以才做个没有单点的架构。个人爱好而已。呵呵。 

5、nfs当然很可靠,只是我没找到怎么用nfs做高可用,一台服务器我又不放心,所以就找到了gluster来做这个,而且在gluster上也是用的nfs协议,兼容性不错。至于说访问文件的速度,这个我们没有通过gluster的read接口来做,我们只是用gluster来写文件,下载的时候绕过gluster直接用nginx读取本地文件,速度上暂时没有问题。 

6、请问一下你的mysql 5.5主备,是怎么做的。5.5不能在配置文件里写主备复制的参数了,你是用脚本做的,还是启动起来之后再登录mysql敲命令?

posted @ 2012-07-02 01:05  luckc#  阅读(312)  评论(0编辑  收藏  举报