稳定性保障工作搞定这四点,宕机退!退!退!

席静(玉沙) 2023-03-07 11:10:00

序——好记性不如烂笔头

 

稳定性是个啥?

 

第一次接触稳定性这个词是在加入阿里第一年的双十一KO会上。接触到限流、扩容、压测等词汇,只觉得稳定性工作是琐碎的、繁杂的、无流程性的、无明确衡量指标的、无从下手的。

 

今年,我和两个小伙伴一起在稳定性保障工作中投入了大量的精力,我也从他们那里学到了不少关于稳定性保障相关知识,开始对稳定性工作有了一定的理解。

 

稳定性工作也是有条理有步骤的,按照步骤一步步来,就能够轻松将稳定性保障工作做全、做对、做好。

 

所谓好记性不如烂笔头。趁机梳理记录下来,以便后续使用时能够信手拈来。

 

一、什么是稳定性保障

 

那么到底什么是稳定性保障呢?

 

根据前人总结,稳定性保障就是保障系统的稳定,在各种不可预知的情况发生时仍然能够持续稳定的运行和提供服务。

 

个人感觉稳定性保障很像一个水利工程。在应用系统中,水可以是用户流量,也可以是资金流。而稳定性工作就是保障这些水能够按照预定的渠道路径流淌,保障没有渗水、漏水、渠道垮塌的现象,或者出现这类现象也能够及时修复将损失降到最低。

 

二、稳定性保障工作做什么

 

稳定性保障工作到底做什么?自然是实现稳定性保障目标。

 

根据前文稳定性保障的定义可知“在各种不可预知的情况发生时仍然能够持续稳定的运行和提供服务”就是稳定性保障工作的目标。

 

这个目标怎么实现?还是一头雾水,无从下手。那是因为这个目标太大太虚了。

 

遇到一个大而虚的目标,可以使用目标细化分而治之的方式。需要如下图的红色虚线箭头所示的几个步骤。

 

图片

 

首先,将目标细分为子目标,可以通过抽取目标中的关键字定义子目标。从“在各种不可预知的情况发生时仍然能够持续稳定的运行和提供服务”中可提取出 不可预知的情况、发生时、持续稳定、运行、提供服务。这五个关键字就是稳定性保障工作的子目标。

 

然后,针对每个子目标进行提问,凡是自己有疑问的都罗列出来。包括但不限于对目标的理解类疑问、对目标实现的方法类疑问、对目标实现的标准类疑问。

 

之后,对自己所有的疑问找出答案或解决方案。就像上学期间导师给出课题,自己去理解去解决的过程。这可能需要查阅资料、现状分析、方案选择、最终决策、实际落地。

 

如此,所有的子目标都将逐一实现,最终,子目标的实现进而完成终极目标的整体实现。

 

综上,上图中最外层的淡蓝色圈中的内容就是我们为了实现稳定性保障目标所需要做的工作了。初看起来很多,细看下来隐有规律,再看之下发现本质就是这几点:什么现象?如何发现?什么影响?如何处理?现象、影响、处理方式都与具体异常耦合,发现靠监控告警。

 

总结起来就是:梳理异常情况->配置监控告警->评估影响面->预定解决方案。

 

接下来,将从这四个步骤入手阐述稳定性保障工作具体如何落地。大框架先展示如下图所示:

 

图片

 

 
1、梳理异常情况

 

那么什么是异常情况?异常情况很多很琐碎啊,像RT飙高、消息队列阻塞、FullGC、NPE、数据异常、数据不一致、资金金额计算出错、数据库连接超时、网络异常、代码出现bug造成异常等等,这么多梳理起来如何保证覆盖率?

 

继续使用前文的目标细化分而治之的方式,但此时目标细化无法通过拆分关键字实现了,此时可以通过类目细化的方式定义子目标。

 

对异常情况进行类目细化。从上面列出的这些异常情况可进行归类梳理,可见消息队列、数据库连接、FullGC等都是中间件导致的异常,而代码bug、数据不一致、资金金额算错这类异常属于开发写出的bug或者产品设计缺陷。综上,可将中间件这类异常定义为基础设施异常,bug和产品缺陷类的异常定义为业务功能异常。

 

