Heroku危机带来的启示

这些日子,说Heroku处在风口浪尖一点都不为过,先前InfoQ就Heroku大用户Rap Genius的不满做了报道,虽然Heroku在官方博客上做了说明,但Rap Genius和众多网友并不买账,问题仍在延续,不妨让我们对整件事件做个回顾,看看能够从中得到什么启示。

事件回顾

2月13日,Rap Genius在其网站上发布了一篇文章,讲述了他们在Heroku上遇到的问题,并做了分析。问题的导火线是之前为了解决一些JavaScript的小问题,Rap Genius在线上做了些ab基准测试,结果却发现有些页面的响应时间远比Heroku和New Relic所报告的时间长。以静态的版权页面为例,Heroku提供的平均响应时间是40ms,而Rap Genius测试所得的时间却是6330ms。

Rap Genius的同学在自己分析的同时,也和Heroku的工程师在进行沟通,最后确定问题的原因是处在Heroku的路由策略上,关键是Heroku的文档并未正确地描述其路由策略(在各个时期的不同文档中,对“智能路由”的描述都是只有在Dyno可用时才会将请求路由过去),但实际情况是路由采用了随机分配的策略,不考虑Dyno当时的情况,导致大量请求都在Dyno上排队,没有得到及时处理。更不幸的是Heroku的日志和监控并没有很好地反应实际情况。

这篇文章在Hacker News和Reddit上一经转发,也获得了不少关注,有网友就在评论中讨论了各种路由算法。在Rap Genius的文章中,他们用R做了个模拟,原本只需75个Dyno的一个应用,使用随机路由则需要4000个以上的Dyno。

面对这一情况,Heroku的危机公关并不成功,虽然Oren Teich(Heroku的GM)及时在Hacker News和官方博客上发表声明,承认了他们在Ruby on Rails应用上的性能问题,并做了一些承诺;第二天,官方博客上如约发表了具体的技术分析及后续改进的计划。但这些举措都没能让用户满意,暂且不论3年来为此多收的费用,至少Heroku并没有写出真实的情况:

  1. 模糊了本次事件的时间——Rap Genius的共同创始人Tom Lehman在2月5日就已经和Heroku的CTO Adam Wiggins讨论这个问题了,而且在2月8日发给他了模拟结果,因此Heroku并不是在2月13日才知道这件事情的。更有人早在2011年2月17日就写过博文反应这个问题,还在Google Group里进行了讨论,Oren Teich还亲自答复过,所以Heroku早就知道文档和系统实现不符合,存在性能问题。
  2. 模糊了本次事件的影响范围——Heroku的文中只说了Bamboo上的Rails应用的性能受到影响,Tom Lehman调侃到为什么Cedar的性能没有受到影响,是因为它用Thin(Cedar上默认的Web服务器)跑Rails的性能就没好过。

问题剖析

在Heroku的技术分析里详细描述了造成问题的原因。

Bamboo是早期的Heroku运行时技术栈,运行于Debian 5.0之上,提供了两套Ruby VM——Ruby Enterprise Edition 2011.03 1.8.7-p334和MRI 1.9.2-p180,只支持Ruby on Rails 2.x,使用Thin作为Web服务器。后来,随着技术的发展,Heroku将默认的Bamboo换成了Cedar,从仅支持Rails演化为了支持多平台多语言(Ruby on Rails的默认服务器同样为Thin)。而问题也正是从这次大的系统架构升级开始的。

Thin本质上是单线程的Web服务器,基于EventMachine来做多路复用,通过回调实现请求的并发处理。但要让Rails应用充分发挥Thin的优势,在编写应用时也要按照EventMachine的写法加以修改,对所用的库也有要求。所以,不管是Bamboo还是Cedar,默认情况下还是单线程的,不能很好地支持并发请求处理。如果在Cedar中选择Unicorn,那么情况则会有所不同。

因为使用Thin的Dyno无法并发处理请求,所以最初Routing Mesh的路由策略是选择只有当Dyno可用时才会将请求分配给它。但是由于Cedar支持多语言多平台,因此Routing Mesh的策略也做了调整,改为了更加通用的随机分配,暂时无法处理的请求在Dyno对应的队列中排队,不考虑Dyno的负载。对于Java应用,这个策略简单有效,但是对于单线程服务器中运行的未经优化的Rails应用,这就很成问题了。

