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的各种验证类。使用自定义验证类能够使代码更加简洁,复用性更强。如:
这里面就使用了三个自定义验证类:可用、合法、符合用户名规则。
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显式转换。如果你的项目确认一辈子都在天朝玩,不想加上啰里啰嗦的显式转换,可如此设置:
3.2.3 诸如ckeditor这样的组件要用到i18n,为了不出问题,最好还是显式指定项目的语言:
3.2.4 日志数据太多,可设置自动轮换,每周换一个新的文件:
3.2.5 更换默认缓存和解析器:
3.2.6 扩展css和js命名:
4、相关问题
4.1 初始化顺序。功能模块和某些配置的代码,有些在config/application.rb中,有些在config/initializers目录下的.rb里,有些库在lib/下面,这就涉及到一个rails初始化顺序的问题。如果代码执行的顺序不对,就会得不到预期的效果,或者会出一些莫名其妙的问题。rails3启动时的初始化顺序大致如下:
以上顺序未做深入研究,不保证完全正确,权威指南请移步官方文档。
4.2 sendfile。如果用nginx passenger做rails应用服务器,而且使用send_file或send_data来发送比如需要先身份验证等特殊需求的文件,需要在config/environments/下面的配置文件中设置:
否则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的参数值,修改配置如下:
5.3 erb解析。rails3的erb解析效率还是比较低的,听说默认的erb解析器erubis还是用c写的,不知道是我们的服务器不够强劲还是效率本来就如此。我们网站的首页大概有1000行erb,在使用了新的虚拟机参数之后,渲染时间大约在1秒钟左右。这个时间在用户体验上属于不可接受的范围。
我们把首页中渲染耗时较长的代码独立成片段文件,每个文件里面全部使用片段缓存机制。例如:
其中$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内存的限制,打个补丁就行了。
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内存的限制,打个补丁就行了。