再反向梳理一下,基础设施异常包括网络、容量、连接、磁盘、缓存、JVM等等中间件或者底层硬件设施产生的异常;这类异常日常一般不会发生,只可能发生在大而突然的流量变化时,基础设施扛不住过大的流量导致异常。因此多发生在大促期间。

 

业务功能异常包括代码逻辑异常和资金异常。与业务功能息息相关,因此这类异常一般出现在每次业务逻辑的变更后,与日常需求相对应。可在每次业务需求开发的同时进行梳理,也可根据异常梳理结果在代码中预置开关和订正工具,万一上线后异常情况出现,就可使用预置开关和订正工具进行止血和修复。

 

顺便提一下,根据异常的分类,平日里大家习惯性将稳定性保障工作分为日常稳定性保障和大促稳定性保障。

 

日常稳定性保障主要针对业务功能异常。此类稳定性保障工作伴随着每次业务需求开发同步进行,发布要先于业务功能发布。日常的业务功能变更一般不会引起基础设施异常,因此日常稳定性保障较少涉及基础设施的保障。

 

大促稳定性保障主要针对基础设施异常。因为大促会导致比日常高数十倍数百倍甚至数千倍的流量增长,此时基础设施将面对巨大的压力,就会出现各种异常。大促稳定性保障工作是在每次大促前需要完成的工作。

 

 
2、配置监控告警

 

监控告警分为三类,基础设施监控告警、业务功能监控告警、资金安全监控告警。

 

基础设施监控告警一般都是在应用创建之初进行配置的,涵盖应用和所有的中间件、网络等。集团对基础监控告警的覆盖范围有明确规定。

 

业务功能监控告警都是在日常业务功能开发时由开发人员配置的,用于监控特定的业务场景。

 

资金安全监控告警主要面向与资金相关的应用,例如下单支付。如果没有就需要从0到1的创建,之后就随着每次业务功能开发进行增量式设置。

 

大促前夕一般会对所有的监控告警进行梳理并查漏补缺。

 

(1)数据流向图

 

监控告警配置其实是个省略句,其完整的表达应该是:监控告警数据源的准备、监控告警配置。

 

图片

 

监控告警的整体数据流向如上图所示。主要通过日志、消息、持久化数据作为数据源,将数据收集起来后用于监控告警的配置。

 

其中日志主要用于监控大盘展示,实时反应线上真是情况;消息主要用于实时核对的触发媒介,触发资金安全核对,资金安全核对通过采用旁路核对、资金一致性核对、两两核对等方式核对线上逻辑;持久化数据主要用于离线核对,通过两两核对的方式校验数据正确性。具体核对逻辑后文详述。

 

最终,监控核对都将告警信息汇总到告警系统,触发告警。同步到告警响应人进行处理。

 

(2)配置步骤

 

综上对监控告警的数据源及其流向有了了解,就可以按照以下步骤配置监控告警了:

 

图片

 

值得一提的是,虽然先要有数据,才能配置监控。但是其实数据准备和监控配置这两者应该是并行的。根据监控项的大概规划准备监控数据,然后进行监控告警配置,有任何数据不满足配置的情况都要返回进行数据准备步骤。

 

个人认为一个监控的好坏评判标准主要通过三个指标:正确性、覆盖率、直观性。正确性保证监控的基本功能,能够正确的反应真实情况。没有正确性的监控毫无存在意义。覆盖率是衡量一个监控系统成功与否的关键指标,覆盖率越高,监控系统就越能够完美的体现系统的实际运行情况。直观的展示监控指标有助于快速发现异常、快速定位问题。

 

对于告警,个人认为告警的配置最重要的是:及时性、有效性、责任制。告警出现一般都是有异常情况,可能涉及到资损或者故障,发现越及时,止血越及时,损失就越小。告警最终都是人工处理,无效的告警会浪费人力成本,因此告警配置要注意过滤噪音,保障告警的有效性,保证报出的确实是问题。至于责任制则是强调告警必须要有人响应,每条告警最好分配到人。有响应的告警才是最终有效的。

 

(3)资金安全核对

 

