亿级Web系统的容错性建设实践

容错实际上是系统健壮性的重要指标之一,而本文会主要聚焦于“容错”能力的实践,但愿对作技术的同窗有所启发和帮助。(备注:QQ会员活动运营平台,后面统一简称AMS)前端

 

1、 重试机制后端

最容易也最简单被人想到的容错方式,固然就是“失败重试”,总而言之,简单粗暴!简单是指它的实现一般很简单,粗暴则是指使用不当,极可能会带来系统“雪崩”的风险,由于重试意味着对后端服务的双倍请求。安全

 

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上有使用,可是相对比较少,由于咱们认为主备服务,仍是不足够可靠。


2、 动态剔除或者恢复异常机器

在AMS里,咱们的后端涉及数以百计的各种服务,来支撑整个运营系统的正常运做。全部后端服务或者存储,首先是部署为无状态的方式提供服务(一个服务一般不少台机器),而后,经过内网的一个public的智能路由服务L5,归入到AMS中。

(1) 全部服务与存储,无状态路由。这样作的目的,主要是为了不单点风险,就是避免某个服务节点挂了,致使整个服务就瘫痪了。实际上,即便像一些具备主备性质(主机器挂了,支持切换到备份机器)的接入服务,也是不够可靠的,毕竟只有2台,它们都挂了的状况,仍是可能发生的。咱们后端的服务,一般都以一组机器的形式提供服务,彼此之间没有状态关系,支撑随机分配请求。

(2) 支持平行扩容。遇到大流量场景,支持加机器扩容。

(3) 自动剔除异常机器。在咱们的路由服务,发现某个服务的机器异常的时候(成功率低于50%),就会自动剔除该机器,后续,会发出试探性的请求,确认等它恢复正常以后,再从新加回到服务机器组。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

例如,假如一组服务下拥有服务机器四台(ABCD),假设A机器的服务由于某种未知缘由,彻底不可用了,这个时候L5服务会主动将A机器自动从服务组里剔除,只保留BCD三台机器对外提供服务。而在后续,假如A机器从异常中恢复了,那么L5再主动将机器A加回来,最后,又变成ABCD四台机器对外提供服务。

在过去的3year里,咱们逐步将AMS内的服务,渐渐从写死IP列表或者主备状态的服务,所有升级和优化为L5模式的服务,慢慢实现了AMS后端服务的自我容错能力。至少,咱们已经比较少遇到,再由于某一台机器的软件或者硬件故障,而不得不人工介入处理的状况。咱们也慢慢地从疲于奔命地处理告警的苦难中,被解放出来。


3、 超时时间

1. 为服务和存储设置合理的超时时间

调用任何一个服务或者存储,一个合理的超时时间(超时时间,就是咱们请求一个服务时,等待的最长时间),是很是重要的,而这一点每每比较容易被忽视。一般Web系统和后端服务的通讯方式,是同步等待的模式。这种模式,它会带来的问题比较多。

对于服务端,影响比较大的一个问题,就是它会严重影响系统吞吐率。假设,咱们一个服务的机器上,启用了100个处理请求的worker,worker的超时时间设置为5秒,1个worker处理1个任务的平均处理耗时是100ms。那么1个work在5秒钟的时间里,可以处理50个用户请求,然而,一旦网络或者服务偶尔异常,响应超时,那么在本次处理的后续整整5秒里,它仅仅处理了1个等待超时的失败任务。一旦比较大几率出现这类型的超时异常,系统的吞吐率就会大面积降低,有可能耗尽全部的worker(资源被占据,所有在等待状态,直到5s超时才释放),最终致使新的请求无worker可用,只能陷入异常状态。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

算上网络通讯和其余环节的耗时,用户就等待了超过5s时间,最后却得到一个异常的结果,用户的心情一般是崩溃的。

解决这个问题的方式,就是设置一个合理的超时时间。例如,回到上面的的例子,平均处理耗时是100ms,那么咱们不如将超时时间从5s下调到500ms。从直观上看,它就解决了吞吐率降低和用户等待过长的问题。然而,这样作自己又比较容易带来新的问题,就是会引发服务的成功率降低。由于平均耗时是100ms,可是,部分业务请求自己耗时比较长,耗时超过500ms也比较多。例如,某个请求服务端耗时600ms才处理完毕,而后这个时候,客户端认为等待超过500ms,已经断开了链接。处理耗时比较长的这类型业务请求会受到比较明显的影响。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

2. 超时时间设置太短带来的成功率降低

超时时间设置太短,会将不少原本处理成功的请求,当作服务超时处理掉,进而引发服务成功率降低。将所有业务服务,以一刀切的方式设置一个超时时间,是比较不可取的。优化的方法,咱们分为两个方向。

