系统稳定性设计原则:简单、冗余、标准化、健壮
系统稳定性设计原则:简单、冗余、标准化、健壮
2018-07-19 07:20
作者介绍
淇公,蚂蚁⾦服技术专家。热爱java和⼀些函数式语⾔,长期关注系统稳定性领域
⼀、差旅随想
因为base在分公司,需要经常去总部出差,所以搭乘飞机成了家常便饭,很多时候坐在飞机上会不由的
感叹,设计制造这样精密复杂的机器的那帮⼈真的是了不起,他们是怎样保证这样⼀台由⽆数零件组成的设备能够稳定运转的?
于是去简单查阅了⼀些资料,了解到⽆论任何情况下,我们所乘坐的客机从⽣产到投⼊运营,任何细节都是将可靠性放在第⼀位的,简单来说,体现在:
设计阶段:
⽅案尽量简化,冗余设计,采⽤成熟经过验证的技术,标准化,健壮性设计等等.
测试阶段:
⽓动⼒试验,环境试验, 可靠性与寿命试验,真实试飞,各项极限测试等等
运⾏阶段:
执飞前检查,运⾏状态监控,定期维护检修.
机组:
持证上岗,定期培训,考核
⼀架飞机在云端飞⾏,⽆数次⾯对⽓流的颠簸,起飞降落的冲击仍然能够保证安全可 靠,这背后从设计到制造再到维护,完全是⼀项系统⼯程, 需要极⾼的专业⽔平和责任⼼才能胜任, ⽽⼯程领域的实践经验通常都有相通性,作为⼀名软件开发⼈员,不禁要思考我们所开发,运营,维护的软件系统,同样是⼀项专业的系统⼯程, 同样需要满⾜极⾼的稳定性要求,⾯对汹涌⽽来的流量冲击,软硬件变化的影响仍然需要屹⽴不倒,我们需要怎么做? 对⽐苛刻的航空器可靠性要求以及保障策略和⼿段,我们可以从中学到什么?
⼆、概述稳定性保障
⾸先要明确稳定性保障时什么,在我看来,系统稳定性保障的内容包括:
稳定性保障通常来讲是指保障系统在运⾏, 运维过程中, 即时⾯对各种极端情况或突发事件仍然能够提供持续的, 可靠的服务能⼒.
此处所指各种极端情况或突发事件包括且并不局限于机房级故障, 城市级故障,线上故障, 线上业务量瞬时爆发, 持续快速增长, 系统服务器故障, 依赖数据库故障, 环境数据改变, 依赖系统故障等.
此处所指持续的, 可靠的服务能⼒是指受影响集依据突发情况严重程度,在业务可接受的时间范围内完成恢复或失败转移,继续提供正确的服务能⼒以及⾜够的运算能⼒.
看上去挺吓⼈的,总结起来就是系统⼀旦上线运⾏,那么⽆论⾯对什么情况都尽量不要出问题,如果出了问题也要求可以快速解决和恢复.
何其复杂的要求,良好的设计, 严格的,全⾯的测试验证,持续的运营维护,迭代,重构,升级,经验丰富的研发维护团队缺⼀不可,航空业以安全为最⾼准则,我们则以系统稳定作为底线.
三、怎么做系统设计
民⽤飞⾏器的设计原则包括⽅案简单,冗余设计,技术成熟,标准化,健壮.⽽软件系统的稳定性同样与设计⽅案息息相关:
职责清晰,单⼀的系统,其稳定性保障的难度会⼩于⼤杂烩,功能繁多的系统,不同的业务功能,其业务特征,业务形态,业务量,资源使⽤形式,依赖关系都可能不同,如果混合在⼀个系统中,将很难避免相互影响.
设计师在设计飞机时,会在可以达到⽬标的情况下,尽量选择简单的⽅案,这样⽆论从 成本节省还是维护效率上都有所提升,两点之间直线最短,对于系统设计也是⼀样的,⼀定不要过度设计和为了技术⽽技术,⽐如⼀组配置数据,数据量不⼤,实时 性要求也不⾼,就不要去引⼊分布式缓存,变更通知,简简单单的JVM堆内缓存+定时刷新就可以搞定,维护简单⽅便,否则技术栈的加深,维护节点的增加都会带来更多的不稳定因素。
冗余设计,对于飞机⽽⾔,体现在机组包括机长副机长,引擎⾄少双发,操作系统⾄少两套等等,于我们的系统⽽⾔,则通常体现在:
数据冗余,数据多份副本,DB⼀主多备,出现问题时可以快速切换.
计算能⼒冗余,服务器的计算能⼒始终保持⼀定冗余,在⼀组服务器出现问题时其他服务器可以接替提供服务,亦或在业务量波动时可以承受冲击。
⽹络,存储或其他基础设施冗余,出现意外时,⽐如断电,光纤断裂时均有备⽤可以实现切换,快速恢复。
技术成熟,标准化,通常来说,在企业级的应⽤中,技术选型是⾮常重要的,由于对可靠性有极⾼的要求,因此⼀种技术组件或框架,中间件在引⼊时要充分的考虑:
是否经过充分的验证
是否有⼴泛的⽤户基础,有⾜够有经验的开发⼈员
是否有完善的⽂档和技术⽀持
标准化则⽐较典型的运⽤于系统交互协议标准化,数据模型标准化,服务标准化,标准化带来的好处也⾮常显著:
规范系统之间的交互⽅式,便于维护,管理
提升效率,避免重复造轮⼦
降低技术学习曲线,解放⼈员劳动⼒
信息理解⽆偏差,数据流动顺畅
服务约定明确,能⼒清晰,对接效率⾼
健壮性对于飞机的安全⾄关重要,因此飞机所使⽤的设备,材料都经过各种严格的健壮 性测试,确保可靠性,对于软件系统⽽⾔,本⾝是个⽐较⼤的命题,⼀般来讲是指系统的容错能⼒,⽆论是⼀次错误的输出,或是⼀个依赖服务的崩溃都不应该影响 系统⾃⾝的服务能⼒和正确性,这⼀点本⾝不容易做到,但也有⼀些⼿段可以辅助我们尽可能做好:
良好的编码规范和严格的测试验证,⽐如对异常的捕获处理,对边界数值的验证避免单 点依赖,⽆论是系统单点还是技术单点,这包括关键依赖服务应该集化,多单元甚⾄多地部署,设计灾备⽅案,
⽐如DB 的FO,交互的同步转异步,RPC转消息等等,保证在依赖的服务出现问题时可以继续提供服务,同时数据可达成⼀致,信息可达,压⼒可控。
四、风险分析
商⽤客机在交付前会进⾏⼀系列的严格测试,这些测试来源于设计⼈员对于飞机将来的 使⽤场合,环境,以及可能⾯临的⼀些极端条件所分析出的可能存在的风险⽽针对性设计的,要求飞机能够承受住考验才可交付,对于应⽤系统的稳定性⽽⾔,道理 是相通的,不过按道理来说,系统设计和风险分析应该放在⼀起讨论,可是很多时候我们很难有机会重头开始设计⼀个系统,那么当我们接⼿⼀个系统时,需要对系 统进⾏全⾯的风险分析,这是实施稳定性保障的基础前提.
⼀般来说,风险分析需要包含⼏个⽅⾯:
对外提供什么服务
那些业务依赖本系统
系统的上游有那些
依赖的下游有那些,依赖的程度如何
系统的部署结构是什么样的
依赖什么数据,从哪⾥获取
依赖那些资源,资源使⽤模型时怎么样的
⽇常常见的维护操作是什么,都有谁参与等等
分析这些内容有什么好处呢,⾸先可以让我们看清系统对业务的影响情况,影响⾯,其 次,结合⽇常运维动作,链路关系,资源使⽤模型可以推导各⽅⾯可能存在的风险点和带来的后果,⽐如⼀个依赖节点耗时上涨了,带来的后果可能是业务处理耗时 整体上涨,甚⾄拖垮调⽤节点,这就是可能存在的风险点. ⼜⽐如运维⼈员可能会进⾏⼀些⽇常维护动作⽽缩容,这有可能导致系统出现容量风险等等.
当我们完成了风险分析,很⾃然的就可以进⼊到风险防范,治理阶段了。
五、风险防范三板斧
风险预防
简单来说就是质量+容量+灰度+回滚。
良好的质量保障是保证系统稳定运⾏的重要基础,如同飞机制造过程中的质量控制,精细到每⼀个部件,完成后还会进⾏覆盖到每⼀个细节的测试验证⼀样,我们的每⼀个迭代,每⼀次变更都需要有完善的质量保障体系进⾏覆盖, 确保变更质量符合要求,这套体系通常包含如下内容:
需求分析
系统分析,设计⽂档产出,评审
开发⾃测,交付测试,联调
持续集成覆盖
代码CR
整体交付报告review
这⾥⾯每⼀项的⽬的都是为了减少出错的可能,避免bug上线.
此外,系统从建设之初就应该考虑容量模型的数据是否符合预期以及⾼度可复⽤的度量 ⼿段的建⽴, 毕竟⼤家都不愿意乘坐⼀架机翼载荷不明的飞机,对于不同资源使⽤模型的系统⽽⾔,其容量模型⼀
般是不⼀样的,对于⼀般的在线服务应⽤系统⽽⾔基本上都是计 算密集型,容量⽅⾯主要需要关注的是 QPS-CPU-耗时 三者的关系, 我们需要知道:
⽬标QPS下, CPU使⽤是否处于安全⽔位之下, 响应时间是否可接受
多少QPS下,CPU⽔位达到危险状态或耗时升⾼到⽆法接受的程度
搞清楚这两组数据,我们就知道⽇常情况下的集容量以及极限情况下的集容量,可以⽤于指导计算资源的配置,不⾄于被汹涌⽽来的请求压垮.
这三个因素相互制约,任何⼀个不达标,则整体容量不符合要求. ⼀般来说,获取容量模型的⼿段包括:
获取单系统容量模型所进⾏的单机压测
获取完整处理链路容量模型所进⾏的全链路集压测
压测时,需要关注的是压测样本的仿真度问题以及服务器配置的差异,如果压测样本与真实情况偏离较⼤,那么获得的容量模型可能与真实情况相去甚远, 如果集机器配置差异较⼤,单机压测或者不全⾯的全链路压测都可能给出错误的指导数据.
然后来讲⼀讲灰度, ⾸先要明确的是,⽆论多么经验丰富的⼈编写的代码,经过多么严格的测试验证, 都⽆法百分百避免问题遗漏, 所以变更发布⼀定要具备灰度和监控的能⼒,⽆论采⽤什么样的灰度策略, ⼤部分情况下都可以将问题的爆发控制在可控制的范围内.⼀般可能采取的灰度策略包括:
按⽤户⽐例灰度
内部⽤户灰度->外部部分⽤户->全量
⽩名单灰度
按区域/地域灰度等等
灰度阶段发现的问题,可能是⽤户的⼀些反馈,意见,可以快速迭代进⾏优化,解决,某些可能就是真实的线上问题,会对⽤户体验甚⾄资⾦造成影响, 这时就需要紧急回滚,因此快速回滚的能⼒必不可少, 这要求做到如下⼏点:
具备良好的代码,数据版本管理能⼒,可以实现快速的替换
提前分析好回滚的依赖性,避免出现链路某个节点回滚其他节点还保持依赖导致整体出错。
风险预警
通常来说,我们乘坐的航班,驾驶舱⾥的设备会实时的对机件的运⾏状态进⾏监控, 地⾯雷达也会不断的监控飞机的动向,⼀旦出现异常则进⾏快速应对, 我们的系统也是⼀样的,必须具备对运⾏状态的监控能⼒, 合理有效的监控体系能够帮助我们及时发现存在的问题,进⽽采取进⼀步的措施来进⾏处置,这种监控体系通常是多层次的:
基础资源监控
⽹络
存储
服务器硬件,系统资源
通⽤中间件
DNS
流量调配
…
应⽤服务监控
业务量
来源分布
结果
耗时
成功率
依赖调⽤量
依赖调⽤成功率
依赖调⽤耗时
…
应⽤基础资源监控
服务器数量,分布
服务器内存
服务器CPU使⽤
GC情况
线程池使⽤情况
DB调⽤量
DB调⽤耗时
IO
磁盘占⽤
…
链路监控
业务整体成功率
业务链路处理整体耗时
报错量监控
问题节点监控
等等内容
在这些监控内容的基础上, 通常⼜要按照部署的集,区域,单元进⾏逻辑化的监控单元,便于进⼀步定位问题范围.
在监控的基础上,会配置各种各样的报警,关注各种指标的变化情况,每当出现异动都 会触发报警,通常这些报警都是基于专家经验⼈为设置的,这样的典型结果是⼀⽅⾯我们可以知道现在系统上出现了异动,另⼀⽅⾯是这种异动可能因为业务发展带 来的量级变化,偶然的抖动,不同时间段的差异等等原因带来海量的噪⾳,引发报警疲劳,⾯对这种问题,通常采⽤下列办法进⾏优化:
多监控点,多层次关联,低层次监控关联⾼层次监控,⽐如监控DB调⽤量的监控点与 业务调⽤量监控点进⾏关联,当DB调⽤增加时可以知道是由于业务量上涨导致,快速给出定位原因,避免不必要的恐
java技术专家慌, 这种关联的优势还在于为问题紧急程度提供评估, ⽐如某个依赖系统down掉了,对它的监控报警点关联到整体业务成功率报警点上,如果报警信息上明确告知没有影响到整体成功率,那么⼀般⽆需采取⼀些极端 的应急⼿段,反过来,则需要⽴即着⼿快速⽌⾎。
另⼀⽅⾯,引⼊⼀些智能算法,通过对业务规律,历史数据的分析,学习,输出⼀套⾃我更新,⾃我迭代的监控机制,可以⾃适应的识别那些常态化的变化因⼦,从⽽有效降低⽆效报警数量,达到精细化,⾼效的监控报警效果.
应急处置
所谓应急处置,则是指系统确实出现了异常情况,通过某些措施⼿段快速⽌⾎,恢复的过程。如同飞⾏员在飞机出现问题时决策返航,切换控制系统,关闭失效发动机⼀样,需要快速掌握情况后决策出合理的下⼀步动作来消除风险。
通常在接收到监控报警后,如果产⽣了⽐较严重或者可能诱发严重问题的情况,那么就需要进⼊应急阶段,应急处置应该是⼀套明确的机制和规范,指导⼈员进⾏快速有效的应急,这包括:
⼈员如何分⼯:
谁负责排查,谁负责关注业务运⾏情况,谁负责信息同步,谁负责决策,谁操作⽌⾎等等,并⾮所有
的事情都需要专⼈跟进,⽽是明确⾓⾊,可以⼀⼈担负多个⾓⾊.
如何决策:
什么样的情况下,应该采⽤何种⼿段进⾏⽌⾎处理,影响是什么,什么样的情况下可以进⾏恢复操作.
预案准备:
⾯对什么问题可以使⽤什么⼿段进⾏处置,达到⽌⾎的效果,通常⽽⾔,预案会包括回滚,降级,切换,扩容,重启等等,每⼀种预案都会针对特定的问题,也可能多重预案联合使⽤,视具体情况⽽定.
回顾复盘:
处置完成后收集那些信息,如何总结和产出优化提升任务.
应急处置机制应该在做在事前, 团队成员充分学习和掌握,并且每次应急后进⾏总结,对不⾜的地⽅进⾏提升,总之应急就是争分夺秒,⾼效的协作和准确的处置可以挽⼤厦于既倒。
六、在此之外
当我们分析完系统的风险,相应的建⽴了监控体系,应急处置机制,是不是就⾼枕⽆忧 了呢,答案肯
定是否定的,事物总是在变化之中,风险点可能随着系统演变⽽变化, 监控可能随之时效或遗漏,预案可能失效,⼈员经验可能过时,精⽓神可能懈怠, 如何应对? 我们还需要常态化的演练机制,不断的吸收出现过的问题进⾏反复的演练,⼀如飞⾏员要定期培训,考核,机组要经常进⾏应急,疏散等演练⼀样,通过演练,我们 可以达到以下效果:
验证监控系统有效性
验证预案有效性
验证团队应急机制执⾏,掌握情况
为潜在问题摸索应对⽅案
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论