欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

rails3项目解析之2——rails基础 Rails项目管理RubyRediscapistrano 

程序员文章站 2022-05-10 13:51:17
...
rails3项目解析之1——系统架构
rails3项目解析之3——redis


rails 3.0是2010年8月份发布的。迄今为止,3.0已历经多个tiny版到了3.0.8。3.1已经放出rc4,看起来离正式版已为期不远。相对于2系,3系还是有一些令人惊喜的变化,而且在架构上也规范和严整了许多。3.1中更是又加入了几个颇为有趣的特性。我们的项目一直都是紧跟rails新版,很欣慰能够毫无道德压力地做一个喜新厌旧常换常新的男人。但什么都追新也吃了不少小苦头,不过仍然死不悔改,大家都在坐等3.1发布试吃。

更多关于rails3的新特性,网上一抓一把,需求和欲望比较强烈的同学可自行解决,在此不再赘述。下面主要说一下我们这个项目。因为项目不大,而且第一期没有太多用户互动的内容,所以涉及到的知识范畴比较小,都是些基础的东西。

1、目录结构

除那些默认目录之外,我们在项目中又另外加了一些自定义的目录来做不同的事情。

1.1 errros/。在app/目录之下,内容是各种自定义的XxxError类。使用异常机制能够使得代码更加清晰,流程语义更准确,更好地实现模块化的程序结构。

1.2 validators/。在app/目录之下,内容是继承自ActiveModel::EachValidator的各种验证类。使用自定义验证类能够使代码更加简洁,复用性更强。如:
validates :username, :availability => true, :legality => true, :username => true, ......

这里面就使用了三个自定义验证类:可用、合法、符合用户名规则。

1.3 workers/。在app/目录之下,内容是resque任务的定义类。

1.4 其它和项目相关的目录。比如在项目根目录下,我们加了一个monitor/目录,里面是resque和redis的监控脚本。因为监控脚本在概念上属于项目外层,所以放到根目录下面,不隶属于app/。

2、使用组件

2.1 rails 3.0.8。这个没什么可说的,用蜂蜜,川贝,桔梗,加上天山雪莲配制而成,实在是居家旅行、杀人灭口之必备良药。不过我们在3.0.8上吃了点苦头,丫把safe_buffer设置为不可改动,结果页面片段缓存的cache方法中的slice!触犯了这个禁条,网站首页直接挂掉。还好,发现这个问题是晚上10点,没有造成太多损失。3.0.9已经修正了这个大bug,请各位不必多虑。

2.2 mysql2。舍弃了mysql驱动,使用mysql2。不过要设置为'< 0.3.0',0.3版的mysql2是配合rails 3.1的。

2.3 ruby-oci8、activerecord-oracle_enhanced-adapter。连接公司oracle数据库取行情数据的。还需要安装oracle的instant client,把安装目录添加到/etc/ld.so.conf.d里,然后ldconfig。有想用oracle的可做参考。

2.4 nokogiri、yajl-ruby。正好项目中也要用到nokogiri,所以就干脆替代了rails自带的xml和json解析器。

2.5 authlogic、cancan。比较了一下authlogic和devise,感觉各有利弊。因为authlogic对于namespace的处理比较灵活,而且也利于第三方身份验证的扩展,比较适合我们的需求,所以最后选了authlogic。cancan对于不是很复杂的权限验证,也差不多够用了。更细粒度的权限系统,恐怕就得根据需求自己定制了。

2.6 acts_as_list、acts_as_state_machine、will_paginate。前两个不解释。will_paginate的rails3版本已经很久没更新了,不知道作者是恋爱了还是失业了。kaminari是个很好的替代,只是有些功能还没完善,是否可以完全替换will_paginate有待论证。

2.7 ckeditor、paperclip、rmagick。其实富文本编辑器已经有不少了,比ckeditor好的也不乏其数。不过具备相配合的成熟gem的,恐怕目前还只有ckeditor。如果不嫌麻烦,可以找个更好的编辑器,直接挂上去用,或者自己写gem挂到rails上。

2.8 redis-store、SystemTimer、mongoid。使用redis作为默认的rails缓存,SystemTimer是redis client等几个gem要求使用的,ruby 1.8.7的timeout不可靠,我见到的几个gem的作者都强烈建议使用SystemTimer。mongoid支持rails3,所以就没用mongo_mapper。mongoid配置简单,语法很不错,功能比较强大。

