二、严防死守精益求精
回过头来说这个“局部故障由于种种原因,影响范围越来越大,最终导致总体失效”,是系统大规模故障的一种典型模式,术语称为“连锁失效”(cascading failure,有时也叫cascade failure,一般翻译成连锁故障,但我觉得叫做连锁失效更准确些)。
话说连锁失效的研究由来已久,最早应该是从大规模电网全面故障的分析开始的,时间基本上是上世纪九十年代左右。那时候其实互联网也已经大规模发展起来了,只是规模还没那么大,没有到达一个巨头宕机会在全世界范围内产生巨大影响的地步。而大规模电网的总体故障,则影响到千百万人的生活,自然引起重视更早些。其中最有名的大概是意大利2003年9月28日的全国大停电,估计因为模式太典型了,这方面文章有很多是以意大利地图为背景的电网模型进行的分析、设计。
先来看看电网领域对于连锁失效的定义,下面是一段引用自《复杂电网连锁故障模型评述》的内容:“电网连锁故障的成因比较复杂,简单地说其发生机理是:电网正常运行时每个元件都带有一定的初始负荷;当某一个或几个元件因故过负荷而导致故障发生时会改变潮流的平衡并引起负荷在其它节点上的重新分配,将多余的负荷转移加载到其它元件上;如果这些原来正常工作的元件不能处理多余的负荷就会引起新一次的负荷重新分配,从而引发连锁的过负荷故障,并最终导致网络的大面积瘫痪和大规模停电事故的发生。”
本人不想冒充电网专家,不能引用更多专业的论述了,但我相信一个有理工科技术背景的人对于上面这段话的理解应该是清晰的,它确实很浅显地说明了电网是怎么发生连锁故障的。类比一下,只要将其中的“元件”改为“节点”,“初始负荷”改为“处理能力”,“过负荷”改为/扩展为“硬件或软件故障、响应迟缓”之类,就能非常清楚地描述一个计算机网络连锁故障的场景,我们暂且把这种故障模式成为经典或叫传统的连锁失效模型。
在国外,连锁失效的概念近些年已经与云计算等大规模计算机网络相关的内容紧密联系起来,这显然与云计算的发展以及类似问题的不断出现相匹配。如果你用cascading+failure+cloud三个关键字搜索,会发现网络上已经充斥了大量对云计算可靠性反思的内容,而这些文章的时间基本上都是最近两年多,显然这也与这几年互联网巨头频繁出现的宕机事件越来越引起人们的关注有关。反观中文网站,搜索“连锁故障”,可以看到几乎所有的文章仍然都是电网故障领域的,与大规模分布式计算机网络系统可靠性相关的文章几乎完全没有,个人感觉国内IT界这方面明显跟进不足。
当然,即使是在国外,目前所能看到的计算机网络系统的连锁失效方面的研究,也远未达到大规模电网可靠性研究的那种几乎可以构成一个领域的理论、模型、方法论的层次,仍然停留在概念的、定性的层面(虽然我不懂电网的业务,对于电网的各类脆弱性/连锁失效的评估模型不甚了了,但是大致看一下这些研究所依据的各类数学模型、算法和分析手段,也能感受到这个领域的研究大概是个什么水准),我后面会尝试分析一下这方面的原因及必然性。
回到正题,既然连锁失效问题方面已经有了一定程度的研究,尤其是有电网这个领域如此成熟的理论作参考,多少能够借鉴一些吧,互联网巨头们的可靠性设计,怎么可能没有考虑连锁失效的应对策略呢?可反过来,如果已经设计得很充分,又怎么会不断出现这种大规模故障呢?我们先用个实际例子看一下目前针对连锁失效预防与解决的方案:
Netflix去年底开源了他们用于开发和管理大规模服务的框架Hystrix,其重要的目标之一就是防止因为某些节点发生故障或响应延迟所导致的连锁失效。在github和Netflix技术博客上有关于Hystrix大量、详细的说明,部分中文技术网站也有一些介绍,大家可以参考。简单来讲,Hystrix是一个大规模服务设计、开发的框架,也包括部分运行时管理功能,作用在于将大量服务按照依赖关系进行隔离,尤其是推荐“一站到底”式的隔离模式(宏观上看,有点类似于前面说提到的Amazon的Availability Zone),有效防止连锁反应。他的四个主要设计理念(参见[3]),充分体现故障隔离和防止连锁失效的设计,同时还有快速反应机制(原文不难理解,翻译反而麻烦):
-
Isolate client network interaction using the bulkhead and circuit breaker patterns
-
Fallback and degrade gracefully when possible
-
Fail fast when fallbacks aren’t available and rapidly recover
- Monitor, alert and push configuration changes with low latency (seconds)
同时,对服务调用给出精细化的开发模型,要求客户端调用的时候遵守严格的规范与流程,每一步都有相应的侦测、回退、错误处理的机制,这样才能把整个体系的作用最大化。系统还采用一些典型的设计模式(circuit breaker),配合近乎于实时的监控和针对故障的快速反应与调整,进一步保证系统可用性。此外,Netflix还有一套评估、审计的方案,通过故障模拟、流量模拟等手段,验证故障发生时系统的反应,发现隐藏的问题等等,总之是相对比较完整的一个体系架构。
Hystrix设计的总体思路可以用下面两个图较好地诠释,一个是服务的架构,一个是每次服务调用的过程,真正做到全面防止连锁失效以及实现其他各个设计目标,需要这两套机制相配合。
图1 系统服务依赖模型
图2 一次服务调用的全流程
我个人是比较认同这套设计能够很好地应对传统的连锁失效场景的,大概也是因为与我的思路比较契合:笔者在2011年所设计的一个金融渠道类系统中,就有着与图1几乎完全一样的架构,因为渠道类的系统有一个重要功能就是能够有效应对复杂的外部环境,面临随时可能的通讯失败、超时等状况,而不影响整个系统的稳定运行,实际结果也证明了这个设计能够避免由于第三方响应缓慢而导致系统整体堵塞的连锁失效问题,可以说做到了“严防死守”。不同的是,系统在服务调用方面的流程没有图2那么“精益求精”,主要是因为系统的服务很多都是“自产自销”,对于规范性、流程性要求没那么严格,另外由于一些数据处理方面强一致性的要求,也不能设计过多错误处理的模式。无论如何,这样的设计是能够有效避免我们所说的经典连锁失效的问题的,举例而言:如果亚马逊的管理系统线程池划分粒度更细一些(是哪怕就到AZ这一级,而不按是Region来划分),显然能够解决由于线程资源耗尽而导致Region这一级管理系统无法访问的问题,从而避免更大范围内的故障。
上面这些内容,算是对经典的连锁失效这一概念以及应对手段的普及贴,我想借此说明,如同本文在一开始提出的:各种架构技术如此普及,可靠性理念深入人心的今天,面对部分节点失效而产生的调用堵塞、周边节点过载等等经典的连锁失效的场景,设计出一套有效地解决方案并不是什么难事。我相信类似于Hystrix这种框架,在大型的互联网服务的体系中普遍存在,至少我所看到的很多大型互联网公司已公开的部分资料中都有这方面的影子。如此看来,互联网巨头们的“宕机”,似乎还有更深层次的原因?
(待续)