【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

Ruby on Rails 案例学习:ChangingThePresent.org

  • 2007-04-17
  • 本文字数:5604 字

    阅读完需:约 18 分钟

五个月前,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

2007-04-17 20:474515
用户头像

发布了 26 篇内容, 共 76260 次阅读, 收获喜欢 2 次。

关注

评论

发布
暂无评论
发现更多内容

Go 专栏|错误处理:defer,panic 和 recover

AlwaysBeta

Go 语言

【Vue2.x 源码学习】第四十三篇 - 组件部分 - 组件相关流程总结

Brave

源码 vue2 9月日更

【Flutter 专题】58 图解 Flutter 嵌入原生 AndroidView 小尝试

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 9月日更

如何设计企业特色的数字化转型架构?

博文视点Broadview

线程同步类CyclicBarrier在性能测试集合点应用

FunTester

多线程 性能测试 线程安全 测试框架 FunTester

Linux内核四大核心框架

hanaper

(深入篇)漫游语音识别技术—带你走进语音识别技术的世界

声网

深度学习 音视频 语音识别

Linux之lastlog命令

入门小站

Linux

架构学习模块一

George

柯基数据通过Rainbond完成云原生改造,实现离线持续交付客户

北京好雨科技有限公司

云原生 需求落地 离线部署 可持续交付

Go 专栏|复合数据类型:字典 map 和 结构体 struct

AlwaysBeta

Go 语言

在线JSON转JAVA工具

入门小站

工具

Electron团队为什么要干掉remote模块

刘晓伦

Electron Node

升级mysql-connector-java-8.x踩坑纪实

小江

Java MySQL 时间戳 服务器时区 夏令时

LeetCode刷题278-简单-第一个错误版本

ベ布小禅

9月日更

Go 专栏|复合数据类型:数组和切片 slice

AlwaysBeta

Go 语言

Go 专栏|流程控制,一网打尽

AlwaysBeta

Go 语言

Go 专栏|接口 interface

AlwaysBeta

Go 语言

从一个并发异常问题引起的想法

卢卡多多

并发编程 9月日更

华为云PB级数据库GaussDB(for Redis)揭秘:如何搞定推荐系统存储难题

华为云开发者联盟

数据库 推荐系统 存储 华为云 GaussDB(for Redis)

Go 专栏|基础数据类型:整数、浮点数、复数、布尔值和字符串

AlwaysBeta

Go 语言

Vue进阶(九十):过滤器

No Silver Bullet

Vue 9月日更

【LeetCode】 二叉树中和为某一值的路径Java题解

Albert

算法 LeetCode 9月日更

Go 专栏|变量和常量的声明与赋值

AlwaysBeta

Go 语言

【报名】飞桨中国行丨企业零门槛AI创新应用-智能制造专场

百度大脑

人工智能

HTTP系列之:HTTP中的cookies

程序那些事

Java 网络协议 HTTP cookies

模块(二)如何设计架构

我是一只小小鸟

Go 专栏|函数那些事

AlwaysBeta

Go 语言

Go 专栏|说说方法

AlwaysBeta

Go 语言

多线程知识体系01-线程池源码阅读讲解-Executor

小马哥

多线程 高并发 源码阅读 源码剖析 日更

就靠这一篇文章,我就弄懂了 Python Django 的 django-admin 命令行工具集

梦想橡皮擦

9月日更

Ruby on Rails案例学习:ChangingThePresent.org_Ruby_Bruce Tate_InfoQ精选文章