OpenFeint 的系统迁移之路

  • 丁雪丰

2012 年 11 月 17 日

话题:RubyRuby on Rails性能调优语言 & 开发架构

在今天举行的RubyConfChina 2012 大会上,来自 OpenFeint 的黄志敏(@flyerhzm)在下午的第一个演讲中为大家带来了 OpenFeint 平台从 REE 向 JRuby 迁移的经验,向大家展示了 JRuby 带来的巨大性能提升,同时就其中遇到的一些问题进行了分享。

开场时,黄志敏就抛出了一个令人振奋的数据:

JRuby 给 OpenFeint 带来了 40% 的性能提升!

OpenFeint 是最大的移动社交游戏平台,每分钟有几十万 API 调用,由于起步时间较早,之前一直运行在 REE 1.8.7 + Rails 2.3.14 的环境之下。为了解决性能问题,他们做了一些方案,其中包含向 JRuby 迁移,但是由于大多数开发者都没有 Java 平台的经验,所以这一项并没有被提上较高的优先级。在美国的 RailsConf 2012 上,大多数人都抱怨 Ruby 的 GC 性能很差,在 Real World Rails Apps at Massive Scale 的讨论中提到了 Ruby GC 占用了大量的系统资源,而在其他两个演讲中讲到了 JRuby 带来种种好处,于是 OpenFeint 决定开始他们的迁移之路。

在开始前,他们做了一个简单的快速性能测试,在一台测试服务器上(memcached、redis 和 rabbitmq 等软件都运行在一台机器里),禁用了 mysql sharding、后台任务和其他无法工作的部分,分别运行 REE 1.8.7 2010.01(GC 未优化) + Passenger 和 JRuby 1.6.7.2 + Torquebox 接受请求,前者响应为 331ms,而后者只需 51.5ms。虽然这个测试里有很多不足的地方,但是其结果的确鼓舞人心。

OpenFeint 的系统栈里主要有以下部分组成:

  1. 负载均衡
  2. Web / App Servers
  3. Mysql
  4. Memcached
  5. Redis
  6. RabbitMQ
  7. 其他部分

迁移工作主要是将 App Servers 从 Passenger 迁移到 Torquebox,迁移时希望能尽可能减少人力投入。App Servers 根据功能分为多个池,迁移时逐个进行迁移,在每个池里先部署一台。这种做法能更快地享受到 JRuby 带来的性能提升,能更快地积累经验,但是必须要保证代码能同时运行在 REE 和 JRuby 下,带来了一定的运维复杂度。

虽然 JRuby 与 CRuby 在语法上没有什么不同,但是两者仍然存在一些不兼容的地方,首先就是要逐步替换不兼容的 gem:

  • yajl-ruby 和 hiredis,这两个 gem 使用了 C 扩展,在 JRuby 下 C 扩展的使用还不理想
  • 替换了 mysql 的驱动
  • typhoeus 在大压力下,很快就会导致 JVM 崩溃,因此将它换为了 net-http-persistent
  • memcached 的客户端被替换为 jruby-memcached,必须保证 REE 和 JRuby 下一致性 Hash 算法能保持一致,所以没有选择其他方案

出于性能考虑,还要开启 JRuby 的 threadsafe 特性。JRuby 往往需要比 CRuby 更多的内存,因此还需要设置 JRUBY-OPTS,调整 JVM 堆等一些配置,比如将 Xmx 从默认的 512M 提高到 2G,提供更大的 CodeCache。还有一些问题也会造成性能下降,例如ActiveRecord 2.3.14 是线程不安全的,大并发下容易造成死锁,导致吞吐量下降

他建议在开发和测试环境中使用 CRuby,在持续集成与线上环境使用 JRuby。目前有以下一些可选的 JRuby 部署环境:

  • Trinidad,基于 Tomcat
  • Torquebox,基于 JBoss AS(运行速度更快,有团队进行维护)
  • Mizuno,基于 Jetty
  • Puma,这是一个新的 Ruby Server(Rack 版本很高)

最后,OpenFeint 选择了 Torquebox-lite,这是一个仅包含 Web 模块的 Torquebox 精简版,可以通过配置进行扩展。Torquebox-lite 也像 Passenger 一样,可以进行热部署,只需简单的一句 touch 就可以了,但是每次热部署时会造成一定的停顿时间,而且每次热部署后非堆内存都会增加,所以最后他们还是选择了逐台重启服务的策略。

在经过了一些的调整之后,OpenFeint 的同学们又进行了一次较为正式的性能测试,在线上的备机上进行测试,结果毫无悬念是 JRuby 胜出。对比 Ruby 1.9.3p194 + Passenger 和 JRuby 1.7.0RC2 + Torquebox-lite,读操作耗时分别为 374ms 和 187ms,写操作耗时分别为 42ms 和 38ms。整体的性能提升在 40% 左右,不同的应用服务器池情况稍有不同,但总体上都有 30% 以上的提升。在使用过程中,他们禁用了 JRuby 1.7 的 invokedynamic 特性,原因是开启该特性后 Torquebox 无法正常运行,相信在修正这个问题后,启用该特性会有更大的性能提升。

一个运行生产环境的系统自然少不了监控,在使用 REE 时,OpenFeint 的监控主要用 NewRelic 和 Scout,迁移到 JRuby 之后,还是使用 NewRelic,Scout 需要加上 JMX Monitoring Plugin 插件。此外,JRuby 的监控还有一些工具,比如命令行下的 jstat、jstack 和 jmap,图形化的 jconsole 和 visualvm。黄志敏还介绍了几个他自己的问题排查经历,就是使用这些工具进行的。

JRuby 最近刚发布了 1.7.0 正式版,相信随着 JRuby 的不断发展,以及 Java 平台的不断升级,JRuby 将会被越来越多的人用于生产环境之中,有兴趣的读者可以访问JRuby 的官方网站了解进一步的信息。


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

RubyRuby on Rails性能调优语言 & 开发架构