面向业务的立体化高可用架构设计摘要:为了实现阿里九游游戏接入系统的业务高可用,技术人员跳出传统的面向系统的高可用的思路,转而从业务的角度来整体考虑高可用,最终实现了一套立体化的高可用架构,本文逐一展示这套立体化高可用架构的一些具体实践。
通常情况下我们在谈论高可用架构设计的时候,主要关注的是系统结构的高可用,例如主备架构、集群架构、多中心架构。
我们做架构设计的时候,也主要是从系统结构本身出发,例如我们把单机改为双机、双机改为集群、单机房改为异地多机房等等。
这种以系统结构为目标的高可用架构设计,更多的是从技术角度出发,但其实我们都知道,无论技术多先进,架构多强大,都不可能保证100%不出问题的。
蓝翔的挖掘机一铲子下去,支付宝就要故障2小时;程序员的误操作,携程12小时不能提供业务;某个黑客1000台肉鸡攻过来,网站可能就拒绝服务了......因此,真正做到高可用,不能单纯从系统结构的角度出发来考虑问题,不能局限于某个系统的高可用,而应该从业务的角度出发,全方位的来考虑高可用的架构设计。
前者我称之为“面向系统的高可用架构”,后者我称之为“面向业务的高可用架构”。
阿里九游游戏接入系统(以下简称“游戏接入系统”)负责所有九游平台游戏的接入(包括用户的登录、注册、支付等业务,也包括游戏开发商(以下简称“CP”)用户验证、支付等业务),对可用性的要求非常高,一旦故障,大量用户就不能愉快的玩游戏,投诉就会满天飞,论坛就被报障的帖子刷爆了,因为对于很多用户来说,不上微信可能问题不大,但要是几小时不能玩游戏,那就要爆粗口了。
如何保证游戏接入系统的高可用,让用户能够愉快的玩游戏,成为了我们的一个巨大的挑战。
为了实现游戏接入业务的高可用,我们跳出传统的面向系统的高可用的思路,转而从业务的角度来整体考虑高可用,最终实现了一套“立体化的高可用架构”。
接下来我将逐一展示这套立体化高可用架构的一些具体实践。
一、面向业务的高可用目标业界高可用的通用指标是几个9,例如5个9代表一年业务不可用的时间是5分钟,4个9代表一年业务的不可用时间是50分钟。
我们最初也是使用这个指标来作为我们高可用的目标,但是在实际的操作和讨论过程中,发现这几个指标虽然简单,但是并不能直观的理解,而且对于我们分析问题和设计方案没有很强的指导意义,因此我们决定找更加容易理解和操作的目标。
但说起来容易做起来难,高可用本身就是一个主观色彩比较强的概念,不同的人理解都不一致,要确定一个更加容易理解和操作、大家又能达成一致意见的目标,竟然成了我们首要面对的难题。
我们先后讨论了多次,前后使用了“提高可用性”、“具备XX能力”、“解决存在的可用性问题”……等多个目标,但这些目标最后都被否决了,主要原因就是我们的BOSS认为这些都没法量化,没法评估,不能认为做了事情就一定能够达到目标。
当时研发团队和BOSS还有一点小分歧:研发团队认为除了几个9外,高可用没法量化;而BOSS认为一切都可以量化,只是我们还没找到方法。
不得已,团队又继续头脑风暴,功夫不负有心人,终于在一次讨论中想出了一个可量化可衡量的高可用目标:3分钟定位问题、5分钟恢复业务、平均最多2个月发生一次问题,这样计算下来一年不可用的时间大约就是50分钟,正好契合4个9的业界通用的可用性目标。
在后来的项目执行过程中,我们发现这个目标真的是非常有用,非常具有指导意义,具体表现为:目标聚焦于业务,而不是聚焦于技术,确保最终效果不会走偏将目标自顶向下分解,很容易就得出要做的事情了设计和讨论方案时,目标就是一根准绳,是否可行,拿这个目标一衡量就很清晰了后来的项目总结时,我们都认为这个目标是项目成功的第一关键。
二、立体化的高可用架构设计将我们的高可用目标分解一下,其实有3个子目标:1. 尽量避免发生问题不出问题当然是高可用的首要目标了,不然的话天天出问题,处理再快也没意义。
2. 快速定位问题出了问题要能够快速发现和定位,不要报警或者用户投诉过来后还要花半天才能定位问题,在问题发生后尽快定位初步的原因所在,尽快处理问题,防止问题恶化。
3. 快速恢复业务特别注意这里我们强调的是“恢复业务”,而不是“解决问题”。
很多人在处理生产问题或者故障的时候有一个误区:那就是一定要找到问题根因,然后解决。
实际上大部分的时候这样做都很难的,也很耗费时间。
比如说某个机器响应很慢,可能的原因有:机器磁盘有问题、机器的CPU被耗光了、这台机器上的程序陷入死循环、jvm垃圾回收时间较长......要在短短几分钟内排查这么多可能的原因是很难的,但我们不知道真正的原因也可以恢复业务,比如说最简单的是直接把这台机器立刻下线,让流量分配到其它的机器。
当我们审视这3个目标的时候,就会发现没有哪个系统的架构能够独立的满足这目标,必须从业务的角度全方位、立体化的来分析和设计高可用方案。
结合我们的业务,最终的架构实现方案如图1所示。
通过这个图我们可以看到,这个架构设计方案并不是传统意义上的软件架构,而是一个高可用的业务架构,真正和传统系统架构相关的就只有“异地多活”,其它的功能或者系统并不属于狭义上的架构设计范畴,但这些功能和系统组合起来,共同保障了业务的整体高可用。
三、客户端重试+ HTTP-DNS当我们的业务发生问题的时候,用户侧肯定是感知最快的,如果能够在用户侧感知并立刻处理,问题的影响将大大降低。
用户侧最简单的处理问题就是重试,当遇到某些错误的时候用户侧再重试一次,错误可以是通用的错误,例如HTTP 404/500,也可以是业务上的错误码,只需要客户端和服务端预先约定即可。
用户侧重试是处理问题速度是最快的,但一个最大的问题就是DNS的不可靠性。
简单来说,如果通过DNS拿到了错误的主机地址,即使重试也一样同样是错误的。
导致DNS错误的原因主要有如下几种:1. 用户侧DNS被劫持,hosts被篡改如下是我们的域名在用户机器上被篡改的实例,可以看到我们的域名被篡改成了127.0.0.1。
2. 缓存DNS服务器污染,返回客户端错误IP2014年1月21日下午3点,国内顶级域的根服务器出现异常,许多知名网站的域名均被劫持到一个错误的IP地址上,至少有2/3的国内网站受到影响,用户无法正常访问。
根服务器恢复后,由于DNS缓存问题,部分地区用户“断网”现象仍持续了几个小时。
3. DNS缓存时间较长,短则10分钟,长则几小时DNS的解析机制为了提升效率,在很多地方会有缓存,例如本机的缓存,DNS服务器上的缓存。
缓存带来了效率上的提升,但同时却给故障处理带来了不小的麻烦,即:当我们将故障的机器下线或者将DNS指向的主机地址修改以后,用户并不能立刻感知新的主机地址,在缓存有效期内还是会继续访问旧的主机。
DNS的这几个问题虽然我们都很清楚,但是也无能为力,因为我们不能控制DNS的设备,也不能修改DNS的实现机制。
要想解决这个问题,只能另想办法,我们的解决方案就是HTTP-DNS。
故名思议,HTTP-DNS就是通过HTTP的方式来自己实现一套DNS的功能,简单来说就是客户端通过HTTP接口来获取指定域名对应的主机地址,而不再通过传统的DNS设备来获取主机地址。
相比传统DNS,HTTP-DNS具备如下优势:1. 自己实现,控制力度强,可以根据业务特点灵活实现可以根据业务进行细粒度的调度,例如发现A业务某个集群请求量较多,可以动态的将请求分配到其它集群。
2. 更新快,故障处理及时当更新域名对应的主机信息后,客户端能够立刻拿到最新的信息。
例如下线一台机器后,客户端再来获取主机地址就不会获取已经下线的机器地址,能够实现秒级的故障处理速度。
当然,HTTP-DNS的这些优势是在特定场景下才能体现的,并不能完全取代传统的DNS。
因为如果每次访问都先通过HTTP-DNS拿取主机地址的话,效率和性能都太低,对于手机类智能设备还会导致耗电和流量增加。
因此我们需要结合传统DNS和HTTP-DNS的优势,既要保证大部分情况下的性能和效率,也要保证异常情况下的故障快速处理。
具体的做法为:正常情况下我们通过传统DNS完成业务请求,异常重试的时候通过HTTP-DNS 完成请求。
如图3所示简要的说明了整体流程。
四、功能分离+ 功能降级我们的高可用目标中,首要的目标就是“尽量避免发生问题”,但对于如何避免发生问题,刚开始的时候团队并没有明确的方向,有的同学从项目管理角度提出“结对编程”、“上线评审”等流程保障机制;也有同学从测试角度提出“加强测试力度”、“自动化测试”等测试手段;我们甚至还想到了“提升人员水平”这些人力资源措施......但这些措施都强依赖于人的因素,而人的因素往往是最不可控的,相比之下,我们认为通过技术的手段是更可控的,所以制定了“技术驱动”的整体策略。
通过对系统业务的仔细分析,以及对过往线上故障的问题的分析,我们制定了“功能分离+ 功能降级”的方案,具体解释如下;1. 功能分离:划分核心功能和非核心功能,将核心功能和非核心功能物理隔离这里有两个关键点:首先要区分核心功能和非核心功能。
例如我们的游戏接入业务中,登录、注册、校验是核心功能,消息推送、日志上报等是非核心功能。
核心功能是用户玩游戏必须的,核心功能一旦有问题,玩家就不能玩游戏了;而非核心功能即使有问题,暂时也不会立刻影响玩家玩游戏,而往往非核心功能的变动反而比较频繁。
因此,优先保证核心功能正常,是我们首要的目标。
其次是物理隔离。
包括数据库、服务器、缓存等都要隔离,因为只要核心功能和非核心功能存在共享的资源,就有可能因为非核心功能影响核心功能。
举个最简单的例子,如果数据库共用一套,那么非核心功能如果出现了大量的整表查询,核心功能同样受到影响。
整体的架构示意见图4。
2. 功能降级:当出现故障的时候,可以将非核心功能直接降级,保护核心功能不受影响拆分为核心功能和非核心功能后,虽然物理上两者隔离了,但有的业务还是需要核心功能和非核心功能配合才能完成,这就存在了一定的风险。
比如说消息下发(非核心功能)需要获取用户的信息(核心功能),如果大量消息下发的话,就会给核心业务系统产生较大的压力。
这种情况下我们可以通过将非核心功能停掉,以保证核心功能不受影响。
将某个功能停掉的传统方式就是打patch,但做过的同学都知道,这样做的效率太低了:研发改代码、测试验证、运维部署,一路走下来,至少1小时以上,还容易出错,而且事后还要打另外一个patch恢复功能。
为了实现“5分钟恢复业务”的目标,我们开发了一个后台管理程序,当需要停用某个功能的时候,只需要在后台上点击一个按钮就能够完成,花费时间只需要几秒钟。
五、异地多活通常我们讲系统架构的时候,异地多活往往都是最吸引人的,因为异地多活看起来很美好,一个机房故障,其它机房能够完全接管业务,故障处理简单快速。