《程序员》2009 05期
Twitter将部分应用从Ruby迁移到了Scala。三位开发者详谈决策背后的因素、Scala在现实应用中遇到的困难以及Scala对编程风格的影响。
=■文,Bill Venners
Twitter是一个快速成长的微博客服务网站。它作为一个Ruby on Rails应用呱呱落地,现在依然用Ruby on Rails支撑着大部分面向用户的网页。但大约一年以前,他们开始将部分Ruby服务替换成用Scala编写、在JVM上运行的程序;来自Twitter的三位开发者——系统工程师Steve Jenson、API主管Alex Payne.服务团队成员Robey Pointer-与Bill Venners坐到一起讨论Scala在Twitter的实际应用。他们详述了运营中遇到的实际问题如何使他们开始考虑Scala.Scala在实践中又产生哪些问题,还会探讨Scala怎样影响了他们的编程风格。
Twitter简短回顾
Bill Venner:在Twitter的技术演变历程里,是什么因素令你开始考虑Scala?
Alex Payne:Twitter最初只是从事播客业务的ODEO公司里面私下鼓捣的项目。当ODEO遇到经营困难,他们开始允许工程师折腾各种想法。其中一位工程师Jack Dorsey对状态尤为感兴趣。他总是在IM上看到有人说“我在遛狗”,“我在做这个”,“我在干那个”,于是开始思考有什么办法能让人们更方便地交流各自的状态。他和另外几位工程师搭建的原型后来成了Twitter。原型沿用了ODEO 惯用的Ruby on Rails。直到今天Twitter都主要是一个Rails
应用加若干Ruby守护进程在后台完成异步处理。
后来我们发现虽然Rails做前台Web开发非常出色,但对重量级的后台处理来说.Rails在运行时有一些性能上的局限。我个人认为Ruby语言缺少一些有助于产生可靠的高性能代码的因素.而当我们的业务越做越大,这样的因素对我们来说非常重要。我们希望写出来的代码是正确的、可维护的。我们还希望维持低成本——和大多数企业对软件平台的期望没什么两样。所以我们开始盯上Scala。
我们看中Scala还有一个原因。虽然我们用Ruby遇到了问题,但依旧热爱Ruby语言的灵活性、喜欢它功能全面、也忘不了用Ruby编程的愉悦。很多Java开发者离开大公司之后都转而用Ruby编程,原因和我们一样,希望每天都过得愉快。我们不希望丢掉这些,投向莱种无趣的、一本正经的语言社群,比如C++。我们知道人们用C++写出非常高性能的代码,在座的Steve和Robey都有那方面的经验。可是我们希望能用一种真正令我们倾心的语言,而Scala令我们感觉值得赌一把。可靠的高性能代码
Bill Venners:我很好奇.Ruby粉丝也会希望你说清楚——能否详细解释对于编写可靠的高性能代码来说,你感觉Ruby缺少了什么?
Steve Jenson:在我的职业生涯中,一直觉得长生命期的进程是不能缺少的东西。而Ruby.像很多脚本语言一样,作为长生命期进程的环境会遇到麻烦。相反JVM非常适合,因为过去十年JVM都-直在为这个方面优化。所以Scala具备了编写长生命期服务的基础,而这也是目前它在Twitter的主要用途。Scala还有一样特质令我们爱不释手,就是静态类型,而且是无痛的静态类型。有时候在Ruby里面会很希望能写一些可选的类型标注,特别指
明在某个地方我们希望出现什么类型。Scala就能指定这样的类型信息.我们感觉特别有用。
Robey Polnter:另外Ruby还没有很好的线程支持。现在情况改善了,不过当初我们编写这些服务的时候,绿色线程是唯一的选择。绿色线程不用真正的操作系统内核线程,它们定期停下手上的工作,检查一下有没有别的“线程”要执行,用这样的办法去模拟。所以Ruby是在单核或者单处理器的范围内模拟线程。我们用多核服务器,而且服务器上的内存是有限的。在欠缺很好的线程支持的时候,就只能依靠多进程了。但因为Ruby的垃圾收集不像
Java那么完善,每个进程都要用掉很多内存。没有足够大的内存我们就没办法在单台机器上跑很多Ruby守护进程。当我们用JVM的时候,可以在堆里跑很多线程,还可以让
单个JVM进程占据机器上的全部内存。
Alex Payne:我要特别强调Steve刚才提到的“类型”。随着我们的系统越来越大.我们的Ruby代码里面有很多逻辑可以说是在模仿一个类型系统,单元测试或者模型验证的时候都容易出现那样的逻辑。我想这种情况可能是动态语言编写的大型系统与生俱来的——你最终不得不自己重写一个类型系统,而且很可能写不好。到处都要检查null值,到处都调用Ruby的kind_of?方法。“kind of?”相当于问“这个是User对象吗?不好意思,我们一定要那种的,不然程序会爆掉。”写这样的东西实在是太丢人了,在编程语言的世界里,分明几十年前就已经有了解决方案。用Scala补足Ruby
Steve Jenson:我们发现Ruby和Scala非常互补。我们把Ruby.具体说是Rails.用在它特别擅长的地方。前端的东西Rails做得很好。
Bill Venners:你们把Scala用在什么地方?
Robey PoInter:我们有一个基于Ruby的队列系统负责Rails前端和守护进程之间的通信,现在换成了用Scala重写的队列。在情况稳定的时候,原来的Ruby队列其实表现得还不错,只不过启动时间和崩溃行为不符合我们的期望。它慢了一点,内存消耗多了一点。有时候遇到流量高峰就撑不住了,而当它崩溃之后,要很长时间才能恢复。这可不行,我们需要能对付极端情况和高负载的方案,即使不像对付一般负载那么轻松,至少要相对容易。
Bill Venners:守护进程做些什么?
Robey Polnter:我们的架构有一个重要的出发点.就是让Rails做它所擅长的事.AJAX、Web前端、网站——总之就是用户看得见的东西。任何能脱离请求,响应循环的工作,我们都会把它卸下来,放进消息系统里面排队.让后端的守护进程去处理。
Steve Jenson:例如当你修改社会关系图的时候,也就是你在Twitter上“跟”或者“不跟”某个人的时候,这些工作以及相应的缓存更新都由守护进程异步完成。
Bill Venners:你们考虑过JRuby吗?
Alex Payne:考虑过。只不过当时试验的时候,没办法在JRuby上启动我们的Rails应用。我们用了大量要求C扩展的Ruby Gems.而当中很多都还没移植成JVM友好的版本。当时JRuby的性能还远不能与MRI( Ruby的C实现)相提并论,更没法与Scala这样的语言相比了。我们不排除将来会再次尝试JRuby.但目前只是希望给Ruby打些补丁能有帮助。Scala的权衡取舍
Bill Venners:你们已经有了Scala的实战经验,用它去解决实际的问题。你们认为它有什幺代价,有什么问题?好处在哪里,坏处在哪里?
Stwe Jenson:我认为Scala的效果很好。我们当中很多人都有过使用研究型语言编程的经验,而一般来说,当把研究型语言投入实际生产会遇到很多问题。可是我们用Scala的时候没有遇到太多这类困难.我知道在Actor和高可伸缩性方面遇到一点麻烦,不过已经解决了。总体来说,从我们的经验看.Scala是一个非常高性能、非常稳定的系统。
Robelt Pohar:我也同意到现在为止遇到的问题非常少。其中一些是由于语言和编译器都还很新。我们偶尔遇到一些令人困惑的编译错误,要花一些时间才能找出真正的错误。Scala有—部分核心集合库还不及格,当然现在他们正在改进;
Bill Venners:哪方面“不及格”?用不了?不够快?
Robey Polnter:我没有遇到用不了的情况,但有那么几个方法写得效率不高.API也存在一些缺口。有的时候我们干脆决定丢开它,直接在Scala里面用Java的集合库。这倒是Scala的一项优点,它有Java做后备。
Alex Payne;我首先做的一件事是给我们的API准备Scala测试平台。主要是包装Apache Commons HTTP库,还要提供代表系统中所有RESTNI资源的一组对象。最困难的地方是把Ruby思维换成Scala思维。要从更加函数式的角度去思考,还要更加从不变性的角度去思考,更何况我好几年没用过静态类型的思考方式了。所以对于Java背景不那么强,更偏向动态语言背景的人来说,过渡期可能长一点,不过只要过了那个阶段,结果是值得的。现在
我的默认思维已经用Scala取代了以前的Ruby。
Bill Venners:学习Scala如何改变你时编程的想法?
Robey Polnter:学习Scala之前,我没有比Python更强的函数式语言背景。我很熟悉Python。越深入学习Scala.我就越从函数式的角度去思考。一开始的时候,我会像写Python 一样用for表达式,现在我发现自己经常会用map或者直接在迭代子上调用foreach。
Alex Payne:我感觉从Actor的角度去思考并发绝对是一次思维转变。我有过一点用IO语言编程的经历,不过我感觉Scala的Actor更接近Erlang的模型,与IO差别更大一些,我非常喜欢Scala的实现。这是好的转变。
Stew Jenson:我来自Java背景,不过我也熟悉Common LISP和ML。能用自己熟悉的运行时,又能用函数式的组合子、闭包和高阶函数,这是美妙的事情。它们在Scala里面的表现我很满意。Scala的顾虑
Bill Venners:如果我打算在生产系统中使用Scala.有什么需要担心的呢?哪些事情是我·玉缅先准备好的?有什么要害怕的吗?
Alex Payne:目前Scala的IDE支持情况,应该说已经过了婴儿期,但还处在别扭的青春期。IntelliJ 8.1的Scala支持感觉相当不错。Scala自带的Emacs模式,缩进有些怪毛病。Textmate的支持很差劲,不过Scala工具邮件列表上已经在讨论怎么改进,IDE支持算是个门槛。
Robey Polnter:如果你不是从Java世界过来的,而是来自Ruby或者Python的世界,有可能受不了编译一部署的流程。你要配置构建环境,还要用很长的脚本去部署jar文件,与Ruby或者Python相比,这个世界差别很大。
Alex Payne:JavaRebel对此有些帮助,把它配置好之后就比较接近以前的流程——写代码、保存、运行测试;情况有所改善,但还是免不了Java世界的拖泥带水,每个项目都要先写一堆配置。不过会得到一套好的惯例.增加新的库很方便,还有很多部署相关的东西都已经内建在里面了。终究是个权衡问题。
Steve Jenson:要保证把可变性(mutability)用对了地方。先从不可变开始,当发现适合的时候再运用可变性。这就是我们得到的教训。应当注重不可变,理由是当用到线程的时候,如果对象是不可变的,就无须担心发生意外的变化。这个原则对我们来说是一大收获,现在如果不是特别需要提高性能的地方,都尽可能不要可变性。
Robey PoInter:JIT编译器还能对不可变对象做一些很重要的优化.大大提高性能。
Alex Payne:我们还遇到一种情况,肯定属于很特殊的情况,大家不要因此被吓倒了,我们的Hosebird服务是一个独立的专用系统,它将全部的公开Tweet消息流通过互联网近乎实时地发送给各家合作伙伴。最初系统里有很多Actor:-个负责从内部的消息队列上抓消息,还有很多个Actor各自代表不同的客户。随着我们做的系统测试越来越多,我们发现Actor不一定是适合系统所有部分的最佳并发模型。现在系统里一部分保留了基于Actor的并发模型,其余部分干脆回到了传统的Java线程模型。
负责的工程师John Kalucki认为这样更容易测试,更好预测。最妙的是只要几分钟就能把基于Actor的代码换成基于Java线程的代码,几次查找替换而已。所以万一发现Actor不适用了,也不是什么大不了的事情。
Robey Pointer:我在队列系统Kestrel也遇到同样的情况。一开头是每个队列一个Actor.但发现任务的粒度太细了,在那么细微的层次上用Java的锁实际效果更好。
Bill Venners:在现实世界中便用Scala还有什么地方要注意的吗?
Alex Payne:如果一个程序员从来没有用过支持模式匹配的语言,那要准备好改变自己对编程的认知。我和一群用Objective-C的Mac程序员谈过,试图说服他们,一旦你开始用模式匹配,就再也不想回到其他语言了。模式匹配是程序员每天都在傲的事情。给我一个集合,我让从大海里面找出特定的那一根针来,根据类去找也好,根据内容去找也好,模式匹配就是那么强大的一个工具。
Robey Pofnter:我还想说说我们是怎么开始用Scala的。绝不是哪天晚上喝多了两杯拍脑袋做的决定.而是挣扎了很长时间,也许算不上挣扎吧,但至少讨论了很长时间。当你用过像Ruby这么高级的语言编程,再回到中级的语言,比如Java.会感觉不耐烦,因为要多写很多代码才能达到同样的效果。Scala在这方面很吸引,因为可以继续写高层次的代码,只不过是在JVM上运行。付诸行动
Bill Venners:不久前JavaPosse的人说过他们是怎么尝试新事物的。他们建议应该在你关心的事情上尝试,但是不要在关键的业务上尝试。因为是你关心的事情,所以会一直保持进展:但要是在关键业务上尝试,一旦失败生意就完了。
Steve Jenson。Twitter也是这么做的。我们首先做小规模的试验,用Scala实现“Public Timeline”服务。效果很好,我们也学到很多,知道了我们想要什么不想要什么。那个服务还在正常运行,已经运行了差不多一年。
Alex Payne:是的,唯一一次遇到问题是因为底层的数据库出现复制延迟。除此之外它都运行得很顺畅。因为太成功了,所以我们打算逐渐把更多架构迁移到Scala。我们的流量中API请求占大头,我们希望把其中大部分都交给Scala去处理,不管是Edge缓存层还是Web应用层。希望到2009年底的时候大多数用户与Twitter的交互背后都有Scala在辛勤劳动。■参考资料
Programming Scala,Alex Payne与Dean Wampler-9-aF:
http://oreilly.com/catalog/9780596157746/
Programming in Scala. Martin Odersky( Scala设计者)、
Lex Spoon与Bill Venners :
http://www.artima.com/sViop/programming_in_scala
视频The Feel of Scala:
http://tinyurl.com/dcfm4c
Bill Venners.Artima Inc.总裁及网站的出版人。著有Inside the Java Virtual Machine-书。领导了Jini社区的ServiceUI项目,还主导面向Scala和Java开发者的开源测试工具ScalaTest项目的开发及设计。
2009 05 83