关于故障障预案

故障之存储层

缓存集群通常是单副本的,通过特定的分片算法,比如一致性哈希,来定位具体的缓存实例。部分缓存实例挂掉,一般来说带来的冲击并不大,基本也就是缓存命中率瞬间有个下降,然后逐步回升。但是如果缓存实例挂掉过多,甚至极端情况下全部挂掉的情况下,就会导致后端数据库的压力很大,出现延时变高,甚至出现雪崩现象。

对于数据库压力太大导致雪崩,数据库再起来就又立刻被打爆,怎么都起不来的情况,最好的做法是在数据库层面就做好过载保护。

在数据库不支持自我保护的情况下,一个替代的做法是通过 SRE 的手段来实现:一旦监控系统发现数据库过载了,就选择由负载均衡来扔掉部分用户请求。如果雪崩已经发生,常见的做法是让负载均衡先扔掉足够多的用户请求,让数据库能够正常服务用户。然后观察数据库的负载情况,逐步减少负载均衡扔掉的用户请求量,直至最后完全正常提供服务。

数据库和存储要保证高可靠(高持久性)和高可用,必然是多实例的。无论是什么架构,对于特定的数据,这些实例有主(Master)有从(Slave),一旦主节点挂掉就会触发选举确定新的主。当然有一些老的数据库是基于一主一备,备节点 Stand-by 直到主节点挂掉时接替它继续工作。但是这种模式已经太过时了,并不推荐使用。

故障恢复

清楚了所有的故障点,我们就可以针对性去做故障预案。对于大部分的故障来说,我们会优先倾向于通过切流量来消除故障。

流量切换,需要遵循最小切量原则。能够通过更细粒度的切量动作来消除故障,就应该用细粒度的。

通过以上分析,我们可以看出流量切换的控制点有这样几个:

负载均衡;

负载均衡实例的 VIP 入口;

DNS 解析。

但是故障根因如果是有状态服务,比如数据库与存储,那么我们就很难通过切量来消除故障。这时我们应该用过载保护机制来对服务进行降级,也就是在特定环节把一定比例的用户请求扔掉。扩容也是解决数据库与存储集群压力大的常规思路。

故障排查

是提供服务状态查询 API 和监控页面来暴露当前业务系统的状态。通过监控页面可以显示最近的 RPC 请求采样信息。这样我们可以直接了解该软件服务器正在运行的状态,包括与哪些机器在通信等等,而不必去查阅具体的架构文档。另外,这些监控页面可能同时也显示了每种类型的 RPC 错误率和延迟的直方图,这样可以快速查看哪些 RPC 存在问题。这种方法很像 X-RequestTrace 机制,它非常轻便。如果故障的现场还在,或者故障过去的时间还不长,那么它将非常有助于问题的排查。但是如果问题的现场已经被破坏,那我们就只能另寻他途。

过载 

过载的成因与后果

所谓过载,最直白的理解,当然就是因为活跃的用户超过了资源的承载能力范围,导致某类资源耗尽,进而体现出系统过载。

那么资源不够用的原因:

其一,用户增长太快了,资源规划上的预期没有跟上,导致资源储备不足。

其二,部分资源因为故障而下线,导致线上活跃的资源不足。举个例子,假设我们做了双机房容灾,但是往往这仅仅是架构上的容灾。从资源储备角度来说,我们需要按 2 倍的容量来做资源规划,才有可能在某个机房下线后系统不出问题。

其三,系统的关键资源负载能力变低,比如数据库。随着线上服务时间的推移,数据库越来越大,到达了某个临界点,可能就会导致数据库整体的延时变长,响应变慢,同时能够支撑的并发变低,从而导致过载。

其四,某类故障导致系统的反应过激,这通常是因为重试导致的。为了提高可用性,通常我们在向服务器请求某个 API 失败后,都会进行重试。这种重试行为,可能由负载均衡发起,也可能是发生在客户端。

过载带来的后果:

过载从表现上看,通常会体现为 “资源耗尽”。比如,CPU 负荷过高,持续接近 100% 下不来。如果 CPU 资源不足以应对请求负载,一般来说,所有的请求都会变慢。这个场景会造成一系列的副作用,比如处理中的请求数量上升。因为处理请求需要较长的时间,同一时间服务器必须同时处理更多的请求(上升到一定数量可能会开始进入队列排队)。这会影响其他所有的资源,包括内存、socket 连接以及其他后端服务器资源的消耗也相应增加。

所以需要强调的是,过载通常是会有连锁反应的。某类资源的耗尽,会导致其他资源出现问题。某个服务的过载,经常会出现一系列的资源过载现象,看起来都很像是根本问题,这会使得定位问题更加困难。

过载现象可能会是一个短时现象,过一段时间就撑过去了。但也有很多时候会由于正反馈循环(positive feedback)导致恶化,短时间内就快速形成雪崩效应,击垮系统。

过载的监控

直接基于该服务所依赖的关键资源,如 CPU 和内存等,来衡量服务的可用容量。我们为该服务预留了多少资源,这些资源已经用了多少,预计还能够用多久。

