谷歌服务中断事故能否避免?

图灵君导读

当地时间12月14日凌晨3点47分,谷歌遭遇大面积技术故障,多项服务受影响,直到约45分钟后才恢复。谷歌在声明中称本次事故的原因是“内部存储配额问题”。

可以想象,谷歌有多少工程师度过了一个不眠之夜?在分布式系统上线后,开发人员和运维人员到底能不能高枕无忧?当出现问题时,系统能否依然稳定运行?

迈克尔·尼加德是拥有20余年经验的运维专家。他在《发布!设计与部署稳定的分布式系统(第2版)》一书中分享了一些案例。今天,图灵君就给大家分享其中一个案例:屋漏偏逢连夜雨。请移至文末了解本书详情。

宝宝的第一个感恩节

我的客户在夏天新推出了一个电商系统。推出产品之后那几周和几个月的经历,一次又一次地证明:新推出一个网站就像养一个宝宝,某些事情必然会发生,例如半夜被唤醒,然后被告知一些“可怕”的发现,就好比听到“天呐!你给孩子喂了什么……橙色橡皮泥吗?”然而,在处理完这个新系统遇到的所有问题之后,我们仍可以持谨慎乐观的态度去迎接这个假日季。

我们的乐观态度源于几个因素。第一,生产服务器的数量几乎翻了一番。第二,我们有可靠的数据显示该网站在目前的负载下能稳定运行。第三,我们有信心应对网站出现的任何错误。

为了过感恩节,有些同事在周末加班,从而在感恩节休假。我有4天假期,于是带着妻儿去父母那团聚,吃感恩节大餐,他们的住所跟我们隔了3个州。我们还在感恩节的那个周末,安排了24小时的工作现场值班。正如我所说的,我们抱着谨慎乐观的态度行事。当然,作为一名前童子军成员(口号是“时刻准备着”),去父母家之前,我将笔记本电脑塞进了家用小货车,以防万一。

把脉


当星期三晚上抵达父母家时,我立即在父母的家庭办公室里安置好了笔记本电脑,我可以在任何有宽带和手机的地方工作。凭借父母家的3兆有线宽带,我使用PuTTY登录跳板机,并启动我的采样脚本。

在新推出这个网站之前的准备阶段,我参与了负载测试。测试完成后,大多数负载测试提供了测试结果。由于数据来自负载生成器而不是被测系统内部,因此这是一个“黑盒”测试。为了从负载测试中获得更多信息,我开始使用应用程序服务器的图形用户界面,检查一些重要的指标,如延迟、空闲堆内存数量、活动的请求处理线程数量和活动的会话数量。

如果事先不知道要找什么,使用图形用户界面就是探索系统的好方法。如果明确知道想要什么,使用图形用户界面就会变得乏味。但如果需要一次查看三四十台服务器,那么使用图形用户界面就完全不切实际了。

为了能从负载测试中获得更多信息,我写了一个Perl模块的集合,实现图形用户界面屏幕抓取,并能解析HTML里面的值。

这些Perl模块可以让我获取和设置属性值,并调用应用程序服务器的内置组件和自定义组件的各个方法。由于整个图形用户界面均基于HTML,因此应用程序服务器不能区别Perl模块和Web浏览器。通过使用这些Perl模块,我可以创建一组脚本,来采样所有应用程序服务器的重要统计数据,打印出详细信息和汇总结果,休眠一段时间,然后循环往复。

这些都是简单的指标,但自从新网站推出以来,我们所有人都通过查看这些统计数据了解网站的正常“节奏”和“脉搏”,比如我们一眼就知道,7月的一个星期二中午系统是正常的。

感恩节


感恩节早上一醒来,我来不及喝完咖啡就跑进父母的办公室查看整晚运行的数据窗口,对所看到的数据检查再三,清晨的会话数量已经达到了正常一周中最繁忙的高峰水平。订单数量如此之高,我不得不打电话给DBA,了解是否有重复提交的订单。答复自然是没有。

截至中午,顾客在半天内下的订单量,已经与过去平常一周的订单量相同。从页面延迟、系统的响应时间和整体网站性能这些总体指标来看,系统虽然承受着压力,但仍处于运维标称范围之内。

更好的是,即使会话和订单的数量在不断增加,但随着时间的推移,这些数据也在趋于稳定,这让我在整个感恩节火鸡晚餐中兴高采烈。