更致命的是Heroku的文档并没有反映出这一变化,而监控也没能体现这一排队的耗时。原本Heroku打算让Bamboo的用户继续使用原来的Bamboo路由,直到他们做好准备迁移Cedar。可惜的是随着流量的增加,Heroku往路由集群里加入越来越多的节点,慢慢的使用新策略的节点占比越来越高,于是Bamboo就渐渐地出现了性能的下降。至于Cedar上用Thin运行的Rails应用,自打其上线起,就因随机路由策略而性能不佳,因此Tom Lehman的调侃也不无道理。

经验教训

本次事件,作为云服务提供商的Heroku有不可推卸的责任,其CTO和COO在很久前就知情的情况下,没有有效修正该问题,对用户造成了巨大的影响。对于一家云服务提供商而言,在进行大的架构升级前应该进行充分的评估及后续的追踪;在发现问题后,应该及时修正,减少对用户的影响。除了关键的技术实力,还有很多其他细节需要考虑,比如相关的文档和客户支持,本次Heroku在这些方面也都做的有欠妥当——文档没有正确反映实际情况,虽然CTO和COO亲自应答这一问题,但没有实际的后续动作。

在文档方面,同为PaaS的Cloudfoundry就比较透明,一方面因其开源特性,使用者可以完全了解其运行机制,另一方面有大量的材料能帮助大家更好地使用它。同样以路由机制为例,EMC中国研究院的颜开撰写的《新版CloudFoundry揭秘》中详细说明了其路由的实现:nginx结合Lua脚本,由Lua分析请求,直接转发给对应的Droplet,转发是随机的,但是请求中增加了Cookie信息,由此实现粘性会话,尽量将请求转发到同一个Droplet上。正是由于这些材料,Cloudfoundry的使用者可以更好地正确使用Cloudfoundry的公有云。

在客服方面,好的客服能及时帮助客户解决问题,更能带给客户好的使用体验,而糟糕的客服,则有可能造成客户流失。之前,国内的盛大云因客服的问题遭到投诉,最终用户放弃盛大云,转用其他产品。可见,要做好一个云服务平台,光有技术是远远不够的

虽说使用PaaS可以降低使用者的运维成本,无需再维护各种基础设施,同时提供适当的冗余,保证可用性。但是,这并不意味着使用者自己不需要下功夫,正确的架构必不可少,还需要一定的投入。

例如,本次Rap Genius在路由的问题上吃亏,一定程度上也是由于其Web请求的处理时间过长,才造成了队列中请求积压。对于耗时较长、占用资源的任务应该异步化,而非置于同步的Web请求中,交由后台专门的Worker处理更为合适(比如Heroku上的Worker Dyno就比Web Dyno更适合这种任务)。eBay在其分享中也多次提到了任务的异步化,可见异步化对于一个高负载高可用系统是何其重要

要在故障之初就发现端倪,良好的监控是必不可少的,虽然各家云服务提供商在其产品中都有监控功能,但用户还是应该自己定制适当的监控。除了对服务端的监控,也不能忽视了用户端的监控,如果平时就能察觉用户访问的时间存在异常,那么早就可以发现问题。百姓网在Velocity大会上就分享过他们的经验,从用户的点击开始获取用户的真实体验。此外,还可以使用Google Analytics,国内的监控宝也能对站点进行有效的监控。

随着网站的不断发展,集群也在不断壮大,此时应该将故障视为常态,即使在云端,故障也是不可避免的,AWS在过去的一年里发生的重大故障就不下三次,2011年那次著名的US-EAST-1故障甚至还让“天网”的攻击没有发生。在这方面Netflix就做的非常领先,他们的“猴子军团”在业内非常有名,通过日常自己制造故障来验证系统能否在真正的故障到来时自行恢复或者降级,降低对用户的影响。

对于云服务的使用者,如果要达到很高的可用度,必须在架构上就将容错和容灾纳入考虑之中,比如节点失效、网络中断、机房断电,甚至是AWS可用区整个故障。公有PaaS在这方面为用户提供了一些支持,但是在AWS可用区故障容灾方面应该还没有很好的支持,比如跨可用区的数据冗余,主从如何选择都是需要考虑的问题,需要用户自己来继续完善。

要做好一个云服务平台需要投入很多的精力与成本,而要用好这个平台,在其中上搭建站点为最终用户创造更多价值也并绝非易事,希望这次Heroku的事件能给大家带来一些启示,不知读者朋友您又有何经验可以分享?

你可能感兴趣的:(Heroku危机带来的启示)