(1) 快慢分离

根据实际的业务维度,区分对待地给各个业务服务配置不一样的超时时间,同时,最好也将它们的部署服务也分离出来。例如,每天酷跑的查询服务耗时一般为100ms,那么超时时间咱们就设置为1s,某新手游的查询服务一般耗时为700ms,那么咱们就设置为5s。这样的话,总体系统的成功率,就不会受到比较大的影响。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

(2) 解决同步阻塞等待

“快慢分离”能够改善系统的同步等待问题,可是,对于某些耗时原本就比较长的服务而言,系统的进程/线程资源仍然在同步等待过程当中,没法响应其余新的请求,只能阻塞等待,它的资源仍然是被占据,系统的总体吞吐率仍然被大幅度拉低。

解决的思路,固然是利用I/O多路复用,经过异步回调的方式,解决同步等待过程当中的资源浪费。AMS的一些核心服务,采用的就是“协程”(又叫“微线程”,简单的说,常规异步程序代码里嵌套比较多层的函数回调,编写复杂。而协程则提供了一种相似写同步代码的方式,来写异步回调程序),以解决同步等待的问题。异步处理的简单描述,就是当进程遇到I/O网络阻塞时,就保留现场,马上切换去处理下一个业务请求,进程不会由于某个网络等待而中止处理业务,进而,系统吞吐率即便遇到网络等待时间过长的场景,一般都能保持在比较高的水平。

值得补充一点的是,异步处理只是解决系统的吞吐率问题,对于用户的体验问题,并不会有改善,用户须要等待的时间并不会减小。

 

3. 防重入,防止重复发货

前面咱们提到,咱们设置了一个比较“合理的超时时间”,简而言之,就是一个比较短的超时时间。而在数据写入的场景,会引发新的问题,就咱们的AMS系统而言,就是发货场景。若是是发货请求超时,这个时候,咱们须要解决的问题就比较多了。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

(1) 发货等待超时,发货服务执行发货失败。这种场景,问题不大,后续用户从新点击领取按钮,就能够触发下一次从新发货。

(2) 发货等待超时,发货服务实际在更晚的时候执行发货成功,咱们称之为“超时成功”。比较麻烦的场景,则是每次都是发货超时,而实际上都发货成功,若是系统设计不当,有可能致使用户能够无限领取礼包,最终形成活动运营事故。

 

第二种场景,给咱们带来了比较麻烦的问题,若是处理不当,用户再次点击,就触发第屡次“额外”发货。

例如,咱们假设某个发货服务超时时间设置为6s,用户点击按钮,咱们的AMS收到请求后,请求发货服务发货,等待6s后,无响应,咱们给用户提示“领取失败”,而实际上发货服务却在第8秒执行发货成功,礼包到了用户的帐户上。而用户看见“领取失败”,则又再次点击按钮,最终致使“额外”多发一个礼包给到这个用户。

例子的时序和流程图大体以下:

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

这里就提到了防重入,简单的说,就是如何确认无论用户点击多少次这个领取按钮,咱们都确保结果只有一种预期结果,就是只会给用户发一次礼包,而不引发重复发货。咱们的AMS活动运营平台一year上线的活动超过4000个,涉及数以万计的各类类型、不一样业务系统的礼包发货,业务通讯场景比较复杂。针对不一样的业务场景,咱们作了不一样的解决方案:

(1) 业务层面限制,设置礼包单用户限量。在发货服务器的源头,设置好一个用户仅能最多得到1个礼包,直接避免重复发放。可是,这种业务限制,并不是每一个业务场景都通用的,只限于内部具有该限制能力的业务发货系统,而且,有一些礼包自己就能够屡次领取的,就不适用了。

(2) 订单号机制。用户的每一次符合资格的发货请求,都生成一个订单号与之对应,经过它来确保1个订单号,只发货1次。这个方案虽然比较完善,可是,它是依赖于发货服务方配合作“订单号发货状态更新“的,而咱们的发货业务方众多,并不是每个都能支持”订单号更新“的场景。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

(3) 自动重试的异步发货模式。用户点击领取礼包按钮后,Web端直接返回成功,而且提示礼包在30分钟内到帐。对于后台,则将该发货录入到发货队列或者存储中,等待发货服务异步发货。由于是异步处理,能够屡次执行发货重试操做,直到发货成功为止。同时,异步发货是能够设置一个比较长的超时等待时间,一般不会出现“超时成功”的场景,而且对于前端响应来讲,不须要等待后台发货状态的返回。可是,这种模式,会给用户带来比较很差的体验,就是没有实时反馈,没法马上告诉用户,礼包是否到帐。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

4. 非订单号的特殊防刷机制