到了晚上,这一天内的订单量已经达到了在此之前11月的总订单量。到午夜时分,日订单量已经与整个10月份的订单量持平。即使是这样,网站还在正常运行,它通过了第一次严酷的负载测试。

黑色星期五


第二天是黑色星期五。用完早餐后,我走到家庭办公室,看了一下数据。订单数的增长趋势甚至比前一天还要高,会话数量也增加了,但一切正常,页面延迟仍然低至大约250毫秒。我决定带着老妈出门买些做鸡肉咖喱的食材。

当然,如果后来没有出现可怕的错误,我是不会絮絮叨叨地讲这个故事的。在我离开办公桌之前,什么状况还都没发生。果然,当在外边走了一半路程时,我接到了电话。

“早上好,迈克尔。我是丹尼尔。”

“一定有麻烦了是不是,丹尼尔?”我问道。

“所有的DRP在SiteScope上都变红了。我们一直在进行DRP的滚动重启,但它们重启后会立即失效。戴维已经召集了电话会议,并请你加入。”

虽然是寥寥数语,但我已从丹尼尔那里得知,网站停机了,问题很严重。SiteScope模拟的其实就是真实的顾客,如下图所示。


SiteScope变红,表明顾客无法购物,我们正在损失收入。在一个使用ATG软件的电商网站中,页面请求由专用的实例处理。Web服务器通过DRP协议调用应用程序服务器,因此通常将应用程序服务器上处理请求的实例称为DRP。一个DRP变红,表示应用程序服务器上处理请求的一个实例已经停止响应页面请求。所有DRP都变红,则意味着该网站已经停机,并且正以大约每小时100万美元的速度流失订单。

我立即拨进了电话会议,里面一片嘈杂声。显然,会议室里的免提电话也拨入了这个电话会议。试图在有回声的会议室里分辨15个声音,这种感觉简直无以言表。然后,我听到有人提醒说“网站存在问题”。是的,我们知道了。谢谢,劳驾请挂机。

生命体征


丹尼尔给我打电话时,事故大约已经过去20分钟了。运维中心已将问题上报给现场团队,团队运营经理戴维请我参与解决。与顾客可能遭受的巨大损失相比,中断休假根本不算什么。

事故发生20分钟后,我们知道了以下一些情况。

- 会话数量非常高,比前一天还要高。

- 网络带宽使用率很高,但没有达到极限。

- 应用程序服务器的页面延迟很高(响应时间很长)。

- Web、应用程序和数据库CPU使用率非常低。

- 搜索服务器这个以往常见的罪魁祸首这次倒是响应良好,其统计数据看起来没问题。

- 几乎所有处理请求的线程处于忙碌状态,其中许多已处理超过5秒。

为了获得更多信息,我开始获取那些有异常行为的应用程序服务器的线程转储。其间,我请在会议室现场工作的那位明星级工程师阿肖克,帮忙检查后端订单管理系统。他在后端看到了与前端类似的模式:CPU使用率很低,大多数线程长时间处于忙碌状态。

从我接到电话到现在已经有近一小时了,或者说,网站已经停机80分钟了。这不仅意味着订单流失,还意味着这次极为严重的事故让我们几乎就要违背SLA。我讨厌违背SLA,因此感到很不安。所有同事和我一样,都很不安。

进行诊断


前端应用程序服务器上的线程转储显示,所有的DRP都表现出相似的模式。有几个线程忙着调用后端,而其他大部分线程则在等着调用后端的可用连接,等待中的线程全部被阻塞在一个没有设置超时的资源池上。

如果后端停止响应,那么进行调用的线程将永远不会返回,而那些被阻塞的线程将永远无法获得调用后端的机会。简而言之,所有3000个处理请求的线程,都被束缚在那里动弹不得,这完美地解释了所观察到的低CPU使用率现象:100个DRP全部处于空闲状态,一直在等待永远不会获得的响应。

再看一下订单管理系统。该系统上的线程转储显示,在其450个线程中,一些正忙着调用外部集成点。剩下的你也许已经猜到了:其他所有线程都在等待调用那个外部集成点。

求助专家


