Heroku的教训:糟糕的负载均衡 + RoR单线程 = 糟糕的性能

两天前,Heroku最大的用户之一Rap Genius(这是一家面向嘻哈音乐迷的网站)在其官方网站上发布了一篇博客,表达了对Heroku极大的不满,因为Heroku的路由系统(Heroku routing mesh)性能太差。该文章很快被推送到Reddit和Hacker News上,引起了极大反响。

之后,Heroku的COO Oren Teich很快在官方网站上更新了一篇声明,表示这是一个累积了三年的性能问题,所有运行在Bamboo上的RoR应用在Heroku经历扩展的同时,性能一直在下降。Teich表达了歉意,并承诺会在第二天发布一篇深入的技术报告,说明问题的原因。同时,Teich还表示Heroku将会提高用户应用Web请求队列的透明度,提供更好的文档、工具和沟通机制。

今天,Heroku的产品经理Jesper如约在网站上发布了相关的技术细节。

Rap Genius方面的问题描述

Rap Genius目前用户数量超过1500万,每月投入到Heroku上的成本约2万美元。十天前,Rap Genius的开发人员在一系列AB基准测试中发现,自己的版权页面——一个纯静态页面——的平均响应时间居然达到了6330ms。更大的问题在于,Heroku的后台和Heroku的监控合作伙伴New Relic报告的数据是40ms,相差了两个数量级。

于是他们去问Heroku。Heroku的工程师说,两个数值不同的原因在于“应用的请求需要在dyno这一层排队”。请求被处理的速度很快,时间都花在排队上了。(注:dyno是AWS上的虚拟机。目前Heroku有两种活跃的堆栈,Bamboo是老堆栈,运行Debian 5.0;Cedar是新堆栈,运行Ubuntu 10.04。)

但是,在Heroku后台提供的日志功能当中,完全没有“dyno这一层的排队等待时间”这一数值。

都是routing mesh的错

Rap Genius的开发人员们四处翻查文档,终于发现了一个情况:Heroku在2010年年中重新设计了routing mesh,新系统的路由规则是:通过一个随机算法将请求分发给dyno——无论dyno是否空闲。

Heroku在2009年的文档表示,在智能路由规则下,请求只有在dyno可用的时候才会被路由到该dyno,如果一个dyno上运行了一个长任务,路由会把请求提交至另一个dyno,而不是继续排队。

于是,Rap Genius方面认为,现在的延时问题都是Heroku偷偷修改了路由规则所导致的。

文章里按照两个路由规则做了一个模拟,结论是:在智能路由模式下用80台dyno可以搞定的并发请求数,在随机路由模式下需要4000台dyno!

“天真的”随机路由模式,老旧且不一致的文档,监控度量的缺失,造成了Rap Genius方面的极大不满。他们希望Heroku能够将路由模式切换回智能模式。

Heroku方面的说明

Heroku的回应文章承认了延时问题的存在,对自己没能早些发现这一问题表示道歉。但是,Heroku表示延时问题跟2010年的新路由规则无关,并简单介绍了Bamboo和Cedar上的路由机制。

2009年开始启动的Bamboo堆栈仅支持Ruby语言,Rails框架和Thin服务器。Bamboo本身不支持并发,一个进程一次只能处理一个请求。所以在请求分发上,为支持Bamboo的架构,Heroku设计了HTTP Router,在路由层为请求消息进行排队,然后再分发给dyno;每个路由自己建立自己的应用队列,并不存在全局的应用队列。

也就是说,2009年文档上描述的“请求仅会被路由到空闲dyno上”的智能路由模式,其实仅针对单一路由,并非针对整个路由系统。在整个路由系统中,一直存在“dyno层的排队”这一事件。在路由集群较小的情况下,这一事件发生的几率较低,所以整个集群看上去还比较智能。

新的路由机制针对在2011年开始运作的Cedar堆栈。由于Cedar希望支持HTTP轮询和区块响应(chunked response),支持JVM、Node.js、Unicorn、Puma等可以多线程、多进程的运行时,支持无状态架构,满足伸缩性需求,所以Heroku的工程团队为Cedar选择了随机路由模式。这一模式仅针对Cedar,而不针对Bamboo上的老用户。

按照Heroku的说法,他们最初设想的是Bamboo用户会逐渐将自己的应用迁移到Cedar上面去,这样就可以保持一个较小的路由集群。然而现实情况是,Bamboo上的应用一直在不断扩展,导致Heroku团队不得不往Bamboo的路由集群中不停地增加节点,这导致智能路由的效率越来越差。用Jesper的话来说,当智能路由集群的节点过多时,这个集群的工作方式基本上等同于一个随机的路由集群。

而Cedar用户感受到的延迟,则源于Rails无法处理并发请求,而大量RoR应用被按照官方文档的指导部署在了Thin服务器上——这是个单线程的服务器。Heroku表示将应用迁移至Puma或Unicorn这样的并发Web服务器上能够缓解这个问题,他们会提供更多的文档和支持帮助用户完成此类迁移。

相关评论

Heroku承认错误的态度得到了不少旁观者的称赞,但是Heroku针对延时问题提出的行动方案,却完全没能让Rap Genius方面满意。Rap Genius方面针对Heroku的技术总结文章又展开了回应,做出如下评价:

  1. Heroku真的刚刚知道这个问题么?2011年2月他们就知道了吧
  2. Heroku针对Rails的默认Web服务器是Thin,无论在Bamboo还是Cedar上都是如此。为什么不把Unicorn设置为默认服务器?害怕占用太多内存么?
  3. Heroku提出了行动方案,但完全不是解决方案。

Hacker News上也普遍存在两种质疑:

  1. 即使可以并发,也只是暂时缓解延时的问题;当规模继续扩大时,延时的问题又会变糟糕。
  2. 多花钱用PaaS就是为了不折腾。如果多花钱还免不了折腾,还不如直接用AWS。

对于Heroku遇到的路由延时问题,你有什么看法?大规模的应用适合放在PaaS上面跑么?欢迎讨论!

2月21日更新:通过和虚拟座谈会的嘉宾进行前期沟通,发现Rap Genius原文中用于描述Heroku routing mesh智能路由算法的论文“The Power of Two Choices in Randomized Load Balancing”并非是Heroku的实际实现,而是Rap Genius根据Heroku对智能路由系统的功能描述而假设的一种智能路由的规则,并按照此规则进行模拟实验,得出“Heroku Swindle Factor”这个数值。为了减少误解,现在在本文中将该论文链接撤下。

后续的相关深度内容:

  • 虚拟座谈会:PaaS的路由延时问题与架构设计思路
  • Heroku危机带来的启示 

感谢马国耀对本文的审校。

你可能感兴趣的:(Heroku的教训:糟糕的负载均衡 + RoR单线程 = 糟糕的性能)