找回密码
 骑士注册

QQ登录

微博登录

搜索
❏ 站外平台:

Linux中国开源社区 观点 查看内容

Ruby社区应该去Rails化了

2013-06-05 08:30       

Rails加载的框架和依赖库过多,内存消耗过度

Rails自身依赖库非常多,造成的结果就是Rails应用持续运行以后内存消耗非常高。举个例子:如果你用到了Rails的asset pipeline功能,那么项目需要依赖一个JS引擎来编译JS和CSS,默认会使用libv8这个库。尽管只是编译阶段使用libv8,运行期并不需要它,但是仍然会加载libv8,这意味着你的每个ruby进程会多占20MB内存。在我们其中一个大项目上,总共开了40个ruby进程,直接浪费了800MB内存。于是我们不得不在生产服务器上安装了node.js,替换了libv8。

此外,一旦其中某个依赖库有内存泄露,整个应用也可能出现内存泄露,这种内存泄露是很讨厌的事情,Rails如此肆无忌惮不加限制的使用第三方依赖库也是一个潜在的隐患。

最后,Rails的Restful路由也是内存消耗大户,它默认会生成全套的URL路由helpers,无论你实际是否使用到,造成的结果就是内存会消耗很多,而且URL路由请求的处理速度会很慢,以致于有第三方专门开发了插件去关闭无用的路由。

我做了一个稍完整的案例比较,分别使用Sinatra, Padrino和Rails框架开发一个简单的数据库CRUD应用,数据库访问都是用ActiveRecord,在我的iMac电脑上,3个ruby应用单进程消耗的内存分别是:

Web框架物理内存
Sinatra 45MB
Padrino 60MB
Rails 85MB

Rails传统多进程模型的IO并发能力很低

Rails的多进程并发模型的IO并发能力很低,开多少个进程,就只能同时响应多少个并发请求,但Ruby进程的内存消耗是很大的,多进程调度的CPU开销也很高,这决定了单台服务器上能开的进程数是非常有限的,一般不会超过30个。但是对于Web Service类型的应用,需要很高的IO并发处理能力,传统Rails多进程很容易就会出现负载的瓶颈。

提高Web应用的IO并发能力,必须抛弃多进程模型,改用多线程模型,纤程模型或者事件驱动的并发编程模型。关于这个话题,我写过一个ppt,请参考:Web并发编程模型的粗浅探讨 ,这篇文章不展开了。总之,我个人更推荐使用Sinatra/Padrino编写多线程的Web服务端应用,或者为了追求更高的并发性能,可以使用Goliath的纤程并发。

从Rails4.0开始,默认也开启了多线程模式,也可以支持多线程方式运行Rails应用。但就目前来说,Rails使用多线程,还面临一些兼容性问题:大量的Rails插件和代码不是线程安全的,在多线程模型下运行,会出现意想不到的bug;另外Rails的多线程应用尚未得到广泛应用,可能会有潜在的bug:

我们尝试在一个实际的生产系统上打开Rails3.2的多线程模式运行,对代码和插件都进行了兼容性修改和仔细的代码审查。但实际跑下来发现,应用系统出现了隐蔽的内存泄露问题,Ruby进程内存会一直增长下去,直到服务器内存占满,进程失去响应,这个bug至今未能找到原因。

总之Rails适合开发Website,但不太适合Web Service,而移动时代的发展趋势就是:未来服务器端会更多的使用Web Service而不是Website,这也意味着Rails将越来越不适合时代的发展

我们应该用什么Ruby框架?

