程序员0806期敏捷与性能的博弈

3
99 2008 06 起源 2007 年元旦P1.cn 创始人通过我的 Blog 与我取得 联系详细交谈了关于使用 Ruby on Rails 建设一个高 端社交网格平台的计划并在这年 3 从瑞典回到北 我也从南京到北京开始了网站建设和团队建设的征 ⋯⋯ P1.cn 作为最新最有趣也是最时尚的社交平台以满足城市年轻消费人群新生活为宗旨成员可以 通过网站结识拥有相同兴趣的朋友可以下载照片以分享最新的想法或者通过 Blog 和大家分享私人的生 活体验网站的所有内容以北京上海深圳和广州四大城 市的潮流生活为主你可以任意选择自己的城市也可以 看看其他城市的人们都在想什么看什么吃什么什么P1 的另外一大特色是我们的网络杂志每个星期四大城市的 P1 特约记者都会向网站提供有最流行本地化最有趣的资讯本篇文章我想从敏捷和性能两方面结合这一年时间 以来 P1.cn 网站建设实践和大家做一分享敏捷性 为了更好的让读者体验到 Ruby on Rails 开发的敏捷 在此我简单列一下 P1.cn 开发上线的里程碑 2007.03.01 开始讨论项目需求 2007.03.15 确定详细项目需求 2007.04.12 项目 Beta 上线 2007.09.22 项目 Gamma 上线增加我的内容功能 博客相册2007.09.28 P1.cn 上线正式启用 P1.cn 域名 以下是团队成员到位的时间表 2007.03.01 本文作者使用Ruby on Rails 开始项目开发 2007.05.21 Benson 加入开发团队职位是 Ruby on Rails 程序员 2007.06.01 Yan 加入开发团队职位是 CSS 设计 2007.12.20 Geoffery 加入开发团队职位是 Ruby on Rails 程序员 从上述时间列表中不难看出 Ruby on Rails 技术的 敏捷性Idea 的形成到网站 Beta 上线运行一名 Ruby on Rails 程序员只用了 4 周的时间从项目 Beta 上线至今我们一直持续完善网站的各 项功能和对服务器架构做出调整以适应不同阶段的性能 要求初始阶段 对于一个start-up 项目在项目开发的初始阶段我们把 重点放在了功能的完成上最大限度的利用了Rails 的特性快速的完成项目在最初的两个月所有服务运行在一台 服务器上服务器硬件配制为Q2.0GHz, 4GB RAM, SCSI RAID-1运行的服务为 Kernel 2.6.22-3-amd64 #1 SMP, Apache2, MySQL5.0.45, Ruby1.8.6, Ruby-MySQL2.7, Rails 1.2.3, Mongrel 1.1.4. 由于刚上线的网站没有什么访问 压力运行状态相当不错随着业务的开展注册会员逐 渐增多考虑到服务器的性能和用户数据安全性我们在 2007 7 月增加了两台服务器MySQL 独立运行在一台 服务器上Mongrel 运行在一个服务器上另外一个服务器 作为备份这样的系统架构持续到了2007 年年底性能问题 2008 1 月份有用户抱怨网站在一些时间非常 几乎不能提供服务虽然在一开始我们就做好了迎 Rails 性能问题的准备可是当问题的出现时还是觉 得它来得太快因为这时我们的网站的日均 PV 流量大约 5 万左右这个数据并不高导致问题出现的原因是 Mongrel 挂死了重新启动 Mongrel 才能恢复服务使用 mongrel_proctitle 插件查看 Mongrel 进程发现当前的访 问请求数目超过了 20 这时我们知道解决 Rails 性能 问题刻不容缓敏捷与性能的博弈 / 蔡望勤 Ruby on Rails Web development

Upload: jesse-cai

Post on 26-May-2015

1.074 views

Category:

Documents


0 download

DESCRIPTION

《程序员》2008年06期约稿 敏捷与性能的博弈 -Ruby on Rails Web Development

