五个月前,Arvato Systems公司找到我说:
\- 有一个重要项目要我领导。 \
- 可以使用我喜欢的技术。 \
- 团队成员(至少部分)可以由我挑选。 \
对了,他们还告诉我,这个项目的成果可以改变整个世界!我以前被这种事情骗过,但这次,我相信它是真的。在本文中,我将向你讲述这个项目非凡创意背后的技术细节。但首先,我得让你知道它的名字——ChangingThePresent.org。这个站点瞄准了每年2500亿美元的礼品市场。以往,我们无非是送人水果蛋糕或一双毛拖鞋,而通过ChangingThePresent,你可以送出更有意义、让人难以忘怀的礼物,比如癌症研究人员的一小时时间、一英亩雨林的保有权等等。它让送礼更为方便,而且有自己的盈利模式;但最让我惊讶的是,这个创意改变了人们的思维方式。
\背景
\如果你对非盈利组织有所了解,那么就能大致猜到ChangingThePresent的运作模式。就使用的技术而言,它和其他需要通过互联网聚集用户的行业别无二致。但在ChangingThePresent,人们彼此不再是买卖关系,而代之以捐赠和无利可图。在兰斯·阿姆斯特朗基金会、儿童救助协会、第一本书(First Book,美国贫困儿童读物捐赠机构)和Sierra俱乐部(Sierra Club,美国自然保护组织)等将近200个顾问机构和380家非盈利组织的支持下,ChangingThePresent的市场运作工作正在走上正轨。但在这里,我更想告诉你的是这个项目技术方面的故事。
\在本文中,我将带你重温我们使用Ruby on Rails开发此站点的整个过程。你将了解我们用到的一些关键特性和常用插件。我们使用的技术并非惊世骇俗,但对我们每天所做工作有所了解,可能对你有所帮助。我的目的是在团队协作、生产环境下的可信任技术、我们使用的工具,以及我们认为非常重要的Rails框架等方面,让你有个大致认识。我还会提供一些链接资源,但不会就具体问题作细节探讨,如果你希望深入了解,可在文后留言。如果你们真有兴趣,我将乐于撰写系列文章,就你们的问题共同深入讨论。
\概述
\最早的时候,我以个人身份加入了这个团队,担任主程序员。因为可扩展性和稳定性优异,团队领导人首选Java,但以此评估整个解决方案后,发现投入巨大且系统复杂。我们后来盯上了Ruby on Rails,它很好兼顾了高效生产力、编程模型的清晰度和运行性能。当然,它也并非万无一失。仔细分析现有的站点和我们的商业计划后,我们觉得自己有能力设计出每天处理数百万请求量的系统。尽管当然还没有这样的成功案例,但我们认为通过聚合缓存(Aggregation Cache)和无共享群集(Shared-Nothing Cluster)技术,可以开发出如此级别的系统。
\9月份来了又去了。我们的工作非常高产,每周都能推出Demo,进展神速,市场开始关注此项目。到11月份,我干脆加入了WellGood LLC公司,并担任CTO。12月间,我们发布了站点的第一个Beta版。Rails为我们生产力的提高和最后的成功起到了至关重要的作用。
\我们离整个站点的完工还差得很远——到目前为止,已经完成的功能大概只占原计划的5%——但我们已经有了足够积累,相信后来的工作会越来越顺利。尽管我们已经初步做过基准和压力测试,但仍然无法保证现在的版本能够长期应对访问量的增长,因为几乎没有哪个Rails站点提供了先例。但我们在以下几个方面大有信心:
\- 我们可以实现快速开发、测试和发布。 \
- 在少量硬件的支持下,我们就可以将系统扩展到更高访问量级。 \
- 我们可以根据实际需要,在不修改软件系统的前提下增加硬件。 \
- 我们的五人开发团队在站点早期成长过程中可以同时完成开发和管理工作。 \
- 我们可以解决未来可能出现的扩展性和性能问题。 \
就上述几个方面而言,对我们来说最重要的还是生产力。我的团队必须走在竞争对手前面,以最快速度满足用户和投资人的需求。本文的剩下部分主要从技术角度介绍我们的一些做法,对于本文难以说清楚的问题,我会提供信息链接。
\拓扑结构
\首先,让我们对系统全貌有个大致了解。Rails应用大多采用LAMP(Linux+Apache+MySQL+PHP)架构。就此站点而言,我们使用了Sun的硬件,以F5 BigIP作负载均衡。和其他很多新兴Rails站点一样,我们的应用服务器是Mongrel(主要处理动态内容。和lighttpd 属同类产品),数据库自然是MySQL。我们的数据库按master/master/slave模式部署,以满足故障切换(Fail-Over)、性能和扩展性要求。
\我们选择了对大流量Rails站点运营有着丰富经验的Text Drive公司托管我们的内容。和其他互联网站点一样,我们主要是只读流量,写流量主要集中在几个社交网络个性化模板,以及用户购物历史上。我们还通过分析发现ActiveRecord模型需要优化,关键是要减少数据库请求次数。此外,我们还利用MemCache插件实现了缓存策略。除缓存层外,我们采用的都是Rails传统配置策略。
\每个捐赠者满怀热情来捐赠,他们常常会为赠品准备很多图片。接受捐赠的机构和组织也会从中挑选能最恰当展示赠品的图片,以激发访问者的兴趣。图片的价值无容质疑,通过图片的推介,可让赠品尽快到达最需要它们的人手中,从而增强捐赠者的信心和自豪感。但大量图片下载,无疑加大了服务器的负担。Mongrel的特长不在于静态内容,所以我们采用了URL重写(URL Rewriting)技术管理图片。目前,我们还正准备利用Panther Express实现图片缓存,加快访问速度。
\一些Java和.NET的开发者及相关厂商警告我们说Ruby on Rails可能无法满足我们在扩展性上的要求,但我们对此并不十分担心。我相信软件技术发展这么多年,已经积累了大量对Rails同样适用的经验教训;Rails的基石LAMP也是目前各类大型Web站点的主流架构准则。对我来说,更重要的问题是如何以最快的速度实现投资人、用户和老板的需求。现在,我更多的时间花在团队、工具和实现过程等方面。
\\Rails宣称:“我们敏捷,我们快乐。”对此我完全赞同,我热爱敏捷开发。我们热烈拥抱以下敏捷准则:
\- 我们专注于缩短开发周期,坚持周周迭代。我们至少每周发布一个新版本。 \
- 我们依靠开发人员边开发边编写测试用例,而不是漏斗式阶段测试保证质量。 \
- 我们不害怕以优化业务模型的系统重构。通过重构,我们提高了代码质量、需求理解准确度和系统灵活性。 \
- 对次级需求,我们实行周内管理;对重要需求,我们实行月度管理。 \
- 我们利用每周发布的Demo,与用户保持紧密联系。 \
我们利用Trac/SVN执行源代码原理、需求跟踪和发布计划管理。Trac和SVN的高度集成给我们的工作带来了极大方便。在重量级自动化测试和轻量级人工测试后,我们根据测试结果渐进式修改我们的产品,并对重要修订实施分支管理。使用Rails平台,通常不需要我们做大的修订。
\我们的团队成员分布在三个时区,不久可能更为分散。成员间可以随时开放交流,这样可以尽快解决问题。虽然要依靠长距离通讯,我还是希望我的团队里有更多的优秀Rails开发人员,而不是工作地虽近,但新手过多。到目前为止,这个决定的好处是显而易见的。我们还尽可能增加会议次数,缩短会议时间。我们每天通过Skype举行例会,每周召集市场人员参加的技术会议。我们还坚持每周公布阶段性计划。
\有人说Rails对复杂解决方案的扩展支持能力不够,我不赞成这种说法。和同类技术相比,Rails的生产力是最高的。Rails对我们的开发过程、团队组建和管理都产生了戏剧性的影响。所用技术的生产力越低,迭代周期就越长;团队越大,花在检查和协调上的时间就越多——在Ruby和Java项目管理我都经历过后,更为支持这个观点。使用别的技术,就需要组建更大的团队(比如10到15人),而我们则可以保持团队最小化。我们目前有5个Ruby开发人员,最多只需要其中两个人专门负责HTML设计。以后可能增加到7人更为合适,那时,我们将拥有第二个开发小组,专门负责复杂和重点问题的解决。
\工具
\我们使用的工具超级简单,比如代码覆盖测试使用RCov,集成测试使用Selenium,驱动所有的测试和覆盖工具的则是Rake。我们的测试用例覆盖率一般在85%左右,特别是严格跟踪覆盖率后,基本保持在79%到90%的范围。因为我们经常修改用户接口,所以Selenium的使用情况不太理想。但我想用户接口逐渐固定下来后,成功率会慢慢提高的。目前,我们主要进行的是单元和功能测试,但在其他测试方法探索方面也取得了较大进步。
\我们的开发人员可以自由选择编码工具,实际上到目前为止,我们没有任何人从头至尾使用过集成开发环境。我们主要使用的源代码编辑工具是TextMate。包含了日志和测试功能的Rails基准和调试工具已经有足够能力帮助我们完成系统调试。重构有点伤脑筋,特别是必须修改数据库模型的时候,因此一旦发现有足够好的IDE,我们还是会考虑使用的。不过,就我的经验而言,我觉得我们现在的维护速度可以达到Java应用的三倍,甚至更快。
\核心架构
\尽管在项目方案的选择上,我们最初显得有点儿激进,但现在最重要的是一如既往、继续坚持。我们的核心架构仍将以Rails为中心。在数据库端,我们是DHH(译者注:Rails作者David Heinemeier Hansson的简称)的忠实信徒;不强行要求引用完整性等数据库约束,也没有使用数据库视图。在测试方面,我们主要依靠Rails框架;当然,随着测试用例规模的增大,我们也在积极尝试其他工具。不少方面都还不到最后定论的时候,仍在探索和变化之中。但总体而言,我们使用的是REST WebService支持下的经典Rails MVC模型。下文对此还将详述。
\模型
\和很多网络社区站点一样,首先对读操作做了优化,因为我们的大部分内容都是静态的,平均而言每天修改不到一次。通过分析竞争对手的统计数据,我们发现,要想提高系统容量,对稳定内容的缓存非常重要,像慈善组织(如UNICEF。译者注:United Nations Children's Fund,联合国儿童基金会)、赠品(如癌症研究者的一小时时间)及机构使命(如世界和平、医学研究等)。这些内容不会频繁变化,可以稳定缓存。我们使用Rails插件ActsAsCacheable实现了静态内容的缓存。其后端部署了分布式的二级缓存服务(MemCache),但在特殊情况下,我们绝大多数时候会直接使用ActiveRecord API。
\此站点也包含工作流机制。比如,慈善机构提交了某项内容,ChangingThePresent的管理员将负责内容的审查和批准。我们必须提供可在任何时候将任何赠品激活和冻结的功能;而且我们更喜欢将记录标记为删除状态,而不是直接从数据库删除。为了支持这种自定义的工作流,我们引入了一个叫做ActsAsStateMachine的插件。此插件支持使用DSL(Domain Specific Language)表述状态机,有关DSL的详细资料请参看:跨越边界:Rails插件。
\知道DSL被用于和我们的商业用户交流,我才认识到DSL的强大。Ruth Ann Hacking负责站点内容搜集与管理。虽然她没有编程经验,却是一个参加过世界杯的击剑手。因为她随时可以用军刀(甚至一支圆珠笔)杀死我,所以我整天想法设法给她找乐子。一次兴起,我将包含状态机的代码给了她。结果出人意料,她居然将代码从头到尾看完了,还找出了几个Bug!Ruth Ann高兴了,我也活到了现在。这就是和Rails在一起的生活,它的框架里总隐藏着一些小东西,时不时跳出来逗我开心开心。
\用户接口
\我们的用户接口构建于经典的Rails控制器-视图-布局(Controller-View-Layout)模式基础上。现在还不支持新的RESTful控制器,但会在以后考虑增加。我们只在少数地方使用了AJAX——如贺卡生成向导和现场编辑。总而言之,我们的目标是让用户接口尽可能简洁明了。为了提高站点的可扩展性,我们采取了如下措施:
\- 尽量少用图片。比如站点中有很多圆角,我们都是用JavaScript库实现渲染。当然,不久实现对PantherExpress的支持后,这一策略将有所改变。 \
- 减少页面缓存。在我们的站点中,用户自定义的东西很多,所以静态页面很少,页面缓存的价值不大。目前缓存主要实现在ActiveRecord层面,另外使用PantherExperss实现图片缓存。 \
- 我们最开始以翻页方式,按名称首字母排序向用户展示赠品。但不久发现所有用户都只能从A开头命名的赠品开始浏览体验,于是修改为随机排序方式,并每天缓存结果。我们没有通过视图实现这个功能,而是利用MySQL随机函数设计了一个用户自定义的、以当前时间为随机种子的搜索器。 \
未来计划
\新功能在不断增加,特别是12月份上线以来。我们设计了新的控制台,方便慈善机构查阅变更历史;强化了用户自组织的社区特性;重新设计了首页;不久后将发布博客和婚礼登记系统。另外,还要加强内容管理功能。
\我们的项目管理团队,将继续保持小型化、地理分布化。我们将继续在Rails和LAMP方向上投资。现在,我们的站点已经稳定运行了,接下来将在社区化和流量优化上多下功夫。我们也清楚知道技术仅仅是整个问题的一个小部分。另外,我们还将吸引更多慈善机构,积极参与站点活动,逐步建设ChangingThePresent品牌。
\期待你的帮助
\Ruby社区已经多次无私地支持我们。我们会将所有捐赠品都转交慈善机构,收取的仅仅是信用卡使用费。因此,我们需要大家的支持。如果你愿意帮助我们,以下任何一件事情,都可以让我们得到你的帮助:
\- 登录我们的站点,点击链接“Support Changing The Present”。 \
- 在你的博客上放上支持我们站点的标语。 \
- 将我们的站点告知你的五位朋友,并请他们也向其朋友宣传。 \
- 我们需要部分设计师、Flash和Ruby开发人员。如果你对技术工作感兴趣,请通过InfoQ联系我。 \
不是每一天,我们都有机会从事一份自己热爱、和彼此欣赏的人在一起并且让自己觉得意义非凡的工作。我希望你访问这个站点的热情能和我们设计它时一样高。
\作者简介:Bruce Tate,皮艇和登山爱好者,育有二子。 ChangingThePresent.org所属公司WellGood LLC的CTO。著书9本,包括《From Java to Ruby》、《Rails Up and Running》和《Beyond Java》等。\
译者简介:罗小平,上海某大型公司互联网中心技术总监,CSDN大版主,网络ID为lxpbuaa(桂枝香在故国晚秋),曾著有《Delphi精要》一书。个人博客为http://blog.csdn.net/lxpbuaa,现在CSDN主持翻译国外专家Herb Sutter的中文博客。他的Email和MSN为lxpbuaa AT 263.net。