资金安全核对的本质是检查是否有资损事件的发生,做到及时报警快速止血,最终达到资损防控的目的。资金逻辑相比一般的业务逻辑存在一些共性,因此我们能够针对这些共性思考出一些通用的资金安全核对方法和资损防控措施。

 

对于资金安全核对的方法论,根据前辈们的总结概括,资金安全问题主要是对资金关键要素的处理过程中出现异常导致的。资金关键要素的生命周期主要包含三个重要节点:生产、传递和消费。三个节点分别可能出现的错误为生产错误、漏传错传、消费错误。针对这些错误,前辈们提出三大核对方法,所谓核对,都是寻找有一个正确数据作为预期,将实际情况与预期数据进行对比核对。

 

图片

 

基线核对是将历史数据作为预期进行对比核对,这种方式依赖历史数据的正确性,投入少,实效低,可发现大的资金问题。

 

两两核对将上游作为预期,进行对比核对,精准度高,时效性高,成本比较高,难以覆盖全面。

 

业务逻辑核对将业务专家的经验作为预期进行核对,需要大量人力投入,对经验的依赖度高,但是精准度高,时效性高。

 

面对资损防控,我们可以采用哪些具体的落地措施呢?

 

我的专攻资损防控的小伙伴做了很全面的总结归纳,如下图所示,资损防控的策略包括保存量、盘增量、控高危、盯特有。资损防控是一个长期的过程,需要时常维护,对存量布控不断优化保鲜;对新增变更进行资损评估,发布前卡点确认,保证有对应的核对规则;对于易资损场景和数据进行专项重保;对大促特有逻辑或者场景进行特别关注。

 

图片

 

资损防控,需要对全链路资金安全风险场景进行梳理,从失血类型、规则表达、规则类型、依赖因子等多方面分析资损场景,建立相应的失血模型。为保证快速、精确,可监听异步消息来进行实时核对,同时结合特定的错误日志告警,再加上小时级离线持久化数据核对兜底来保障资金安全。对于核对脚本,可通过组织review和攻防验证保障其正确性。

 

建立资损大盘,在大促高峰期值班期间安排专人盯盘,及时响应问题处理,同时对于高资损风险项预置必要的应急预案,在紧急情况发生时可以及时熔断止血以保障资金安全。

 

 
3、预估影响面

 

影响面自然也是和异常相关的。

 

基础设施的异常分多种,例如程度较轻的仅仅是短期内的负载高,严重的某个中间件(例如MetaQ)不可用、机房断电,光缆被挖断。其异常的严重程度直接决定了影响面,可能错误率飙高、RT飙高、消息阻塞、FullGC频繁,影响到系统的持续稳定性,也可能系统瘫痪不可用、网络不可用、流量跌零。不过对于这种严重的异常一般不会发生。机房都是多机房部署,硬件容灾考虑有专门的团队去保障。中间件也要专门的团队运维保障。

 

稳定性保障工作对于基础设施异常一般只考虑大促流量激增引起的容量不足、系统压力大等问题。这类问题的原因明确,就是流量过大。直接现象就是错误率飙高、RT飙高、消息阻塞、FullGC频繁等,较严重的情况下会引起客诉和舆情。

 

业务功能异常就是错误,无论是逻辑异常还是资金计算错误,资金流转错误,本质上就是产品设计有缺、开发留下的bug或者某处配置有误。这类异常与具体的业务场景有关,小则仅影响某一个局部小功能,大则影响核心功能。可能引起客诉和舆情,资金异常可能导致资损。

 

 
4、预定解决方案

 

前文大框架中,我们已经知道了预定的解决方案大概有限流、压测、扩容、预案等措施,那么这些解决方案具体如何落地呢?他们之间是否存在一定的顺序?

 

解决方案与异常类型强相关。不同的类型有不同的解决方案。因此解决方案也分为业务功能异常的解决方案和基础设施异常的解决方案。

 

(1)业务异常的解决方案

 

稳定性保障对于业务异常主要是从“万一发生了怎么办”这个角度出发去思考解决方案的。因此需要提前准备锦囊,以备不时之需。

 