TRANSCRIPT

Page 1: 程序员0806期敏捷与性能的博弈

98 99程序员 2008 0698 99程序员 2008 06

起源2007年元旦,P1.cn 创始人通过我的Blog与我取得

联系,详细交谈了关于使用Ruby on Rails建设一个高端社交网格平台的计划。并在这年3月,从瑞典回到北京,我也从南京到北京,开始了网站建设和团队建设的征

途⋯⋯

P1.cn作为最新、最有趣也是最时尚的社交平台,它以满足城市年轻消费人群“新生活”为宗旨。成员可以

通过网站结识拥有相同兴趣的朋友,可以下载照片,可

以分享最新的想法,或者通过Blog和大家分享私人的生活体验。

网站的所有内容,以北京、上海、深圳和广州四大城

市的潮流生活为主,你可以任意选择自己的城市,也可以

看看其他城市的人们,都在想什么,看什么,吃什么,玩

什么!

P1的另外一大特色是我们的网络杂志。每个星期,四大城市的P1特约记者,都会向网站提供有最流行、最本地化、最有趣的资讯。

本篇文章我想从敏捷和性能两方面,结合这一年时间

以来 P1.cn 网站建设实践,和大家做一分享。

敏捷性为了更好的让读者体验到Ruby on Rails开发的敏捷

性,在此我简单列一下P1.cn开发上线的里程碑:2007.03.01 开始讨论项目需求2007.03.15 确定详细项目需求2007.04.12 项目Beta上线2007.09.22 项目Gamma上线,增加我的内容功能(文

章/博客/相册)

2007.09.28 P1.cn上线,正式启用P1.cn域名以下是团队成员到位的时间表:

2007.03.01本文作者使用Ruby on Rails开始项目开发2007.05.21 Benson 加入开发团队,职位是Ruby on

Rails程序员

2007.06.01 Yan 加入开发团队,职位是CSS设计2007.12.20 Geoffery 加入开发团队,职位是Ruby on

Rails程序员从上述时间列表中,不难看出Ruby on Rails技术的

敏捷性。从 Idea的形成,到网站Beta上线运行,一名Ruby on Rails程序员只用了4周的时间。

从项目Beta上线至今,我们一直持续完善网站的各项功能,和对服务器架构做出调整以适应不同阶段的性能

要求。

初始阶段对于一个start-up项目,在项目开发的初始阶段我们把

重点放在了功能的完成上,最大限度的利用了Rails的特性,快速的完成项目。在最初的两个月,所有服务运行在一台

服务器上。服务器硬件配制为Q2.0GHz, 4GB RAM, SCSI RAID-1。运行的服务为Kernel 2.6.22-3-amd64 #1 SMP, Apache2, MySQL5.0.45, Ruby1.8.6, Ruby-MySQL2.7, Rails 1.2.3, Mongrel 1.1.4. 由于刚上线的网站没有什么访问压力,运行状态相当不错。随着业务的开展,注册会员逐

渐增多,考虑到服务器的性能和用户数据安全性,我们在

2007年7月增加了两台服务器。将MySQL独立运行在一台服务器上,Mongrel运行在一个服务器上,另外一个服务器作为备份。这样的系统架构持续到了2007年年底。

性能问题在2008年1月份,有用户抱怨网站在一些时间非常

慢,几乎不能提供服务。虽然在一开始我们就做好了迎

接Rails性能问题的准备,可是当问题的出现时,还是觉得它来得太快。因为这时我们的网站的日均PV流量大约在5万左右,这个数据并不高。导致问题出现的原因是Mongrel挂死了,重新启动Mongrel才能恢复服务。使用mongrel_proctitle插件查看Mongrel进程,发现当前的访问请求数目超过了20个。这时我们知道,解决Rails性能问题刻不容缓。

敏捷与性能的博弈

■文 / 蔡望勤

—Ruby on Rails Web development

Page 2: 程序员0806期敏捷与性能的博弈

Technology

技术