我一直觉得Ruby社区的很多开发者长期以来待在Rails的舒适区里面,完全丧失了探索和尝试其他东西的勇气,其实在Rails的世界之外,Ruby社区的好东西还有很多很多。这里简单介绍3个Ruby轻量级框架,性能都远远超过Rails,很适合做Web Service:

  • Sinatra

    Sinatra本身也是Ruby社区非常流行和著名的轻量级Web框架,核心源代码不超过1000行,文档只有1页。对于Rails开发者来说,花了几个小时,就可以快速使用Sinatra开发Web Service了。Sinatra对多线程支持的非常好,可以用rainbows来跑多线程Sinatra,IO并发处理能力很好。Github也是用它来提供开放API服务的。我自己写了一个Sinatra的项目模版,如果你用Sinatra开发Web Service,可以参考。

  • Padrino

    Padrino是一个基于Sinatra之上的轻量级Web框架,在Sinatra基础之上提供了命名路由,模块化项目组织,页面helpers和generators等等。Padrino是一个高度模仿Rails的框架,API的命名和Rails很像,Rails开发者花1-2天看看文档就可以快速上手开发了。Padrino相比Rails易学易用,多线程支持良好,性能比Rails好很多,开发Website推荐使用。我自己的网站也是用Padrino开发的,源代码在:robbin_site

  • Goliath

    Goliath是一个Ruby的纤程开发框架,性能非常好,作者本身是在开发PostRank产品过程中开发的Goliath。PostRank是一个用户社交行为实时跟踪工具,需要很高的性能来支撑,PostRank被Google收购了,作者现在在Google工作。Goliath适合用来开发对性能非常敏感的Web Service或者real-time的应用,但使用Goliath有一些门槛,你不能使用普通的阻塞IO库,必须使用作者封装的一些纤程的库。

总之,无论是Linkedin的移动API网关还是Iron.io的后台任务系统,用Ruby来编写,本身并不是问题,实践也有大量案例证明使用Goliath或者Sinatra编写高性能Web Service都是可行的。问题只是在于我们应该: Ruby off rails 了。

如何去Rails化

掌握一门编程语言实际上包括两部分:

  • 编程语言语法以及核心类库

    无论你用不用Rails,是否开发Web应用,这些都是必须牢固掌握的,即使你不用Rails了,这些知识和技能也不会过时。

  • 开发特定领域应用所需要的第三方类库

    当你用Rails开发一个项目的时候,仍然需要依赖大量的第三方类库,每当你在Gemfile里面require一个类库的时候,都意味着你付出了一定的学习成本。而Rails本身也不过就是几个核心Gem包而已,具体来说最核心的就是ActiveRecord和ActionPack这两个库。

学习Rails无非意味着你花了时间熟悉ActiveRecord和ActionPack以及相关库的功能而已,所谓去Rails化也仅仅只是放弃使用ActionPack,换一个更轻量级更简单的URL路由处理器,例如换成Grape,Sinatra,Padrino或者Camping而已。这对一个长期使用Rails的Ruby开发者来说,应该是举手之劳的事情。所以自己动手,根据实际应用场景挑选最合适的组件。例如ActionPack不太适合写Web Service,那我换成Sinatra就行了,但是ActiveRecord照常用,这并不需要你付出多少学习成本,更不需要你放弃什么。

为何不用node.js和Go?

有一点是毫无疑问的,node.js的V8引擎,Go的静态编译进去的GC,性能远远好于Ruby的虚拟机,尽管在实际的应用中,未必会表现出来这么明显的差距。那么,一个随之而来的问题就是:为何不用node.js和Go呢?

每个程序员都有自己的倾向性,答案可能都不同。我在去年底花了很多时间了解node.js和Go,最终还是觉得用Ruby对我来说最合适:

  • 用Sinatra或者Goliath这样的轻量级框架写Web Service,性能已经足够好了,特别是@黄志敏的案例证明,16核已经可以支撑每天1.5亿次请求了,对我来说已经不太可能遇到超过这个负载量的应用了。而Ruby的开发效率,代码表达能力和可维护性对我来说还是很重要的。

  • node.js的Event IO编程风格在我看来是“反人类”的,极其变态的。用来写代码上规模的应用,代码的可读性和可维护性都很差。Event IO是很底层的技术,我很难理解为何不封装成coroutine来使用。node.js只适合用来开发real-time类型的应用。

  • Go的主要问题在于现阶段还不成熟:一方面Go自身还在演进当中;另一方面Go的类库还是过于贫瘠了,用来开发项目还是需要自己写很多东西的,感觉很不方便。

via http://robbinfan.com/blog/40/ruby-off-rails 

12
查看其它分页:

最新评论

我也要发表评论

收藏

返回顶部

分享到微信

打开微信,点击顶部的“╋”,
使用“扫一扫”将网页分享至微信。