业务类异常的解决方案一般分为三类,止血解决方案、临时解决方案和长期解决方案。需要消耗的时间逐渐增多,对问题的解决程度逐渐增加。都是问题发生后的应对方案。

 

止血解决方案一般不需要走代码变更发布,通过预案、设置、开关等实现,通常也是需要提前有所计划,有所准备的。临时解决方案和长期解决方案一般需要走代码变更发布,耗时较长。因此遇到问题一般都先执行效率最高的止血解决方案,如果止血解决方案依旧承受较大的损失,就需要快速拿出临时方案来解决问题,临时方案虽然一定程度上解决了问题,但是可能存在一些小功能问题、性能问题或者优雅性方面的瑕疵。因此需要在问题得以缓解之后思考出一个稳定优雅的长期解决方案。当然,那已经是后话了,不属于稳定性保障的工作范畴。

 

对于业务功能异常,在日常开发时就需要提前打算,准备好能够多维度多程度降级的开关或者设置,留作异常发生时紧急止血使用。也就是预案。

 

预案需要预演进行验证,保证预案配置和执行的正确性。

 

图片

 

①预案

 

预案的本质是一个或者多个能够快速改变代码逻辑的设置。例如开关、diamond配置或者其他工具,将这些配置跳过繁琐的审批流程、实现快速执行就是预案,是用于大促前期关闭非核心功能、大促期间紧急问题及时止血保障主要功能而选择断尾某些功能的操作配置。

 

预案按照执行时间分为提前预案和应急预案。

 

提前预案是大促前期自动执行用于关闭非核心功能以保障核心功能的预案,例如日志降级等;这类预案一般不会造成损失,风险可控,影响面是业务和消费者都可接受的。

 

应急预案是大促期间发现线上问题,经过大促负责人审批同意后由相关测试或者开发人员手动执行,用于及时止血以保障主要功能的预案。这类预案类似于壁虎断尾的行为,舍弃小的损失,保留大的功能,因此一般都存在一定的损失。

 

预案项需要提前梳理清楚,对功能无影响但是对性能无影响的锦上添花的部分在大促期间是是可以作为提前预案降级;对于可能出现异常情况的代码逻辑,或者评估风险较高的逻辑都要不吝增加开关设置,实现多维度降级(业务身份维度、商品类目维度、商家维度等等),在大促前夕配置好紧急预案。

 

预案设置越多越好,但是预案执行须谨慎。预案越多,能够快速应对的异常场景就越多,就越能够快速止血,当然也要考虑维护成本。但是配置预案的时候就要全面细致的评估清楚其执行影响,这样到了执行的时候才能正确选择合适大小的创可贴应对止血伤口。执行预案时须谨慎,按照大促要求走相应的流程,保证有double check。

 

②预演

 

预演就是预先演练一遍。包括功能预演、活动预演、预案预演等。

 

  • 功能预演:提前将功能演示一遍,保证功能的正确性。注意核心功能的覆盖率,尤其是未参加过大促的新功能。

     

  • 活动预演:主要面向某些大型活动进行提前预演。例如预售。

     

  • 预案预演:对预案进行演练测试。每一个预案在创建后都会进行一次预案预演,以保证预案执行结果符合预期。

 

(2)基础设施异常的解决方案

 

稳定性保障对于基础设施异常主要是从“如何才能不发生”这个角度出发去思考解决方案的。因此需要提前修炼内功,增强自身实力。

 

对于基础设施异常,一旦发生,难以快速止血和修复。因此一般都是在大促前夕就要做足稳定性保障工作保证大促时不发生或者少发生这类异常,可通过压测、预演提前发现异常,提前提出解决方案进行修复处理。

 

如前文所述,对于基础设施异常,稳定性保障工作仅考虑流量过大导致的异常。因此这类异常的原因明确就是流量过大。其解决方案也就明确是解决流量问题,分为对外解决方案和对内解决方案。对外限流拒绝过多流量对自身进行保护,但限流需要基于容量预估,有考量有依据的设置。对内扩容增强自身实力,并提前预热做好应对准备。

 

内外解决方案通过压测相互协调配合,最终达到一种权衡利弊后的和谐。

 

图片

 