当订单管理系统的技术支持工程师拨入电话会议时,我感觉像是等了半个世纪(但可能只等了半小时)。他解释说,通常处理送货调度的4台服务器中,有2台在感恩节这个周末因维护而停机,而另一台由于未知原因失灵了。直到今天,我还是不知道为什么他们会在全年52个周末中,偏偏选择这个周末进行维护!

这种情况使得流量在几个系统之间产生了巨大的失衡,如下图所示。


当那位接到技术支持请求的工程师检查那台“孤独”的送货调度服务器时,发现其CPU利用率已经达到100%。尽管已经多次收到CPU利用率过高的警告,但这位工程师还是没有做出反应。该团队经常因为CPU利用率的瞬间峰值收到警告提示,但结果常常是误报。之前所有的误报,让他们学会忽略所有CPU高利用率警告。

在电话会议上,业务负责人语气低沉地告诉我们,市场营销部门在感恩节前准备了新的广告插页,登在感恩节第二天(星期五)的报纸上。广告上提到,所有在感恩节后第一个星期一之前在线下的订单,均享受免费送货上门服务。在这个持续了4小时的电话会议上,所有参会者第一次陷入了沉默。

回顾一下,前端系统是一个电商系统,拥有分布在100台服务器上的3000个线程,以及发生了根本性变化的流量模式(因为广告促销)。电商系统发出的请求流量,淹没了其下游的订单管理系统。订单管理系统拥有450个线程,它们既可能被用来处理来自前端系统的请求,也可能被用来处理订单。而订单管理系统发出的请求又淹没了其下游的送货调度系统,后者一次只能处理25个请求。

这种状况会随着促销宣传而一直持续到星期一。这简直就是噩梦,网站已停机,而且这种情况没有手册可以参考。我们正处于事故的“漩涡”中,不得不硬着头皮解决问题。

如何应对


接下来就做头脑风暴。大家提出了许多方案,也否决了许多方案,否决的原因大多是在目前的情况下,应用程序代码的行为是未知的。此时唯一可行的方案逐渐变得清晰起来:停止发出如此多的请求来对订单进行送货调度预约。由于周末的市场营销活动主要围绕免费送货上门,因此用户下订单的请求是不会放慢速度的。此时必须找到一种方法来抑制对送货调度系统的调用数量,而订单管理系统无法做到这一点。

当查看电商系统的代码后,我们看到了一丝希望。电商系统的代码使用了标准资源池的一个子类,管理访问订单管理系统的连接。实际上,它还有一个单独的连接池,专用于处理有关送货调度的请求。电商系统拥有一个专用于处理送货调度连接的组件,所以我们就可以将该组件用作限流器。

要是电商系统的开发人员为连接池添加了一个enabled属性,那么将其设置为false就会使事情变得很简单。也许他们下一步就可以这样做。不管怎样,把资源池中最大的连接数设置为0也能有效地将资源池关闭。

尾声

我编写了一个新的脚本,可以完成重置该连接池最大值所需的所有操作。它能设置max属性,停止服务,然后重启服务。

通过执行一个命令,运维中心或客户现场“指挥所”(会议室)的工程师,就可以将最大连接数重置为任何所需的数值。我后来才知道,这个脚本在整个周末都被不断地使用着。

电话会议结束了。我挂断电话,去哄孩子上床睡觉。这着实花了我一点时间,因为他们不停地说着各种趣闻:逛公园,在草坪上的喷洒器下边玩儿,去后院看刚出生的兔宝宝。我很喜欢听他们聊这些。

本文节选自以下图书

迈克尔·尼加德  著

吾真本  译


案例研究 + 行业思想家的真知灼见

助你掌握生产环境的生存法则

原版美亚评分接近满分
 

作者介绍


迈克尔·尼加德,程序员兼架构师,拥有20余年的从业经验,先后为美国政府以及银行、金融、农业、零售等多个行业交付过运营系统,对如何在不利的环境下构建高性能、高可靠性的软件有独到的见解。

译者介绍


吾真本,本名伍斌,ThoughtWorks首席咨询师,著有测试驱动开发入门读物《驯服烂代码》。工作20余年,做过程序员、测试工程师、项目经理、敏捷教练。最近7年成功辅导10余家大型金融和科技公司的敏捷和DevOps转型团队。曾主办多场编程道场,人称“道长”。

| 留言

你有过一辈子也忘不了的开发事故经历吗?欢迎跟小伙伴们分享!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章