我们基于基础资源的用量来估算,比基于服务的 QPS 要稳定可靠得多。在绝大部分情况下(当然总会有例外情况),我们发现简单地基于 CPU 使用量作为服务容量的指标,效果已经非常好了。

过载的应对策略

首先,应该在过载情况下主动拒绝请求。服务器应该保护自己不进入过载崩溃状态。当前端或者后端进入过载模式时,应尽早尽快地将该请求标记为失败。

当然过载保护可以做得很粗,只有一个全局的负载保护。也可以很细,给每个用户设置独立的负载配额,部分特殊客户甚至可以单独调整负载配额。在理想情况下,当全局过载情况真的发生时,使服务只针对某些“异常”客户返回错误是非常关键的,这样其他用户就不会受影响。

过载保护可以基于 QPS,也可以基于资源利用率实现。但如前文已经说过的那样,基于资源的负载情况判断,会比基于 QPS 更加稳定。

过载保护也可以由负载均衡来做。避免过载是负载均衡策略的一个重要目标。这是个双保险,万一业务服务器没有考虑这块的时候,还有人能够阻止因为过载而崩溃情况的发生。

其次,应该进行容量规划。好的容量规划可以降低连锁反应发生的可能性。容量规划应该伴随着性能测试进行,以确定可能导致服务失败的负载程度。

进行容量规划只能减少触发连锁反应的可能性,但是并不能完全避免。当一个计划内或者计划外的事件导致大部分集群容量同时下线时,连锁反应是不可避免的。负载均衡问题、网络分区事件,或者突发性流量增长,都会导致意料之外的负载问题。有些系统可以根据需要动态增加容量,这可能防止过载发生,但是适当地进行容量规划还是必要的。

最后,服务优雅降级。如果说前面主动拒绝请求,是一种无脑、粗暴的降级方式的话,根据请求的类型和重要性级别来降级,则是一种更为优雅的降级方式。

值得强调的是,优雅降级不应该经常被触发。否则就显示了我们在容量规划上的失误。另外,代码中平时不太触发的代码分支有可能是不能正常工作的。在稳定运行状态下,优雅降级不会经常触发,这意味着在这个模式下的 SRE 的经验很少,对相关的问题也不够熟悉,这就会升高它的危险性。我们可以通过定期针对一小部分的服务进行压力测试,以便更多地触发这个模式,保证这个代码分支还能正常工作。

客户端能做什么事

第一个话题是重试。我们可以有这样一些方式来降低重试导致的过载概率。

限制每个请求的重试次数,比如 2 次。不要将请求无限重试。

一定要使用随机化的、指数型递增的重试周期。如果重试不是随机分布在重试窗口里的,那么系统出现的一个小故障,比如发生某个网络问题,就可能导致这些重试请求同时出现,进而引发过载。另外,如果请求没有成功,以指数型延迟重试。比如第一次是 3 秒后重试,那么第二次 6 秒,第三次 12 秒,以此类推。

考虑使用一个全局重试预算。例如,每个进程每分钟只允许重试 60 次,如果重试预算耗尽,那么直接将这个请求标记为失败,而不真正发送它。这个策略可以在全局范围内限制住重试造成的影响,容量规划失败可能只是会造成某些请求被丢弃,而不会造成全局性的故障。

第二个话题是请求的重要性级别(criticality)。可以考虑将发给服务端的请求重要性级别标记为 1~4 之间的数,它们分别代表 “可丢弃的”、“可延后处理的”、“重要的”、“非常重要的”。在服务端发生过载时,它将优先放弃 “可丢弃的” 请求,次之放弃 “可延后处理的” 请求,以此类推,直到系统负荷回归正常。

第三个话题是请求延迟和截止时间(deadline)。一个超长时间的请求,只是会让一个客户慢。但是结构性的超长时间的请求,它可能会导致系统持续恶化并引起雪崩效应。给 API 请求设置一个小但合理的超时时间,是大幅降低雪崩风险的有效手段。如果处理请求的过程有多个阶段,比如每个阶段又是由一系列 API 请求组成,该服务器应该在每个阶段开始前检查截止时间,以避免做无用功。第四个话题是客户端侧的节流机制,也就是是否可能在客户端做自适应的过载保护。客户端的过载保护有它天然的优势,在抛弃超过配额的请求时,它完全不会浪费服务端的资源。

当某个客户端检测到,最近的请求错误中的一大部分都是由于 “配额不足”错误导致时,该客户端就开始自行限制请求速度,限制它自己生成请求的数量。超过这个请求数量限制的请求直接在本地回复失败,而不会真正发到网络层。

我们使用一种称为自适应节流的技术来实现客户端节流。具体地说,每个客户端记录过去两分钟内的以下信息:

请求数量(requests):应用层代码发出的所有请求的数量总计。

请求接受数量(accepts):被服务端接受处理的请求数量。

在常规情况下,这两个值是相等的。随着后端任务开始拒绝请求,请求接受数量开始比请求数量小了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值