综上,先要进行容量预估,预估外部流量峰值,再根据预估容量进行限流设置,然后进行压测,评估内部容量是否能够支撑预估容量,如果无法支撑,就要考虑扩容,扩容后根据机器数量进行限流调整,然后再次压测,压测后能够支撑预估容量甚至游刃有余时可以考虑再次调整限流,承载更多的流量。最后可以考虑在大促前进行缓存预热防止流量峰值击穿缓存。

 

①容量预估

 

容量评估要做的事情总结起来就是三件:

 

  • 对上游:询问预估流量,即他们要求我们的保障值。上游需要调用我们的服务,因此我们提供的服务量级需要满足他们的诉求。简言之,我们的水渠需要能够容纳得住从他们那里流下来的水流量。

     

  • 对自身:梳理自身上下游链路,基于自身预估,根据上游诉求,预估对下游的诉求。

     

  • 对下游:提供自己的预估容量,要求下游提供足够的容量。

 

自身容量如何预估呢?一般是通过以下几个方法进行:

 

  • 梳理业务变化对流量影响:业务逻辑每年都在变化,进而对流量有所影响。因此要梳理去年同一大促结束到今年大促之前这段时间内的业务变化,预估其对流量的影响量。

     

  • 参考往年同一大促容量值:梳理往年同一大促的流量、峰值发生时间、整体流量走势;参考预估,没有影响流量的业务变化的情况下(理想情况),基本可以直接用来作为预估值。

     

  • 参考同年之前的大促容量值:梳理同年前一次大促的流量参考预估。对比往年两次大促的流量比例来预估,例如,如果去年618与双11的的流量比是1:2,那么可以将今年618的容量乘以2的值来作为今年双11的容量预估值。

     

  • 结合上游诉求:收集到所有上游的诉求保障值,总结归纳。再对比自身预估容量。一般是两者取大。但是如果差异很大,就需要再认真核对,可能有预估错误或者遗漏。

 

图片

 

对下游的诉求保障值如何预估呢?

 

  • 根据代码逻辑总结容量公式:代码链路梳理,汇总出对下游每个接口的调用场景和次数,最终得到下游每个接口的总调用量的公式,入参是自身容量,出参就是下游接口的容量。不过这个方法有个缺点就是一旦调用链路发生变化就需要及时更新,有一定的维护成本。优点就是精确度高。

 

  • 参考历年容量:这个方式同上文自身容量的预估方式。可参考往年大促调用量预估今年容量。

 

②限流

 

限流类似于水渠源头的闸门,这个闸门开的大小直接决定了水渠中的水流量。将闸门开启到一定程度,而非完全打开,保障水渠不至于被冲垮的行为就是限流。

 

无论有没有扩容,无论系统是游刃有余还是苦苦支撑,都需要对系统进行限流。限流是系统的门卫,超出的容量可以被拦截在外。

 

我们一般都使用单机限流,即设置单台机器最大可接受的QPS,超过则触发限流,限流可以直接拒绝,即快速失败,也可以排队等待。单机限流可以进行调用来源应用维度的限流,可以对所有上游应用一概而论(流控应用设置为default),也可以因人而异保障主要业务(针对核心的应用限流设置较大,非核心的应用限流设置较小)。

 

也可以考虑集群限流,对整个集群进行限流。

 

③压测

 

所谓压测,就是构造数据流量通过几台压力机模拟用户持续并发请求系统接口,测试系统的性能和承受能力的过程。

 

集团的压测分为单链路压测和全链路压测。所谓单链路压测,即自己的应用服务入口作为压测入口进行触发压测,主要面对的仅限于单个应用,涉及应用少,涉及人员少。全链路压测则从用户实际操作入口作为压测入口,这个操作涉及到的所有应用服务全部参与压测,是一个跨部门跨应用的过程,每次全链路压测,涉及到链路上所有团队的协调参与共同努力。

 

全链路压测更能反应线上真是情况。条件允许的情况下都选择全链路压测。

 

集团的压测都会提前构造影子链路,即真实链路的影子,和真实链路一模一样,却又不会对真实链路产生影响。库表也是使用影子表,将压测的持久化数据与真实持久化数据分开。

 