2.9 resque、resque-scheduler、eventmachine。研究过twitter当初做的starling和workling,勉强能用,但配置很复杂,而且很久不更新了,就始乱终弃之。resque套件另行开文,在此不多说。

2.10 formtastic。formtastic号称是语义表单,实际用起来还是挺不错的,erb上的代码简洁多了,学习曲线也不高,一看即会。而且formtastic扩展性非常好,我们修正了和ckeditor的接口,还自定义了日期选择(date_picker on jquery)、日期选择组(date_selects)和验证码(captcha)控件。

2.11 client_side_validations。这年头网站上没个客户端动态验证,出门都不好意思跟人家打招呼。它能够直接使用model中定义的验证规则,DRY得很。而且能够和formtastic无缝整合。另外也很好扩展,我们利用回调加上了动态验证时的绿对号红叉号的小图片。

2.12 capistrano、capistrano-ext。代码发布管理的。如果你有不同开发环境的十几台服务器需要依次发布代码,就知道这东西有什么用处了。同样,这部分将另行开文。

3、配置

虽然说“约定优于配置”,但也不能没配置。不过如果只要配置配置就能搞定,总比写代码好多了。

3.1 自定义配置。项目中要使用到一些自定义的配置,比如oracle数据库的地址、redis服务器地址等等,而且开发环境和产品环境的还不一样。我们在config/目录下放了一个configuration.yml,里面区分出不同的environment,各个env下写入各自的配置。rails初始化的时候读取该yml文件,把相应env的配置以hash存入全局变量。以后以此全局变量来取得配置值。

3.2 系统配置。rails3的系统配置大部分都在config/application.rb中。

3.2.1 config.autoload_paths里要加上#{config.root}/lib,3.0版本里不再自动加载lib/目录。

3.2.2 数据库存储时间的字段默认使用UTC的时区,显示这些字段时要加time_zone显式转换。如果你的项目确认一辈子都在天朝玩,不想加上啰里啰嗦的显式转换,可如此设置:
config.time_zone = 'Beijing'
config.active_record.default_timezone = :local


3.2.3 诸如ckeditor这样的组件要用到i18n,为了不出问题,最好还是显式指定项目的语言:
config.i18n.default_locale = 'zh-CN'
I18n.default_locale = 'zh-CN'


3.2.4 日志数据太多,可设置自动轮换,每周换一个新的文件:
config.logger = Logger.new(config.paths.log.first, 'weekly')


3.2.5 更换默认缓存和解析器:
config.cache_store = :redis_store, ‘redis://xx.xx.xx.xx:6379’
ActiveSupport::XmlMini.backend = 'Nokogiri'
ActiveSupport::JSON.backend = 'Yajl'


3.2.6 扩展css和js命名:
config.action_view.stylesheet_expansions[:formtastic] = %w(formtastic formtastic_changes)


4、相关问题

4.1 初始化顺序。功能模块和某些配置的代码,有些在config/application.rb中,有些在config/initializers目录下的.rb里,有些库在lib/下面,这就涉及到一个rails初始化顺序的问题。如果代码执行的顺序不对,就会得不到预期的效果,或者会出一些莫名其妙的问题。rails3启动时的初始化顺序大致如下:
config.ru -> config/environment.rb -> config/application.rb -> config/boot.rb -> rails framework -> bundle gems -> configs in config/application.rb -> alphabetical .rb files in config/initializers

以上顺序未做深入研究,不保证完全正确,权威指南请移步官方文档

4.2 sendfile。如果用nginx passenger做rails应用服务器,而且使用send_file或send_data来发送比如需要先身份验证等特殊需求的文件,需要在config/environments/下面的配置文件中设置:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

否则send_file送出的文件大小为0。

4.3 本地化。需要翻译输出本地化文字的时候,尽量在config/locales/下面的相应文件里定义,这样能够精简erb或model的代码,职责也分得很清楚。目前我们在locales下面放了authlogic、formtastic、model中字段中文名和rails系统信息的中文翻译。

4.4 路由。rails3的routes语法改动比较大,而且写起来很*。从项目管理的角度来说,有必要对routes的写法做统一的培训,使团队成员都真正理解routes的语法规则和适用环境,能统一使用最优写法的就尽量使用,实在不行再用其它写法解决。这样routes风格会比较统一。而作为反例,现在我们项目里的routes就是大家各显神通,什么样的写法都有,很不利于沟通和维护。