100 101程序员 2008 06100 101程序员 2008 06

紧急方案1. 调整mongrel进程的个数,从原来的40个减少

到20个,每个mongrel进度大约占用150M RAM, 并不是mongrel启动的越多,服务能力越强。

2. 使 用 monit/god监 控 mongrel进 程, 当 发 现mongrel占用内存过大时重启该进程。实践证明,这不能解决任何问题,当mongrel出现挂死的情况时,重启后,由于访问队列很大,又马上会被挂死。

3. 优化MySQL配置文件。4. 启用MySQLslow query log, 根据每一个具体的

查询语句优化索引,重写耗时的MySQL查询语句。减少 SQL查询的数量,有时候 include和 select都不够用,我们放弃了很多以前漂亮的association的写法,用find_by_sql代替,效果很明显。

5. 重构代码,去掉部分性能及差的插件,如acts_as_commentable。

6. 根据不同的业务逻辑,使用 action cache和fragment cache加快访问速度。

7. 使用memcached存贮sessions。8. 将原来在两台计算机上运行的Memcached

服务更改为在一台服务器上运行,Cache 附加数据在memcached中。经过实践,Memcached服务运行在一台服务器上的速度远远快于运行在两台服务器上。

9. 将Apache2替换成Nginx 0.5.35。这时,我们的服务器架构如下图,其中Nginx,

Memcached和部分mongrels运行在server 1上,MySQL和部分mongrels运行在server 2上:

在这样的架构下,服务仅持续运行了1周,Mongrel又遇到了同样的挂死问题。我们从production log中看到以下错误信息:

(ActiveRecord::StatementInvalid) "Mysql::Error: Lock wait timeout exceeded; try restarting transaction: UPDATE some_table.....

经过分析,这个错误是由于ActiveRecord的一个

transaction执行太长时间,超过了MySQL所定义的innodb_lock_wait_timeout时间。

ActiveRecord 的 :after_update, :after_destroy 这些 callback是在数据写入数据库之前执行的。这是由于ActiveRecord 的 Built-in Transactions。为了保证数据更新操作的完整性,ActiveRecord缺省将 destroy和 save操作都包裹在一个 transaction中(详见 activerecord/lib/transaction.rb),而整个 filter chain 也被包含了进来,也就是说,after_update和after_destroy都是在 transaction提交前发生的。在 transaction commit成功返回之前,所有的数据库操作并没有真正在服务器端完成。

而我们使用了 action_cache插件,在 after_update和after_destroy时都要执行expire_action方法,其中的expire_action是从磁盘上删除和一个正则表达式匹配的缓存文件,这些文件是存在RAID-5硬盘上的,当更新一条记录时,从大量的文件中删除缓存文件花费太长的时间,

从而产生上述问题。我们找到了一个简单的解决方案,将

缓存文件放到 ramfs中解决这一问题:mount -t ramfs ramfs /home/yay/rails_app/current/

tmp/cache到这个阶段后,我们的网站能正常服务当前的访问

量了。市场增长要求我们的应用在未来6个月达到日PV百万的访问要求。拥有了上述经验后,我们了解到,使用

Swiftiply event mongrel和 fair proxy等部署优化措施,都只能很有限度的提升性能,根本的解决方案是优化程序代

码和增加服务器数量。

稳定服务要达到日PV百万的访问要求,从数字上计算,就是

每个Request要达到30 reqs/sec. 得出这个数据的方法是,网站一天主要的访问来自于12个小时,每小时3600 sec,12 x 3600 sec x 30 reqs/sec = 1296000 reqs.首先我们进一步在程序代码上做了如下的优化(并在

团队中使用此编码规范):

1. 数据库查询中只返回需要的数据,为 find方法的选项上增加 :select, :limit, :offset. Rails默认的 find方法返回所有字段,往往程序中不需要一条记录的所有字段数据,

这浪费了大量的带宽和数据库消耗。

