我在职业生涯的初期就开始扮演“消防员”的角色。经常上网执行“救火”、“止损”、“调研”等应急任务,然后通过分析、推理、验证……图片来自Pexels的“抓挠”找出背后的根本原因,仿佛他是一个“经验丰富、沉着冷静、心思缜密”的侦探。以前,我一直认为线上的问题定位和分析处理能力是架构师的“看家本领”,一直以此为荣。但这两年开始觉得“防”比“救火”更重要,就像古话“上医治未病,中医治所欲病,下大夫治已病者”。接下来给大家分享一下稳定治理的经验和阿里的稳定保障体系。阅读本文后,你将收获:高并发大流量场景常见问题及对策知名互联网公司高可用架构及稳定性保障体系稳定性治理常见场景突如其来的大流量上图相信大家都不陌生,尤其是刚刚过去的双11、双12,这是电商大促场景中最常用的自动方案——“限流保护”,而不是像很多朋友说的“宕机”、“闪退”。“限流”是应对高并发或突发大流量场景的“三招”之一,无论是在电商推广中,还是在社交媒体上突发热点事件(例如:遇到“明星出轨”),在正常情况下还是非常必要的保护手段。本质上,当检测到当前请求量即将超过自身处理能力时,进行自动拒绝(或执行“请求排队”),避免系统完全不堪重负。说到不稳定服务的“限流”,就不得不提另一个斧头“降级”。除了我们之前提到的“开关降级”(关闭次要功能或服务)、掩盖、降低一致性等,技术层面最常用的就是“自动熔断降级”。“限流”是为了防止系统被大流量压垮,“熔断”是为了防止不稳定的服务导致超时或等待,从而级联传输,最终导致整个系统雪崩。如图所示,假设此时服务D出现故障或FullGC,会导致上游服务G和F出现大量等待和异常,并级联投递到最上游的服务A和B。即使在服务G和F中设置了“timeout”(如果不设置“timeout”会更糟),会导致线程池中大量的线程资源被占用。如果服务H、I和服务G、F在同一个应用中,默认共享同一个线程池,也会因为资源耗尽而变得不可用,最终最上游的服务A和服务B会整体不可用.所有环节都会出现异常,在线核心系统出现这样的事故,将是一场灾难。如果我们检查服务G和服务F中的RT明显变长或者异常比例增加,我们可以让它自动关闭并快速失效,这样H和我就不会受到影响,最上游的服务A和服务B仍然可以保证“部分可用”。举一个现实生活中比较通俗的例子,当你家的电器发生短路时,空气开关会自动跳闸(保险丝会自动“熔断”)。这是通过牺牲你家的用电量来恢复小区的正常供电,否则会烧毁整条线路,后果不堪设想。所以,你得结合实际业务场景,找出哪些接口和服务可以“降级”。单点架构的事件大概发生在2015年,被载入支付宝的“历史”,也推动了蚂蚁金服整体LDC架构(三地五中心多活架构)的演进。异地多活架构:突破单机房容量限制,杜绝机房单点,高可用品质打造。根据以往的经验,60%以上的故障都是由变更引起的。请牢记变更“三招”:回滚/可回滚应急可灰度可监控预防质量事故常用手段:做好分析、设计、评审、能力评估、规避风险、制定规范,控制流程,增加代码扫描检查等,做好CodeReview测试用例覆盖率(通过率,Row覆盖率,分支覆盖率),尽可能改全量回归,避免人肉(容易出错),关键时刻DoubleCheck高可用架构的基石:稳定性保障体系从故障角度进行稳定性保障,如下图:稳定性保障的核心目标如下:Preventfailuresas尽早,以减少失败的可能性。及时预测故障,发现并定义故障。可在即将发生故障时快速应急响应。故障发生后,可以快速定位,及时止损,快速恢复故障。可以从失败中吸取教训,避免重蹈覆辙。仔细想想,所有的稳定性保证都是围绕这些目标建立的。稳定保障体系上图涵盖了稳定体系的方方面面,下面我会一一说明。应用架构稳定性应用架构稳定性是一个比较宽泛的话题。根据我的理解,它主要包括很多设计原则和方法:①架构设计的简化。系统架构简单、清晰、易懂。同时需要考虑一定的可扩展性,这符合软件设计中的KISS原则。现实中有太多的“过度设计”和“为了技术而技术”。这些都是反例,架构师需要懂得权衡自己。②分裂。拆分的目的是为了降低系统的复杂度,模块或服务是“自治的”,符合软件设计中的“单一职责”原则。分的太粗或者太细都会有问题,这里没有标准答案。应该按领域拆分(有兴趣的同学可以学习DDD中的boundedcontext),结合业务复杂度、团队规模(康威定律)等实际情况判断。想象一个5个人的小团队维护着30多个系统一定是非常痛苦的。③隔离。拆分本质上是系统级和数据库级的隔离。另外,应用程序内部也可以使用线程池隔离等。分清“主次”,识别“高危”,将其隔离,可以降低发生概率。④冗余。避免单点、容量冗余。机房是否单点、硬件是否单点、应用部署是否单点、数据库部署是否单点、链路是否单点……无论是硬件还是软件不可靠,冗余(“备用轮胎”)由高可用性常规方法保证。⑤无状态、一致性、并发控制、可靠性、幂等性、可恢复性……等。例如:消息投递时如何保证消费者能收到?上游重试调用你的接口保证数据不重复?Redis节点挂了,分布式锁失效怎么办?...这些都是在架构设计和功能设计中必须考虑的。⑥尽量异步,尽量减少依赖。异步可以在一定程度上提高性能,减少RT,减少直接依赖,这是一种常用的方法。⑦容错模式。在团队中,我经常强调学习“为失败和失败而设计”,尽量做一个“悲观主义者”。有的同学可能会嫌弃我“操心”,但事实证明很有效。入行以来有幸向一些高手学习,分享两句受益匪浅:出来混,迟早要还的。不要冒险,你担心的事情迟早会发生。上图是一个典型的互联网分布式服务,如果其中任何一个红色节点出现问题,你确定不会影响你系统的正常运行吗?限流降级之前已经介绍了一些限流降级的场景,这里简单总结一下实际使用中的一些关键点。以限流为例,你需要问自己,思考一些问题:你限流的实际目的是什么?只是为了过载保护?需要什么限流策略,是“单机限流”还是“集群限流”?哪些资源需要限流保护?网关?应用?水位在哪里?(在动态配置中心加个开关)还是熔断自动降级?保险丝的依据是什么?哪些服务可以降级?怎么去到底(比如:去缓存或者挂了就返回默认值)?……这里我先说篇幅问题,下一篇再说明。监控预警上图是我个人对一个成熟的互联网公司应该具备的监控体系的理解。前面提到云原生时代的“可观察性”是监控的三大基石。这里简单补充一下“健康检查”,形成经典的“监控四联体”:关于健康检查,主要分为“服务器端轮询”和“客户端主动上报”两种模式。MySQL之类的服务不能主动报优缺点(除非使用第三方代理)。需要注意的是,传统的基于端口的健康检查很难发现“进程死机”问题。说到监控,就不得不提到GoogleSRE团队提出的监控的四大黄金指标:Latency:响应时间Traffic:请求量,QPSerror:错误数量,Errorratesaturation:资源利用率类似于USE方法,REDMethods,有兴趣的读者可以自行查阅相关资料。云原生时代,应用/节点通常是暴露的端点,监控服务器采用pull方式采集指标。目前比较典型的是Prometheus监控系统。如果你有兴趣,你可以了解更多。当然,有些监控用来通过Agent采集指标数据,然后上报给服务器。除了监控本身,预警能力也很重要。配置告警阈值也需要技巧。如果阈值过高,可能会不灵敏,漏掉故障。阈值过低会造成“监控告警疲劳”。StrongandWeakDependencyGovernance这是在网上找的一张“系统依赖拓扑图”。可见,面对这种复杂性,是通过个人经验,还是借助“链接跟踪工具”,很难快速理清。什么是强依赖和弱依赖?A系统需要依赖B系统的服务来完成业务逻辑。当B挂掉时,会影响系统A中主要业务流程的推进,这就是“强依赖”。比如:订单服务依赖“生成订单号”,是“强依赖”,“扣除库存”也是“强依赖”。同理,当A所依赖的B系统挂掉时,不会影响主进程的进程,则为“弱依赖”。例如:购物车页面显示的库存数量是可选的。这种强弱依赖如何梳理,这个可以通过阿里内部专门的中间件来完成,目前开源社区没有替代品,可能需要结合链接跟踪+个人经验来做系统级和接口级链路梳理。我们关注“强依赖”,而“弱依赖”通常可以实现容灾策略,例如:可以直接降级,可以返回空值或默认值,可以实现来回等等总结一下几个关键点:当前业务功能/应用/服务,依赖服务描述,依赖类型(例如:RPC调用),峰值调用量,链路流量调用率,强度等级,挂机后果,etc.CapacityPlanning容量规划非常重要,无论是成本控制还是稳定性保障。否则,无法知道需要投入多少资源,资源应该投入到什么地方。需要知道系统的极限水位在哪里,然后计算出一个合理的“阈值”,做好“过载保护”。第一阶段:主要根据经验值、理论值等进行估算,或者通过“拍脑袋”进行估算。50台机器跑应用,20台机器跑中间件,10台机器做数据库……估计能处理日均1000W的访问量,日均100W的订单……靠谱的人甚至可以拉出来”MySQL并发连接数大约是几核几千兆字节。”可以达到xxx”,“Redis官方说可以达到10WTPS”等参考值,至少听起来有点道理。不靠谱的人呢?那可能是个废话,也可能会说“我用了这么多supportsinmylastcompany”,其实是纯粹靠头脑风暴,总之这个很不靠谱,会造成资源配置不合理,有的浪费,有的荒废。Phase2:线下压测一般都是用single做的接口和单节点,压测结果可以帮助我们了解应用的性能,定位性能瓶颈,容量规划,那个参考价值不大,因为实际情况往往太复杂,有网络等各种因素带宽、公共资源、覆盖场景不一致、在线多场景混合,根据“木桶短板”原理,系统的容量往往取决于最薄弱的环节。应曰:“一千里之差”。第三阶段:线上单机压测比较常见的手段有:线上引流、流量复制、日志回放。其中在线引流最容易实现,但需要统一的中间件。通过接入层或服务注册中心(软负载中心)调整流量权重和比例,将单机负载推到极限。这样就比较清楚系统的实际水位在哪里。第四阶段:全链路压测,弹性扩容这是阿里的第一个创举,目前很多公司都在跟进。属于业务驱动技术创造的手段。全链路压测会涉及到很多内容,关键步骤包括:链路整理、基础数据准备、脱敏等中间件改造(透传影标、软加载/消息/缓存/分库分表和其他路由,创建影子表)建立压力测试模型,流量模型,流量引擎计划系统的检查执行压力测试,关注监控和告警数据清洗对于业务开发团队同学,链路梳理和评估流量模型是重中之重在链路中,全链路压测是模拟用户真实的访问路径构建请求,在生产环境进行实战演练。这里我以大家熟悉的买电影票的场景为例。如下图所示,整个链路的业务流量实际上是一个“漏斗模型”。至于每一层的比例,一是参考当前监测,二是参考历史数据计算平均值。漏斗模型推导示例:可见每一层(不同的应用,不同的服务)需要承载的真实流量不同,负载也不同。当然,实际场景更复杂。实际情况是多个场景混合并行。当用户A在调度期的时候,用户B已经被锁定在座位上了……我们要做的就是尽可能的贴近现实。还有一个最关键的要求:不能影响真正的线上业务。这就需要非常强的监控告警和故障隔离能力。关于系统容量和水位标准,这里给大家一个建议参考值:水位标准:单机房部署水位70%以下,双机房部署水位40以下%。/集群容量理论机器数:实际机器数*(集群水位/水位标准)双机房为什么是40%?如果一个机房出现故障,所有流量将切换到另一个机房。要保证整体不受影响,不会被压垮。计划系统的影片片段中,“侦测到不明生物入侵地球,联合国宣布进入一级戒备状态,立即发射飞船抵达现场。”计划系统。触发条件、级别、执行动作、事后情况都非常清晰,整个过程是闭环的(当然这个片段是我YY出的)。前面讲了“面向失败的设计”,就是尽可能的考虑到各种异常场景和特殊情况。这些零散的“知识点”和一些日常的复习经验,都可以作为未来的规划。当然我们前面提到的限流降级等应急措施,以及容错模式在整个计划体系中也是非常重要的。方案积累越丰富,技术就越成熟。总的来说,计划的生命周期包括:从大的层面上可以分为:提前制定和完善计划、日常演练、统一指挥、数据收集、计划的决策和执行。这里解释一下,有的计划是在满足特定的触发条件后自动执行的,有的则需要依靠人工决策,然后触发执行。在这里给大家做一个简单的demo,供大家参考:网络演练正所谓“养兵千日,用兵一时”。现实生活中的“消防演习”就是一个很好的例子。不然时间长了,我都不知道灭火器放在哪里,怎么打开?开封后还能喷泡沫吗?在我们的技术领域也是如此。你怎么知道你的计划是有效的?你怎么保证oncall值班机制没问题?怎么知道监控报警真的灵敏?不管是大促还是阿里内部的正常情况,都会时不时的进行一些线上演练。在蚂蚁,每年都有一次“红蓝两军对抗演习”,这是一次很好的“以战养兵”的实践。我们先来看一张故障画像大图。这里列出了一些典型的故障场景。大家不妨想想如何通过“故障注入”来验证系统的高可用。简要总结故障演练的主要场景和目的:预案的有效性、完整性监控告警的准确性、容灾能力测试的及时性,以检验故障是否会重现。检查oncall机制,验证团队在突发事件中的实战能力……“混沌工程”是近年来流行的工程实践。这个概念是由Netflix提出的。谷歌和阿里在这方面有丰富的实践经验(或者巧合的是,大多数拥有顶尖技术的公司都差不多)。通俗地说,就是通过不断地为现有系统“找麻烦”(进行实验)来验证和提高现有系统的高可用性和容错性。引用一句鸡汤是:“Whatdoesn'tkillmewillmakemestronger”,混沌工程原理:系统成熟度模型:失败/面向失败的设计是一种技术文化,应该在团队中大力推广。小结在这篇文章中,我主要讲解了稳定治理的常用手段和稳定保障体系。涉及的知识、手段、内容非常多。限于篇幅,无法一一详述。读者还需慢慢消化,更重要的是反思现状,努力改进,欢迎大家留言讨论。作者:丁浪简介:目前在一家创业公司担任高级技术架构师。曾就职于阿里文娱和蚂蚁金服。具有丰富的稳定性保障和全链路性能优化经验。建筑师社区特邀嘉宾!
