三年多前,我在腾讯负责的活动运营系统,因为业务流量规模的数倍增长,系统出现了各种各样的异常,当时,作为开发的我,7*24小时地没日没夜处理告警,周末和凌晨也经常上线,疲于奔命。后来,当时的老领导对我说:你不能总扮演一个“救火队长”的角色, 要尝试从系统整体层面思考产生问题的根本原因,然后推进解决。我幡然醒悟,“火”是永远救不完的,让系统能够自动”灭火”,才是解决问题的正确方向。简而言之,系统的异常不能总是依赖于“人”去恢复,让系统本身具备“容错”能力,才是根本解决之道。三年多过去了,我仍然负责着这个系统,而它也已经从一个日请求百万级的小Web系统,逐步成长为一个高峰日请求达到8亿规模的平台级系统,走过一段令人难忘的技术历程。
容错其实是系统健壮性的重要指标之一,而本文会主要聚焦于“容错”能力的实践,希望对做技术的同学有所启发和帮助。
(备注:QQ会员活动运营平台,后面统一简称AMS)
一、 重试机制最容易也最简单被人想到的容错方式,当然就是“失败重试”,总而言之,简单粗暴!简单是指它的实现通常很简单,粗暴则是指使用不当,很可能会带来系统“雪崩”的风险,因为重试意味着对后端服务的双倍请求。
1. 简单重试我们请求一个服务,如果服务请求失败,则重试一次。假设,这个服务在常规状态下是99.9%的成功率,因为某一次波动性的异常,成功率下跌到95%,那么如果有重试机制,那么成功率大概还能保持在99.75%。而简单重试的缺陷也很明显,如果服务真的出问题,很可能带来双倍流量,冲击服务系统,有可能直接将服务冲垮。而在实际的真实业务场景,往往更严重,一个功能不可用,往往更容易引起用户的“反复点击”,反而制造更大规模的流量冲击。比起服务的成功率比较低,系统直接被冲击到“挂掉”的后果明显更严重。
亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者
简单重试,要使用在恰当的场景。或者,主动计算服务成功率,成功率过低,就直接不做重试行为,避免带来过高的流量冲击。
亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者
2. 主备服务自动切换既然单一服务的重试,可能会给该带来双倍的流量冲击,而最终导致更严重的后果,那么我们不如将场景变为主备服务的自动重试或者切换。例如,我们搭建了两套获取openid的服务,如果服务A获取失败,则尝试从服务B中获取。因为重试的请求压力是压到了服务B上,服务A通常不会因为重试而产生双倍的流量冲击。
亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者
这种重试的机制,看似比较可用,而实际上也存在一些问题:
(1) 通常会存在“资源浪费”的问题。因为备份服务系统,很可能长期处于闲置状态,只有在主服务异常的时候,它的资源才会被比较充分地使用。不过,如果对于核心的服务业务(例如核心数据、营收相关)进行类似的部署,虽然会增加一些机器成本和预算,但这个付出通常也是物有所值的。
(2) 触发重试机制,对于用户的请求来说,耗时必然增加。主服务请求失败,然后再到备份服务请求,这个环节的请求耗时就至少翻倍增长,假设主服务出现连接(connect)超时,那么耗时就更是大幅度增加。一个服务在正常状态下,获取数据也许只要50ms,而服务的超时时间通常会设置到500-1000ms,甚至更多,一旦出现超时重试的场景,请求耗时必然大幅度增长,很可能会比较严重地影响用户体验。
(3) 主备服务一起陷入异常。如果是因为流量过大问题导致主服务异常,那么备份服务很可能也会承受不住这种级别的流量而挂掉。
重试的容错机制,在AMS上有使用,但是相对比较少,因为我们认为主备服务,还是不足够可靠。
二、 动态剔除或者恢复异常机器