2. 不使用 find_by_*,因为 find_by_columnname虽然很容易阅读,但是不利于性能。实际上ActiveRecord是在method_missing方法中动态生成这些方法的。直接使用 find_by_sql甚至 find都更有效。

3. 使用 find_by_sql执行优化的SQL查询。使用find的可读性要比 find_by_sql好很多,但在一些手动优化的SQL查询时,建议使用 find_by_sql的方式执行。

Page 3: 程序员0806期敏捷与性能的博弈

1012008 06

4. 在View层直接使用HTML标记。因为 link_to, url_for 这些helper方法并不比直接使用HTML标记减少多少代码,而却依赖Ruby去解释执行。

以下是我们做了代码优化后和之前的 railsbench对比图表:

同时,我们将Production环境增加至7台服务器,使每个服务器独立运行不同的服务。现在的系统构架图如下:

在应用部署上,因为缺少对Linux各项性能优化的经验,我们选择多使用几台价格便宜的服务器来运行服务。

对我们目前来讲,相对于测试和优化Linux/Ruby性能,这更经济有效。

后记从Ruby on Rails的诞生开始,性能一直是受到人们广

泛关注的。以致发生了Twitter.com 团队和 DHH 在Blog上彼此攻击的情况。Twitter.com 由于其创新的内容展示和发布形式,很快的受到了大家的关注,于是大量的访问者涌

入导致网站不能正常提供服务。Twittter团队人员在Blog上发布了Ruby on Rails性能不好的言论,而后DHH马上在

他的Blog上发表了“到底是Ruby on Rails性能不好,还是团队成员不够努力?”的文章。引起一次争论热潮。

前不久又有言论说 twitter.com已经放弃Ruby on Rails, 但 twitter团队成员伊万 威廉姆斯 (Evan Williams)表示Twitter目前没有计划放弃Ruby on Rails,但有大量的代码已经不是使用Ruby on Rails开发的。然而争论的存在,并不影响那些追求完善的程序员对

Ruby on Rails的热爱。因为不仅Ruby本身具有优雅的气质,而且Rails确实有她独特的魅力。Rake, Capistrano等管理工具极大了方便了对网站部署更新的操作,而且简

单好用,活跃的Rails社区提供了大量的Plugin可以直接使用,使网站开发变得像积木游戏一样有趣。

这里面要注意的是,从互联网上得到的数据和代码,

一定要经过自己的完整测试后,才能应用到自己的项目中。

在开始项目时,不要因为Ruby on Rails开发的快速,就过于频繁的修改项目需求,这样,她的敏捷性就变成项目

完成的最大障碍了。

使用TDD开发模式,测试还是不可缺少的部分。这个网站发布和升级流程,和使用PHP、.NET、Java是一样的。

随着网站用户和访问量的不断增大,Load Balance的使用,专职SA的配备显得尤其重要。这就使得Ruby on Rails团队建设和传统的软件团队建设有很大的区别。在不同的阶段合理扩展团队规模,而不是一直保留在三人

编程小组的阶段。虽然目前还没有多少大型的应用是使用

Ruby on Rails开发的,但是也挡不住人们使用这项技术进行网站建设的创新工作,毫无疑问,如果你要开始一个

网站项目,她是最棒的。

最后,要记住的是,在使用Ruby on Rails开始一个创业项目时,Ruby on Rails不断地要求程序员是全才,要求程序员具有全面的网站建设素质。Ruby on Rails框架本身也在不断地更新,一些新的技术不断涌现,如

Phusion Passenger mod_rails. 如果你富有激情,持续学习,和Ruby on Rails社区保持紧密联系,一切问题都将迎刃而解。■

作 者 简 介

蔡望勤 (Jesse Cai),湖北黄冈人。1997年开始接触计算机,对计算机编程情有独钟。 2005年底加入南京 UUZone,开始专注于研究 Ruby on Rails和Web2.0领域。现任亚艺网媒科技发展(北京)

有限公司 CTO。

■ 责任编辑:赵健平([email protected])