4.5 js框架。rails 3.0的默认js框架仍然是prototype,在jquery大行其道的今天,这个做法有点逆潮流而动。作为某些简单的js功能,用js generator是省时省力的做法,代码也干净。找了一个N年前就不更新的rails2的jquery rails,改了改使之适用于rails3,简单功能先凑合着用了。还好,3.1终于把默认js框架改成jquery了,当真是从善如流,幸甚至哉。

4.6 测试效率。即使在ubuntu平台上,跑rspec时rails所做的前期准备工作现在也延长到了好几秒钟,作为珍惜生命的开发人员,这几秒钟是难以忍受的。我们引入了spork作为测试的加速器,它会事先prefork出完整的测试环境,节省了初始化加载的时间。不过很不幸,我们现在做的rspec很少,所以在spork方面不做太多发言。

5、调优

在江湖的传说中,很多哥都会用“效率低下”来攻击rails。不过作为一个非计算密集型的项目,我们很少会注意到rails在代码执行上有瓶颈。但是在以下几个方面,我们还是感觉到了rails的某些不足,也尝试着做了一些优化。就效果来看,虽然没有数量级上的提升,但作为项目来说基本达到了要求。

5.1 应用初始化。rails3引入了rack作为底层架构,在架构层面上比rails2更为复杂,所以在应用的初始化时耗时相对较长。我们通过设置passenger_min_instances,提前多启动几个实例,减少用户访问时启动实例的时间。另外使用passenger_pre_start http://www.xxx.com/来初始化rails应用,减少初次访问的响应时间。不过比较郁闷的是,passenger_pre_start貌似效果并不是很明显,而且不支持touch restart.txt的重启,不知道以后会不会好一些。

5.2 虚拟机参数。即使是ree,ruby虚拟机的默认参数也基本上不满足普通应用的需求。我们参考了网上有关twitter和37signal的参数值,修改配置如下:
RUBY_HEAP_MIN_SLOTS=500000
RUBY_HEAP_SLOTS_INCREMENT=250000
RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
RUBY_GC_MALLOC_LIMIT=50000000
RUBY_HEAP_FREE_MIN=100000


5.3 erb解析。rails3的erb解析效率还是比较低的,听说默认的erb解析器erubis还是用c写的,不知道是我们的服务器不够强劲还是效率本来就如此。我们网站的首页大概有1000行erb,在使用了新的虚拟机参数之后,渲染时间大约在1秒钟左右。这个时间在用户体验上属于不可接受的范围。

我们把首页中渲染耗时较长的代码独立成片段文件,每个文件里面全部使用片段缓存机制。例如:
<% cache "recommend_#{$recommend_pageid}" do %>
……
<% end %>

其中$recommend_pageid是rails在初始化时计算的该片段文件的md5值,这样如果该片段文件有改动,缓存中就不存在这个新的key “recommend_xxxxxx”,rails将重新生成该片段的缓存。这个方法既可使用片段缓存提高渲染速度,又可实现片段文件改动时的缓存自动更新。

使用片段缓存之后,首页渲染时间降低到了200ms左右,基本满足了要求。当然我们还留了一部分页面代码没做缓存。作为一个懂人情讲政治的开发团队,要给自己留出足够的业绩提升空间以体现自身价值,并且给领导留出足够的命令空间以展现领导权威。这样下一次领导再让你进一步缩短首页访问时间时,双方可皆大欢喜。

6、rails on windows

首先我承认,windows确实很不适合rails开发,尤其是那个执行效率,生活在mac和ubuntu上的幸福的人儿是永远都体会不到的。不过,如果rails只是我生命中的一小部分,其它大部分事情我确实需要在windows上做,而且我还很不喜欢虚拟机,那就要研究在windows上开发rails了。本节内容仅是给喜欢windows的同学提供一点参考,ubuntu和mac控可鄙视加无视。

其实解决方案很简单,就是使用rubyinstaller,同时安装devkit。基于mingw32的这套系统应该还算不错,到目前为止,一般的需要编译的gem象mysql2、nokogiri等等都没问题,最多也就是加几个参数,便可编译安装。迄今只有SystemTimer因为使用到了linux的底层SIGALRM而无法安装,不过也可以想个办法绕过去即可,不影响实际使用。

我的操作系统是windows 7 sp1 ultimate x32,不要用x64,x64下的mysql2安装会有一些莫名其妙的问题。感觉x32位还是比x64方便,一般不会出兼容性问题。至于4G内存的限制,打个补丁就行了。