某些特殊的合做场景,咱们没法使用双方约定订单号方式,例如一个彻底隔离独立的外部发货接口,不能和咱们作订单号的约定。基于这种场景,咱们AMS专门作了一种防刷的机制,就是经过限制read超时的次数。可是,这种方案并不是完美解决重复发货问题,只是能起到够尽量减小避免被刷的做用。一次网络通讯,一般包含:创建链接(connect),写入数据发包(write),等待而且读取回包(read),断开链接(close)。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

一般一个发货服务若是出现异常,大多数状况,在connect步骤就是失败或者超时,而若是一个请求走到等待回包(read)时超时,那么发货服务另一边就有可能发生了“超时但发货成功”的场景。这个时候,咱们将read超时的发生次数记录起来,而后提供了一个配置限制次数的能力。假如设置为2次,那么当一个用户第一次领取礼包,遇到read超时,咱们就容许它重试,当还遇到第二次read超时,就达到咱们以前设置的阀值2,咱们就认为它可能发货成功,拒绝用户的第三次领取请求。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

这种作法,假设发货服务真的出现不少超时成功,那么用户也最多只能刷到2次礼包(次数可配置),而避免发生礼包无限制被刷的场景。可是,这种方案并不彻底可靠,谨慎使用。

在发货场景,还会涉及分布式场景下的CAP(一致性、可用性、分区容错性)问题,不过,咱们的系统并不是是一个电商服务,大部分的发货并无强烈的一致性要求。所以,整体而言,咱们是弱化了一致性问题(核心服务,经过异步重试的方式,达到最终一致性),以追求可用性和分区容错性的保证。


4、 服务降级,自动屏蔽非核心分支异常

对于一次礼包领取请求,在咱们的后端CGI会通过10多个环节和服务的逻辑判断,包括礼包配置读取、礼包限量检查、登录态校验、安全保护等等。而这些服务中,就有不能够跳过的核心环节,例如读取礼包配置的服务,也有非核心环节,例如数据上报。对于非核心环节,咱们的作法,就是设置一个比较低的超时时间。

例如咱们其中一个统计上报服务,平均耗时是3ms,那么咱们就将超时时间设置为20ms,一旦超时则旁路掉,继续按照正常逻辑走业务流程。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

5、 服务解耦、物理隔离

虽然,你们都知道一个服务的设计,要尽量小和分离部署,如此,服务之间的耦合会比较小,一旦某个模块出问题,受到影响的模块就比较少,容错能力就会更强。但是,从设计之初,就将每个服务有序的切割地很小,这个须要设计者具有超前的意识,可以提早意识到业务和系统的发展形态,而实际上,业务的发展每每是比较难以预知的,由于业务的形态会随着产品的策略的改变而变化。在业务早期流量比较小的时候,一般也没有足够的人力和资源,将服务细细的切分。AMS从日请求百万级的Web系统,逐渐成长为亿级,在这个过程当中,流量规模增加了100倍,咱们经历了很多服务耦合带来的阵痛。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

1. 服务分离,大服务变成多个小服务 

咱们经常说,鸡蛋不能都放在一个篮子里。AMS之前是一个比较小的系统(日请求百万级,在腾讯公司内彻底是一个不起眼的小Web系统),所以,不少服务和存储在早起都是部署在一块儿的,查询和发货服务都放在一块儿,无论哪个出问题,都相互影响。后来,咱们逐渐的将这些核心的服务和存储,慢慢地分离出来,细细切分和从新部署。在数据存储方面,咱们将原来3-5个存储的服务,慢慢地切为20多个独立部署的存储。

例如,2015.6-12月,咱们就将其中一个核心的存储数据,从1个分离为3个。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

这样作带来了不少好处:

(1) 原来主存储的压力被分流。

(2) 稳定性更高,再也不是其中一个出问题,影响整个大的模块。

(3) 存储之间是彼此物理隔离的,即便服务器硬件故障,也不会相互影响。

 

2. 轻重分离,物理隔离

另一方面,咱们对于一些核心的业务,进行“轻重分离”。例如,咱们支持2016year“手Q春节红包”活动项目的服务集群。就将负责信息查询和红包礼包发货的集群分别独立部署,信息查询的服务相对没有那么重要,业务流程比较轻量级,而红包礼包发货则属于很是核心的业务,业务流程比较重。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

轻重分离的这个部署方式,能够给咱们带来一些好处:

(1) 查询集群即便出问题,也不会影响发货集群,保证用户核心功能正常。

(2) 两边的机器和部署的服务基本一致,在紧急的状况下,两边的集群能够相互支援和切换,起到容灾的效果。