压测是一个复杂的过程,需要专业的压测团队的同学支撑。对于一个从未参加过压测的应用而言,要做的工作简要概括如下:

 

图片

 

  • 应用适配改造:要走通压测链路,涉及改动较多,包括应用系统改造、nginx升级、中间件改造、缓存端升级、DB端升级等等。

     

  • 构造压测数据:压测数据构造需要根据具体的业务。例如价保的压测,需要构造的压测数据是处于价保有效期内的订单。如果要压到申请链路,还要构造优惠制造差价。

     

  • 创建压测模型:所谓压测模型是指压测数据的分布情况,压测模型要能够反应线上真实流量占比。要覆盖到所有链路,不同业务的流量占比等同线上真实情况,中心机房和单元机房的流量比例也要按照线上比例分配。

     

  • 进行压测:压测分为单链路压测和全链路压测,单链路压测是指仅仅对自己关注的系统进行压测;全链路压测是从用户发起请求开始到整个业务逻辑结束的全部链路压测。压测入口也分为http接口压测和端上接口压测,具体根据系统情况而定。

     

  • 观测与总结:在压测的过程中,要时刻盯盘,观测系统水位。压测后总结梳理出压测报告。

 

压测一般不是一次就能够完成的,可能因为各种原因导致压测结果不理想,因此要多次压测,每次根据压测问题进行修复后再次压测,直到压测结果符合预期为止。

 

上图中只绘制了压测适配原因导致的重新压测,如前文所示扩容和限流后也要进行压测。

 

④扩容

 

扩容,就是暴力增加机器。所以也要考虑成本。

 

如果压测的结果,系统无法达到上游诉求,为了保证业务的顺利进行。就需要扩容,并再次压测,直到系统能够保障目标容量。

 

如果压测的结果皆大欢喜,满足了上游诉求,那么就不需要扩容了。

 

⑤预热

 

预热可简单的理解为参赛前的热身。让容器、缓存、数据库等都准备好迎接大促的流量峰值。

 

预热分为缓存预热和启动预热。缓存预热属于大促稳定性保障工作,但是启动预热属于一种日常的性能优化。

 

集团每年大促的商品预热是被人所熟知的。就是将热点商品提前加载进缓存中,避免大促期间缓存击穿,流量直接打挂db的情况。这就是缓存预热。多用于大量依赖缓存的应用。

 

启动预热一般是处理服务刚启动时服务RT较大甚至超时的问题,处理方式是在启动时通过构造参数,模拟真实请求的方式把代码跑热后再对外提供服务。

 

总结

 

稳定性保障工作从时间上来说,包括日常业务需求开发时的监控告警配置和开关预留,大促前夕的容量预估、压测、限流、扩容和预热,其实还有一部分,上文未及提及,那便是大促值班。

 

建议在值班前写一个值班手册,将可能出现的问题,解决方案,需要使用到的工具链接全部罗列清楚,避免值班时手忙脚乱找资料找工具。还有必要的权限申请在值班前申请好。

 

在大促期间,严阵以待,这个时候需要做到两动,主动关注监控大盘,注意流量变化,监控基础设施指标;被动关注告警,一旦被告警提醒就是有异常情况了,要立刻投入定位解决。

 

最后,稳定性保障工作是没有尽头的,其重要性不言而喻,但是也要在业务功能与稳定性之间做好权衡,如果稳定性核对的调用流量都超过了业务流量,那么稳定性工作就有点过了,从机器成本、人力成本上都没有这个必要。

 

本文整理了自己对稳定性保障的认识和理解,可能存在理解有误或者认识不足的情况欢迎指正,也期待更多的学习逐渐修正和完善自己的稳定性相关知识。

 

作者丨席静(玉沙)
来源丨公众号:阿里开发者(ID:ali_tech)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
最新评论
访客 2024年04月08日

如果字段的最大可能长度超过255字节,那么长度值可能…

访客 2024年03月04日

只能说作者太用心了,优秀

访客 2024年02月23日

感谢详解

访客 2024年02月20日

一般干个7-8年(即30岁左右),能做到年入40w-50w;有…

访客 2023年08月20日

230721

活动预告