(3) 每一个集群里的机器,都是跨机房部署,例如,服务器都是分布在ABC三个机房,假设B机房整个网络故障了,反向代理服务会将没法接受服务的B机房机器剔除,而后,剩下AC机房的服务器仍然能够正常为外界提供服务。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

6、 业务层面的容错

若是系统架构设计层面的“容错”咱们都搭建完善了,那么再继续下一层容错,就须要根据实际的业务来进行,由于,不一样的业务拥有不一样的业务逻辑特性,也可以致使业务层面的各类问题。而在业务层面的容错,简而言之,避免“人的失误”。无论一我的作事性格多么谨慎细心,也总有“手抖”的时候,在不经意间产生“失误”。AMS是一个活动运营平台,一个月会上线400多个活动,涉及数以千计的活动配置信息(包括礼包、规则、活动参与逻辑等等)。在咱们的业务场景下,由于种种缘由而致使“人的失误”并很多。

例如,某个运营同窗看错礼包发放的日限量,将本来只容许1天放量100个礼包的资源,错误地配置为天天放量200个。这种错误是测试同窗比较难测试出来的,等到活动真正上线,礼包发放到101个的时候,就报错了,由于资源池当天已经没有资源了。虽然,咱们的业务告警系统可以快速捕获到这个异常(每10分钟为一个周期,从十多个维度,监控和计算各个活动的成功率、流量波动等等数据),可是,对于腾讯的用户量级来讲,即便只影响十多分钟,也能够影响成千上万的用户,对于大规模流量的推广活动,甚至能够影响数十万用户了。这样的话,就很容易就形成严重的“现网事故”。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

完善的监控系统可以及时发现问题,防止影响面的进一步扩大和失控,可是,它并不能杜绝现网问题的发生。而真正的根治之法,固然是从起源的地方杜绝这种场景的出现,回到上面“日限量配置错误”的例子场景中,用户在内部管理端发布活动配置时,就直接提示运营同窗,这个配置规则是不对的。

在业界,由于配置参数错误而致使的现网重大事故的例子,能够说是多不胜数,“配置参数问题”几乎能够说是一个业界难题,对于解决或者缓解这种错误的发生,并无放之四海而皆准的方法,更多的是须要根据具体业务和系统场景,亦步亦趋地逐步建设配套的检查机制程序或者脚本。

所以,咱们建设了一套强大而且智能的配置检查系统,里面集合了数十种业务的搭配检查规则,而且检查规则的数目一直都在增长。这里规则包括检查礼包日限量之类比较简单的规则,也有检查各类关联配置参数、相对比较复杂的业务逻辑规则。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

另一方面,流程的执行不能经过“口头约定”,也应该固化为平台程序的一部分,例如,活动上线以前,咱们要求负责活动的同事须要验证一下“礼包领取逻辑”,也就是真实的去领取一次礼包。然而,这只是一个“口头约定”,实际上并不具有强制执行力,若是这位同事由于活动的礼包过多,而漏过其中一个礼包的验证流程,这种事情也的确偶尔会发生,这个也算是“人的失误”的另一种场景。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

为了解决问题,这个流程在咱们AMS的内部管理端中,是经过程序去保证的,确保这位同事的QQ号码的确领取过所有的礼包。作法其实挺简单的,就是让负责活动的同事设置一个验证活动的QQ号码,而后,程序在发货活动时,程序会自动检查每个子活动项目中,是否有这个QQ号码的活动参与记录。若是都有参与记录,则说明这位同事完整地领取了所有礼包。同时,其余模块的验证和测试,咱们也都采用程序和平台来保证,而不是经过“口头约定”。

亿级Web系统的容错性建设实践 - 徐汉彬Hansion - 技术行者

 

经过程序和系统对业务逻辑和流程的保证,尽量防止“人的失误”。

这种业务配置检查程序,除了能够减小问题的发生,实际上也减轻了测试和验证活动的工做,能够起到节省人力的效果。不过,业务配置检查规则的建设并不简单,逻辑每每比较复杂,由于要防止误杀。

 

7、 小结

不管是人仍是机器,都是会产生“失误”,只是对于单一个体,发生的几率一般并不大。可是,若是一个系统拥有数百台服务器,或者有一项工做有几百人共同参与,这种“失误“的几率就被大大提高,失误极可能就变为一种常态了。机器的故障,尽量让系统自己去兼容和恢复,人的失误,尽量经过程序和系统流程来避免,都尽量作到”不依赖于人“。

容错的核心价值,除了加强系统的健壮性外,我以为是解放技术人员,尽量让咱们不用凌晨起来处理告警,或享受一个相对平凡闲暇的周末。对于咱们来讲,要彻底作到这点,还有很长的路要走,与君共勉。

相关文章
相关标签/搜索