高可用性指的是系统如何保证比较高的服务可用率,在出现故障时如何应对,包括及时发现、故障转移、尽快从故障中恢复等等
可用性的理解
理解目标
业界高可用的目标是几个9,对于每一个系统,要求是不一样的。研发人员对所设计或者开发的系统,要知道用户规模及使用场景,知道可用性的目标。
比如,5个9的目标对应的是全年故障5分钟。
拆解目标
几个9的目标比较抽象,需要对目标进行合理的分解,可以分解成如下两个子目标。
频率要低:减少出故障的次数
不出问题,一定是高可用的,但这是不可能的。系统越大、越复杂,只能尽量避免问题,通过系统设计、流程机制来减少出问题的概率。但如果经常出问题,后面恢复再快也是没有用的。
时间要快:缩短故障的恢复时间
故障出现时,不是解决或者定位到具体问题,而是快速恢复是第一要务的,防止次生灾害,问题扩大。这里就要求要站在业务角度思考,而不仅是技术角度思考。
设计:根据业务变化不断进行迭代
满足业务要求,快速上线。
即使都挂了,量也比较小,出现问题,重启、扩容、回滚就解决问题了。
迭代速度、研发效率
频率要低:减少出故障的次数
设计:根据业务变化不断进行迭代
以点评交易系统的演进过程为例。
幼儿时期:2012年前
使命:满足业务要求,快速上线。
因为2011年要快速地把团购产品推向市场,临时从各个团队抽取的人才,大部分对.NET更熟悉,所以使用.NET进行了第一代的团购系统设计。毕竟满足业务要求是第一的,还没有机会遇到可用性等质量问题。考虑比较简单,即使都挂了,量也比较小,出现问题,重启、扩容、回滚就解决问题了。
系统架构如下图所示。
少年时期:垂直拆分(2012-2013)
使命:研发效率&故障隔离。
当2012年在团单量从千到万量级变化,用户每日的下单量也到了万级时候,需要考虑的是迭代速度、研发效率。垂直拆分,有助于保持小而美的团队,研发效率才能更高。另外一方面也需要将各个业务相互隔离,比如商品首页的展示、商品详情页的展示,订单、支付流程的稳定性要求不一样。前面可以缓存,可以做静态化来保证可用性,提供一些柔性体验。后面支付系统做异地容灾,比如我们除了南汇机房支付系统,在宝山机房也部署了,只是后来发现这个系统演进太快,没有工具和机制保证双机房更新,所以后来也不好使用了。
系统演进如下图所示。服务垂直化了,但是数据没有完整隔离开,服务之间还需要互相访问非自己的数据。
青年时期:服务做小,不共享数据(2014-2015)
使命:支撑业务快速发展,提供高效、高可用的技术能力。
从2013年开始,Deal-service (商品系统)偶尔会因为某一次大流量(大促或者常规活动)而挂掉,每几个月总有那么一次,基本上可用性就在3个9徘徊。这里订单和支付系统很稳定,因为流量在商品详情页到订单有一个转化率,流量大了详情页就挂了,订单也就没有流量了。后来详情页的静态化比较好了,能减少恢复的速度,能降级,但是Deal-service的各个系统依赖太深了,还是不能保证整体端到端的可用性。
所以2014年对Deal-service做了很大的重构,大系统做小,把商品详情系统拆成了无数小服务,比如库存服务、价格服务、基础数据服务等等。这下商品详情页的问题解决了,后面压力就来了,订单系统的压力增大。2014年10月起,订单系统、支付系统也启动了全面微服务化,经过大约1年的实践,订单系统、促销系统、支付系统这3个领域后面的服务总和都快上百个了,后面对应的数据库20多个,这样能支撑到每日订单量百万级。
业务的增长在应用服务层面是可以扩容的,但是最大的单点——数据库是集中式的,这个阶段我们主要是把应用的数据访问在读写上分离,数据库提供更多的从库来解决读的问题,但是写入仍然是最大的瓶颈(MySQL的读可以扩展,而写入QPS也就小2万)。
这时系统演变成如下图所示。这个架构大约能支撑QPS 3000左右的订单量。
成年时期:水平拆分(2015至今)
使命:系统要能支撑大规模的促销活动,订单系统能支撑每秒几万的QPS,每日上千万的订单量。
2015年的917吃货节,流量最高峰,如果我们仍然是前面的技术架构,必然会挂掉。所以在917这个大促的前几个月,我们就在订单系统进行了架构升级和水平拆分,核心就是解决数据单点,把订单表拆分成了1024张表,分布在32个数据库,每个库32张表。这样在可见的未来都不用太担心了。
虽然数据层的问题解决了,但是我们还是有些单点,比如我们用的消息队列、网络、机房等。举几个我过去曾经遇到的不容易碰到的可用性问题:
服务的网卡有一个坏了,没有被监测到,后来发现另一个网卡也坏了,这样服务就挂了。
我们使用 cache的时候发现可用性在高峰期非常低,后来发现这个cache服务器跟公司监控系统CAT服务器在一个机柜,高峰期的流量被CAT占了一大半,业务的网络流量不够了。
917大促的时候我们对消息队列这个依赖的通道能力评估出现了偏差,也没有备份方案,所以造成了一小部分的延迟。
这个时期系统演进为下图这样:
未来:思路仍然是大系统做小,基础通道做大,流量分块
大系统做小,就是把复杂系统拆成单一职责系统,并从单机、主备、集群、异地等架构方向扩展。
基础通道做大就是把基础通信框架、带宽等高速路做大。
流量分块就是把用户流量按照某种模型拆分,让他们聚合在某一个服务集群完成,闭环解决。
系统可能会演进为下图这样:
上面点评交易系统的发展几个阶段,只以业务系统的演进为例。除了这些还有CDN、DNS、网络、机房等各个时期遇到的不同的可用性问题,真实遇到过的就有:联通的网络挂了,需要切换到电信;数据库的电源被人踢掉了,等等。
易运营
高可用性的系统一定是可运营的。听到运营,大家更多想到的是产品运营,其实技术也有运营——线上的质量、流程的运营,比如,整个系统上线后,是否方便切换流量,是否方便开关,是否方便扩展。这里有几个基本要求:
可限流
线上的流量永远有想不到的情况,在这种情况下,系统的稳定吞吐能力就非常重要了,高并发的系统一般采取的策略是快速失败机制,比如系统QPS能支撑5000,但是1万的流量过来,我能保证持续的5000,其他5000我快速失败,这样很快1万的流量就被消化掉了。比如917的支付系统就是采取了流量限制,如果超过某一个流量峰值,我们就自动返回“请稍后再试”等。
无状态
应用系统要完全无状态,运维才能随便扩容、分配流量。
降级能力
降级能力是跟产品一起来看的,需要看降级后对用户体验的影响。简单的比如:提示语是什么。比如支付渠道,如果支付宝渠道挂了,我们挂了50% ,支付宝旁边会自动出现一个提示,表示这个渠道可能不稳定,但是可以点击;当支付宝渠道挂了100% ,我们的按钮变成灰色的,不能点击,但也会有提示,比如换其他支付渠道(刚刚微信支付还挂了,就又起作用了)。另一个案例,我们在917大促的时候对某些依赖方,比如诚信的校验,这种如果判断比较耗资源,又可控的情况下,可以通过开关直接关闭或者启用。
可测试
无论架构多么完美,验证这一步必不可少,系统的可测试性就非常重要。
测试的目的要先预估流量的大小,比如某次大促,要跟产品、运营讨论流量的来源、活动的力度,每一张页面的,每一个按钮的位置,都要进行较准确的预估。
此外还要测试集群的能力。有很多同学在实施的时候总喜欢测试单台,然后水平放大,给一个结论,但这不是很准确,要分析所有的流量在系统间流转时候的比例。尤其对流量模型的测试(要注意高峰流量模型跟平常流量模型可能不一致)系统架构的容量测试,比如我们某一次大促的测试方法
从上到下评估流量,从下至上评估能力:发现一次订单提交有20次数据库访问,读写比例高峰期是1:1,然后就跟进数据库的能力倒推系统应该放入的流量,然后做好前端的异步下单,让整个流量平缓地下放到数据库。
降低发布风险
严格的发布流程
目前点评的发布都是开发自己负责,通过平台自己完成的。上线的流程,发布的常规流程模板如下:
灰度机制
服务器发布是分批的,按照10%、30%、50%、100%的发布,开发人员通过观察监控系统的曲线及系统的日志,确定业务是否正常。
线上的流量灰度机制,重要功能上线能有按照某种流量灰度上线能力。
可回滚是标配,最好有最坏情况的预案。
时间要快:缩短故障的恢复时间
如果目标就要保证全年不出故障或者出了故障在5分钟之内能解决,要对5分钟进行充分的使用。5分钟应该这样拆解:1分钟发现故障,3分钟定位故障出现在哪个服务,再加上后面的恢复时间。就是整个时间的分解,目前我们系统大致能做到前面2步,离整体5个9的目标还有差距,因为恢复的速度跟架构的设计,信息在开发、运维、DBA之间的沟通速度及工具能力,及处理问题人员的本身能力有关。
生命值:
持续关注线上运行情况
熟悉并感知系统变化,要快就要熟,熟能生巧,所以要关注线上运营情况。
了解应用所在的网络、服务器性能、存储、数据库等系统指标。
能监控应用的执行状态,熟悉应用自己的QPS、响应时间、可用性指标,并对依赖的上下游的流量情况同样熟悉。
保证系统稳定吞吐
系统如果能做好流量控制、容错,保证稳定的吞吐,能保证大部分场景的可用,也能很快地消化高峰流量,避免出现故障,产生流量的多次高峰。
故障时
快速的发现机制
告警的移动化
系统可用性的告警应该全部用微信、短信这种能保证找到人的通信机制。
告警的实时化
目前我们只能做到1分钟左右告警。
监控的可视化
我们系统目前的要求是1分钟发现故障,3分钟定位故障。这就需要做好监控的可视化,在所有关键service里面的方法层面打点,然后做成监控曲线,不然3分钟定位到具体是哪个地方出问题,比较困难。点评的监控系统CAT能很好的提供这些指标变化,我们系统在这些基础上也做了一些更实时的能力,比如订单系统QPS就是秒级的监控曲线。
有效的恢复机制
比如运维的四板斧:回滚、重启、扩容、下服务器。在系统不是很复杂、流量不是很高的情况下,这能解决问题,但大流量的时候就很难了,所以要更多地从流量控制、降级体验方面下功夫。
几点经验
珍惜每次真实高峰流量,建立高峰期流量模型。
因为平常的压力测试很难覆盖到各种情况,而线上的真实流量能如实地反映出系统的瓶颈,能较真实地评估出应用、数据库等在高峰期的表现。
珍惜每次线上故障复盘,上一层楼看问题,下一层楼解决问题。
线上出问题后,要有一套方法论来分析,比如常见的“5W”,连续多问几个为什么,然后系统思考解决方案,再逐渐落地。
可用性不只是技术问题。
系统初期:以开发为主;
系统中期:开发+DBA+运维为主;
系统后期:技术+产品+运维+DBA。
系统较简单、量较小时,开发同学能比较容易地定位问题并较容易解决问题。
当系统进入较复杂的中期时,就需要跟运维、数据库的同学一起来看系统的瓶颈。
当系统进入复杂的后期时,系统在任何时候都要考虑不可用的时候如何提供柔性体验,这就需要从产品角度来思考。
单点和发布是可用性最大的敌人。
可用性要解决的核心问题就是单点,比如常见的手段:垂直拆分、水平拆分、灰度发布;单机到主备、集群、异地容灾等等。
另外,系统发布也是引起系统故障的关键点,比如常见的系统发布、数据库维护等其他引起系统结构变化的操作。
https://blog.csdn.net/meituantech/article/details/80062421
https://www.sohu.com/a/135752858_639793
可用性的高低是使用不可用时间占总时间的比例来衡量。不可用时间是从故障发生到故障恢复的时间。比如,可用性 4 个 9 的系统(99.99%),它一年宕机时间不能超过53分钟(=3652460*(1-0.9999))。做到高可用系统,需要尽可能的降低故障发生的次数和减少故障持续的时间。
出现系统不可用的原因,一种是人为的,比如发布了有 bug 的代码、不规范的发布流程导致的宕机或者网站访问量过载造成的雪崩等;另一种则是非人为的,由于外部系统和环境的变化造成的,比如硬盘老化造成的故障、机房断电、电缆中断等。我们需要在复杂的外部环境下保证系统的高可用。以下总结了常用的高可用解决手段。
1 拆分
这类解决手段不是以减少不可用时间为目的,而是以减少故障影响面为目的。因为一个大的系统拆分成了几个小的独立模块,一个模块出了问题不会影响到其他的模块,从而降低故障的影响面。手段包括:
1.1 水平拆分
系统水平拆分成三层:接入层,服务层和数据存储层。将有状态和无状态的划分开来,接入层和服务层设计成无状态的,存储层是有状态的。无状态层的服务可以平行扩展,请求落到哪台服务器都没有关系。平行扩展也有利于系统容量的扩充,快速扩容应对突然爆发流量的冲击。
1.2 垂直拆分
根据功能垂直划分,拆成相对独立的模块。有的仅是服务层做了拆分,存储层共用。更为彻底的是,拆分与该系统的业务领域模型关联,一个领域模型划分成一个模块。在数据库层面,还可以分库分表拆分,这样一个库的损坏,不会影响到其他库。分库分表需要增加路由逻辑,及保证路由规则的一致性。
1.3 读写分享
也属于垂直拆分的一种。写请求的依赖主库,读请求的依赖备库。这样做,当出现故障的时候,可以只有读请求的流量,写服务暂时关闭,从而减少了故障的影响面。但需要关注数据一致性的问题。
2 降级
这类手段不是为了避免故障的发生,而是当故障发生后,怎么减小故障所造成的损失。比如,系统正常时提供的服务能力是 100%,出现系统故障后,我们有措施能让系统服务能力不直接降到了 0,而是还能提供 50% 的服务能力。
2.1 限流
限流,流量控制。当请求量超过系统的最大容量后,访问延迟就会增加,超过峰值的流量会拖累整个系统,出现宕机。因此,需要提前流量控制,对于超过峰值的流量,可以直接拒绝掉或者随机选择拒绝。限流结合业务自定义配置,优先保证核心服务的正常响应,非核心服务可直接关闭。
2.2 异步调用
系统进行拆分之后,会分成多个模块。模块之间的依赖有强弱之分。如果是强依赖的,那么如果依赖方出问题了,也会受到牵连出问题。这时可以梳理整个流程的调用关系,做成弱依赖调用。弱依赖调用通过消息中间件的方式来实现。
异步调用不关心返回结果,不会传递依赖方的错误,进而避免造成更大规模的不可用。
2.3 同步调用合理设置超时时间
对于不能异步化的,采用同步调用,需要注意设置合理的超时时间。过长的超时,会延迟结果等待时间,导致整体的链路调用时间延长,降低整体的QPS。
经验值:超时时间设置成平均响应延迟的2倍。
2.4 失败重试
要区分调用失败的类型。有些失败是短暂偶然的(比如网络抖动),进行重试即可。而有些失败是确定,那么重试反而会造成调用请求量的放大,加重对调用系统的负担。
经验值:重试的次数一般设为3次,再多次的重试没有好处。
2.4 兜底方案
在系统真的出现了不可用的时候,需要有兜底方案。比如一些提示安抚用户,或者有跳转链接转移用户的请求。
3 冗余
冗余,目的是避免单点故障。比如对于接入层和服务层,可以平行扩展机器部署,这样一台机器宕机,可以将请求转移到其他机器。数据层的冗余比较复杂,增加一份备份数据,需要考虑一致性的问题。按照分布式系统的 CAP 理论三者不可用同时满足的原理,为了满足可用性和分区容错性,就必须牺牲一致性,因此考虑使用弱一致性、最终一致性的解决方案来解决(此类文章很多,略)。
冗余备份有全量和增量之分,有热备和冷备之分。冗余可以是两台机器的主备冗余,可以是多机的集群式冗余。从部署来看,可以是跨机架、跨机房到跨城的备份。多机复制部署,上层调用采用负载均衡策略,还需要注意负载均衡设备的单点问题。
失败通知和失败切换
当集群机器某台机器出现了故障,或者某个进程挂了,能够快速的发现,并且告警通知出来。路由选择器能快速的切除掉这台机器,当恢复后又能自动的加入回来。
4 灰度发布
有个观点,单点和发布是可用性最大的敌人。线网出现了故障,查故障的原因,一个常用的办法就是追查下最近是否有发过版本,比较下发布前后的代码。
使用灰度发布策略,发布并且验证没问题后再全量发布。灰度发布的策略,包括搭建预发布环境,有专用的预发布机器;或者路由策略先摘除灰度发布的机器,验证正常后再加入该机器;或者采用UIN取模灰度策略,验证没问题后再取消灰度策略。尽量采用自动化发布,减少人为发布的流程。尽量选择在访问量低峰时段升级,减小影响用户群。
回滚机制
出现问题后,能有有效的回滚机制。涉及到数据修改的,发布后会引起脏数据的写入,需要有可靠的回滚流程,保证脏数据的清除。
除了发布流程外,还应该在其他开发流程上做规范,比如代码控制,集成编译、自动化测试、静态代码扫描等。
5 切换
切换之前需要做好监控。监控应该是贯穿于上述所有手段的。比如业务某个模块访问量要监控,依赖的调用方出问题要监控,某个机房故障了要监控,发布了服务要监控等。监控既包括系统层面的(比如CPU、内存、网络、IO、进程),还包括业务层面的(请求量、错误率、耗时)。监控的间隔需要支持到分钟级甚至到秒级的。
监控不是目的,监控没法保证高可用,切换才是目的,从故障的系统切换到正常的系统才能保证可用性。比如监控某台机器硬盘出问题了,那么会告警出来,然后使用一台新的机器替换。
切换可以是自动的,也可以是人工的。人工切换会有延迟恢复的问题,但能做到准确。自动切换,会比较快速,但必须要确保切换源是正常的,否则可能会引起更加严重的事故。切换后,要有实时的效果反馈。
https://www.zhihu.com/collection/345502946
本文是2015年肖飞在内部分享的《高性能高并发系统的稳定性保障》PPT内容。
性能、并发、稳定性三者关系
高性能:高吞吐量、低延时
公式:吞吐量(并发)=单位时间/平均延时
N-th% Latency:TP99, TP999
稳定性:低延时的稳定性标准为TP99/TP999是隐含的必要条件;系统的稳定性标准:高+可用;用户标准
吞吐量:QPS, TPS,OPS等等,并发。并不是越高越好,需要考虑TP99。用户角度:系统是个黑盒,复杂系统中的任何一环到会导致稳定性问题。SLA:在某种吞吐量下能提供TP99为n毫秒的服务能力。降低延时,会提高吞吐量,但是延时的考核是TP99这样的稳定的延时。
如何改善延时
你应该知道如下表格
高性能
原文:http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html
JeffDean
Disk random read IOPS:
IOPS = 1000 / (4 + 60000/7200/2) = 122
IOPS = 1000 / (4 + 60000/10000/2) = 142
IOPS = 1000 / (4 + 60000/15000/2) = 166
SSD random read IOPS:
IOPS = 1000000/16=62500
数字的启示
高速缓存的威力;
线程切换代价cache miss
顺序写优于随机写
局域网络快于本地HDD
大块读优于小块读
SSD解决随机读写
跨地域IDC网络是最大的延时
策略
关键路径:“28原则”(20%的代码影响了80%的性能问题,抓重点)、“过早优化是万恶之源”。不同解读;
优化代码:空间换时间:各级缓存;时间换空间:比如传输压缩,解决网络传输的瓶颈;多核并行:减少锁竞争;lesscode;各类语言、框架、库的trick;算法+数据结构,保持代码的清晰、可读、可维护和扩展;
通过性能测试和监控找出瓶颈
metric
高并发
原文:http://www.vpsee.com/2014/09/linux-performance-tools/
通过性能测试和监控:
单系统operf/jprofiler etc;
Java的一系列工具:jstat, jstack, jmap, jvisualvm,HeapAnalyzer, mat
分布式跟踪系统:Dapper,鹰眼等
benchmark
benchmark
原文:http://www.vpsee.com/2014/09/linux-performance-tools/
微观
内存分配
吞吐量和利用率的权衡
显式分配器:jemalloc/tcmalloc代替默认的ptmalloc
隐式分配器:JVM GC的各种调优
是否使用hugepagen预分配和重用:Netty的Pooled ByteBuf
减少拷贝:new ArrayList(int), new StringBuilder(int)
内存分配器利用率:减少内部或外部碎片;Page Table(页表), TLB(页表寄存器缓冲),减少TLB miss,pin cache。增加COW的开销, 与内存分配器的实现冲突。JVM的GC调优是很多Java应用的关注重点。
减少系统调用
批处理: buffer io,pipeline
使用用户态的等价函数: gettimeofday ->clock_gettime
减少锁竞争
RWMutex
CAS
Thread local
最小化锁范围
最小化状态,不变类
批处理增加了内存拷贝的开销,但是减少了系统调用开销,减少了上下文切换的影响。bufferio的例子:日志、网络读写。pipeline的例子:redis。
减少上下文切换
触发:中断、系统调用、时间片耗尽、IO阻塞等
危害:L1/L2 Cache Missing,上下文保存/恢复
单线程:基于状态机redis和Master/Worker的nginx
CPU亲和性绑定
ThreadPool的配置,不同任务类型不同的ThreadPool
几个例子:1、docker中线程池大小的核数自动设定;2、CPU节能模式;3、CENTOS-7.1内核BUG。
网络
内核TCP Tuning参数和SocketOption:net.ipv4.tcp_*
TCP Socket连接池
网络I/O模型
传输压缩
编解码效率
超时、心跳和重试机制
网卡:多队列中断CPU绑定;增加带宽:万兆、Bonding;Offload特性:ethtool -k eth0;UIO Driver: DPDK
连接池:减少握手、减少服务端session创建消耗。网络I/O模型:BIO、Non-Blocking IO、AIO;select/poll、epoll/kqueue、aio;netty使用nativetransport。Offload特性:ethtool-k eth0。 将数据包分组、重组、chksum等从内核层放到硬件层做。
如何提高吞吐量
改善和降低单机的延时,一般就能提高我们的吞吐量。从集群化上讲,因素就比较多。
宏观
提升系统扩展能力
应用的无状态架构
缓存/存储的集群架构:冗余复制(负载均衡、异构解除系统依赖);分布式(数据sharding , 副本,路由,数据一致性);切换
微服务/SOA
扩容
异步化
缓存
复制
通过复制提高读吞吐量、容灾、异构
通过数据分片,提高写吞吐量
程序双写:一致性难以控制,逻辑复杂,幂等性要求。完全把控复制和切换时机。异构系统唯一选择。 同步双写(数据一致性高,影响性能,不适合多个复制集); 异步双写(数据一致性差,性能高,适合多个复制集);CDC[Change Data Capture](canal,databus等)
底层存储复制机制:一致性由底层控制,对应用端透明。程序和底层存储配合切换
扩容
每年大促前的核心工作:该扩容了吗?现状分析;扩容规划(关键系统峰值20倍吞吐量);扩容依据(架构梳理、线上压测);
扩容checklist:前(部署、DB授权….);后(配置更新、LB更新、接入日志、接入监控….)
应用扩容、数据扩容、写扩容、读扩容
垂直扩容:加内存、升级SSD、更换硬件。数据复制、切换
水平扩容:数据迁移或初始化
现状分析:去年双十一到目前,峰值时的性能数据;软硬件性能指标;数据存储容量。
扩容规划;流量规划:核心系统20倍吞吐量;数据增长量规划;扩容依据;架构梳理;线上压测。
读扩容比写扩容难;读写分离。
异步化
解耦利器
削峰填谷
页面异步化
系统异步化
JMQ
状态机(worker)+DB
本地队列
集中式缓存队列
本地内存队列:实时价格回源服务响应之后,通过BlockingQueue异步更新前端缓存。本地日志队列:库存预占。集中式缓存队列:商品变更任务下发系统。
异步化的一些例子:
1、操作系统内核的高速缓存队列,磁盘延迟刷盘;
2、mysql数据库复制、redis复制;
异步化需要注意的是:
1、任务要落地;
2、不可避免的重复执行,需要幂等;
3、是否需要保证顺序、如何保证顺序。
缓存
久经考验的局部性原理
多级缓存:浏览器browser cache、cdn、nginx本地redis缓存、本地JVM缓存、集中式缓存…
缓存前置:2/8原则、单品页、实时价格、库存状态
一致性、延迟权衡
缓存主节点负责写,和最重要的校验
通过CDC监听数据库binlog主动更新缓存
CPU不是瓶颈,网络才是
优化编码,减少尺寸
优化操作
优化拓扑
如何保障稳定性
宏观
提高可用性
分组和隔离
限流
降级
监控和故障切换
可用性
可用性衡量指标:几个9
可用性度量:A = MTBF / (MTBF + MTTR)
减少故障、加长可用时间
减少故障修复时间(发现、定位、解决)
冗余复制、灾备切换,高可用的不二法门
如何快速切换?
切换的影响
监控、ThoubleShooting、软件质量的影响
可行性指标:999,一周10分钟;9999,一周1分钟不可用。可用性:从客户角度。可用性度量:A = MTBF / (MTBF + MTTR) ,其中MTBF表示mean time betweenfailures,而MTTR表示maximum time to repair or resolve。
高可用行性的成本和收益,好钢用在刀刃上。
如何快速切换:有可以切换的?可以不重启应用么? 操作快捷么?演练过么?
切换的影响:切换目标资源能否承受新增的压力;切换是否影响状态(数据的一致性、丢失问题)。
监控到位、即时,减少故障发现时间;监控全面,增加故障分析时可以参考的数据。
troubleshooting的能力,踩坑的精力, COE,问题本质、根源的追查。
软件质量:编码是否健壮、(异常处理、防御性、2/8原则)超时处理、日志是否全面合理、线程名称等等。
测试:case是否全面、自动回归。
上线:是否灰度:N+1, N+2;回滚方案、数据回滚。
分组和隔离
网络流量隔离:大数据单独部署,QOS;
业务系统隔离:秒杀系统独立出主交易;
流量分组:对使用者按照重要程度、请求量、SLA要求等因素分级
存储的分组:按照使用者重要程度、实时性要求等因素,将数据库的复制集分组
传统世界的例子:道路被划分为高速道路、自行道、人行道等,各行其道。
流量分组
举例:商品基础信息读服务。对使用者按照重要程度、请求量、SLA要求等因素分级,将服务实例和存储分组:交易、生产、网站、移动、promise、ERP…
读写分离
举例:商品主数据服务。按照使用者重要程度、实时性要求等因素,将数据库分组:ERP、POP、网站、大数据平台…
限流
限流原则:影响到用户体验,谨慎使用
区分正常流量和超预期流量:限流标准来自压力测试、折算
读少限,写多限
客户端配合限流
不同分组的限流阈值
各层限流手段
前置限流,快速失败:比如通过提供给调用方的JSF客户端,封装限流逻辑。
Nginx层限流:自主研发的模块;几个规则:账户,IP,系统调用流程。
应用限流:减少并发数线程数;读少限,写多限;DB限流;连接数。
降级
保证用户的核心需求
降级需要有预案和开关:确定系统和功能级别,是否可降,影响如何;降级需要有开关
非关键业务屏蔽:购物车的库存状态
业务功能模块降级:实时价格更新不及时;peking库,保订单管道、生产,暂停统计相关
数据降级:动态降级到静态;远程服务降级到本地缓存:采销岗服务
监控和切换
无所不在的监控:网络流量;操作系统指标;服务接口调用量、TP99、错误率…;日志;业务量变化;太多监控了,如何提高监控的质量
切换:切换开关;成熟的流程可自动化;数据的重要性、一致性,要求强一致的,可以人工介入;系统的指标没法判断、监控点不全的,需人工判断决定
review
review
Nginx层限流:自主研发的模块;几个规则:账户,IP,系统调用流程。
应用限流:减少并发数线程数;读少限,写多限;DB限流;连接数。
如何验证性能和稳定性
线上压测:两类压力测试场景(读业务压测、写业务压测);压力测试方案(从集群中缩减服务器、复制流量、模拟流量、憋单)
全流程演练:降级、切换等
读业务压力测试:是将线上业务隔离后,压测至系统临界点,通过分析系统在临界点时软硬件指标定位系统短板并优化。
写逻辑压力测试,如果数据具有不可恢复性,一定要提前做好数据隔离保护,如订单号压测,为避免影响线上业务,压测前后都要做好“跳号”以隔离线上数据。
从集群中缩减服务器。加大单台服务器的压力。大概估算出正常的集群规模能够承载的流量。
复制流量。主要通过 Tcpcopy 复制端口流量,多层翻倍放大流。
模拟流量。模拟流量主要脚本攻击工具和压测工具结合,主要用ab,siege,webbench,loadruner通过多台机器压测。分机房,按分支进行压测。
憋单。主要针对后续的订单生产系统压测。通过在管道积压一批订单,然后快速释放,形成对后续生产系统持续快速的冲击,达到压测的目的。
https://www.cnblogs.com/zengkefu/p/6372157.html
https://www.jianshu.com/p/be66a52d2b9b
从目前的实战经验来谈谈为了保证服务可用性应该考虑哪些方面(对于简单服务):
一:服务架构层面
(1)根据服务对象地区,考虑节点分布
(2)避免服务单点,至少双机
(3)防止代码之间干扰,避免稳定代码和迭代频繁代码放在一起,可以按照业务或者功能做服务分离。
(4)防止服务之前干扰,重要服务最好做隔离,单独部署
(5)防止数据库压力过大,不然,可能产生雪崩效应,可以根据业务特点做分库分表,加缓存等处理
(6)保证服务尽量有冗余处理能力
二:运维层面
(1)服务监控。比如磁盘、CPU、网络
(2)监控多级别,到达不同级别给出不同警告
三:代码层面
(1)保证代码异常不会导致服务挂掉
(2)保证服务是无状态的,可以支持水平扩展
https://blog.csdn.net/hanjungua8144/article/details/86238255
https://www.jianshu.com/p/cdc1b876329b
https://www.cnblogs.com/mlyflow/p/10486558.html
https://www.zhuanzhi.ai/document/2a149e71e57d9e30e5638b1a6a00d0c5
https://blog.csdn.net/u012562943/article/details/100879341
https://blog.csdn.net/weixin_34185560/article/details/87995213
https://www.sohu.com/a/114468695_411963
https://book.douban.com/annotation/49729748/
https://www.cnblogs.com/ming-blogs/p/10965053.html
拆分一般分为水平拆分和垂直拆分,这并不单指对数据库或者缓存的拆分,主要是表达一种分而治之的思想和逻辑。
1,水平拆分
水平拆分是指由于单一节点无法满足需求,需要扩展为多个节点,多个节点具有一致的功能,组成一个服务池,一个节点服务一部分请求量,所有节点共同处理大规模高并发的请求量。
2,垂直拆分
垂直拆分指按照功能进行拆分,秉着“专业的人干专业的事”的原则,把一个复杂的功能拆分为多个单一、简单的功能,不同单一简单功能组合在一起,和未拆分前完成的功能是一样的。由于每个功能职责单一、简单,使得维护和变更都变得更简单、容易、安全,所以更易于产品版本的迭代,还能够快速的进行敏捷发布和上线
https://blog.csdn.net/atom_pig/article/details/81278576
https://www.sohu.com/a/166790079_683048
稳定性建设是一个很大的话题,涉及到多个部门、各个内部平台之间的协作。这里我主要从整体的处理流程、故障生命周期的分阶段投入分别来说明滴滴如何处理故障,提高处理效率。
在公司层面,滴滴以内部竞赛的形式来促进公司整体的稳定性建设工作开展,是公司级的竞赛之一。每个季度将会总结稳定性建设最好和最差的服务,并有相应奖惩。稳定性技术竞赛在公平公正、数据优先、持续迭代等方面逐步完善的同时,也保障了各个团队的高度参与,积极投入。
稳定性建设在滴滴不是某个部门的重点工作,而是围绕着技术竞赛,全体团队都深度参与的事情。也正是得益于竞赛机制的良性循环,故障发生后,各个可能涉及到的团队都会同时高优跟进,使得故障处理效率得以大幅提高。
从技术思路出发,我们把故障处理分为预防、发现、定位、止损、恢复几个大的阶段。其中发现、定位、止损三个阶段是故障现场的重点阶段,故障处理的效率提升也主要是在这三个阶段。
快速发现除了传统的系统指标监控外,公司整体以业务不可用时长定义业务状态,当任一关键业务指标下跌一定比例,则认为当前业务不可用。这里最大的挑战是准确判断业务指标下跌,并且准确性难以保证,基于该背景,“核心业务监控”重点在异常检测上投入,实现智能报警,使得业务不可用能及时、准确的发现。
快速定位方面,我们的目标是定位到可以确定一个止损预案即可。而定位能力可以说是一个无底洞,比如公网问题需要公网监控、内部交换机问题需要内网监控、机器环境问题需要系统级监控等等,只有相应的监控覆盖之后,才能直观的定位问题,因此各个团队都在发力提升监控覆盖面。但随着监控越来越多,业务规模越来越大,如何快速从众多监控、变更中找到故障根因成为当前的一个难点,建设中的“事件监控”应运而生。事件监控的目标是将各维度的监控报警、各系统的变更以事件的形式整合为一个 timeline,从众多的事件中智能筛选故障根因。
快速止损方面,更重要的是平时对预案的建设,以此提升故障阶段的处理效率。在预案建设上,我们主要从时效性、完备性、可执行性三个方面综合来评估某个系统。时效性是指每个预案都应当有保质期,失效前或架构调整时需 review,根据预案的新鲜程度给予评分;完备性考察各关键系统对预案场景的覆盖率,我们从网络、程序、安全几个方面综合梳理了一个比较通用的场景列表以此来规范各个系统需要建设的预案;可执行性指预案不能只呈现在文字上,还需时常通过演练确定能否达到预期目标,根据预案的执行效率和最终效果进行评分。通过上述的评分机制来引导各个关键系统完善其预案,提升故障处理效率。
InfoQ:监控系统发现问题时,你们一般的处理流程是怎么样的?
张云柳:上面提到了“消防群”沟通机制来保证故障有条不紊的处理,但即使如此,由于业务规模及监控专业度的问题,仍然需要 involve 不少的同学一起进行问题排查。为尽可能规避各个人员信息量不对称的问题,我们维护了一个故障处理最佳实践,参考下图:
上图勾勒了每次业务故障时,我们的处理、排查思路。通过 BI 业务报警发现业务故障,随即通过“灭火图”快速圈定影响面。在圈定的影响面,通过各个性化的监控来具体定位原因,确定止损方案。更多关于定位环节的细节以及我们在智能定位上的尝试,请一定到现场共同探讨。
InfoQ:监控是避免故障的前提,可否谈谈你们监控系统的架构以及技术栈?
张云柳:监控建设确实是稳定性建设最基础的环节,目前公司内部自研的监控平台作为比较通用的基础监控,提供了采集、数据存储、绘图、报警策略等监控功能,伴随着滴滴的业务发展经历了几次迭代,形成了现在以 OpenFalcon 为基础的监控体系。相对于 OpenFalcon,滴滴的实现有一些自己的改进:根据滴滴用户习惯实现了一个多级索引结构、报警数据获取模式调整等。可以说基于 OpenFalcon 进行了深度定制。感兴趣的朋友可以看看作者的分享“”。
基础监控重点在于基础技术能力的输出,由于各个具体业务方向的特殊性,在展示上需要有贴合业务定制的风格。仅仅展示上的差异如果需要建设完整的监控系统那在各个基础的实现环节还是会有比较大的重复性。因此比较基础的技术能力、规范还是进行的统一的建设:
收敛数据采集方式
目前监控数据的来源主要有日志、运行时主动上报以及 DB 数据。对于日志及 DB 数据采集比较常见,这里主要介绍下运行时主动上报的采集。日志和 DB 采集需要运维同学单独对接具体的业务日志、DB schema,因此借鉴了开源的 statsd 的解决方案,通过业务代码埋点将状态数据上报给监控系统。
数据实时聚合
监控系统采集得来的数据,大多数时候是单个实例的数据。随着业务监控细化,将数据以 idc、来源渠道等等不同的维度聚合展现变成很基本的需求,为此实时计算能力建设成为监控中比较重要的环节。目前主要的实时计算是有实时计算团队基于 Spark Streaming, Flink 实现的。
数据存储能力
监控数据存储受数据量、数据格式及使用方式影响,使用的基础组件各有差别。目前主要使用的有 RRDTool、Druid 和 ES。
异常检测智能化
异常检测是监控数据体现意义最重要的一环。比较传统的方式是根据历史曲线变化人为设定一个上下界进行异常报警,在指标项数量小且业务稳定的情况下,该方式成本小、收效快,是个不错的选择。但是对于滴滴来讲,业务高速发展带来的是监控指标类别变化快、量级变化快。因此我们不得不寻求更加智能的手段进行异常检测,现在主要应用的检测手段有三次指数平滑、上下界训练、斜率判定等手段。
报警事件统一收敛,智能过滤
监控覆盖完善并且异常检测就位后,也带来了一些甜蜜的烦恼——报警轰炸。当一次真实的故障发生后,整条链路的监控数据都会异常,也因此各个系统都会开始产生报警。目前正在将各个系统产生的事件统一收敛到事件库,同时开展基于时间、底层报警优先的基础规则做一些过滤尝试。
监控基础能力建设大概从上述几个方向进行统一能力输出。在智能化方向上的探索目前主要是异常检测及智能定位。异常检测现阶段在监控上应用的比较成熟,当前核心业务指标的报警控制完全是基于预测算法来实现的,误报漏报都比较符合预期。智能定位方向上现阶段刚刚起步,目前正在进行相关的基础建设,比如事件库的统一收敛、事件分类、业务拓扑关系等。
InfoQ:从一开始简单的系统监控到现在独立的监控系统,聊聊你们就监控这件事的迭代思路?
张云柳:监控系统的发展确实经历了几次迭代,但不管如何演变,我觉得还是始终遵循着“快准稳全”来迭代的。
从最初使用 Nagios 进行系统监控,到开始自研运维平台及监控系统,主要是贴合业务需求,也建立了滴滴基于 WorldTree 的运维平台体系;
后面 Odin 监控的迭代升级分别从数据准确性、监控系统稳定性两方面进行了很多细节打磨;
随着业务的不断发展,监控数据不断增涨使得查询速度开始受到影响,因此新版监控系统又在查询速度、数据存储上进行了重大升级;
与此同时,各类业务定制监控需求也开始全面铺开,使得监控方向日趋完备。
在上述基础能力不断迭代的过程中,需要运维、研发同学关注的事件也越来越多,因此整合监控能力,智能定位根因将是接下来监控方面的重要思路。
InfoQ:从以往滴滴出行的运维经历来看, 故障可以分为哪几类?
张云柳:故障方面,稳定性最大的敌人还是变更操作。目前变更引起的业务故障占比最多,主要包括手动操作、检查点不够完备以及高峰期操作等变更问题导致业务故障。针对这类变更问题,滴滴主要建立明确的规范、同时通过平台的技术保障规范的强制执行。其次是依赖的第三方服务故障导致业务受影响,对于该类问题加强多方联系沟通,建立日常协作保障机制,沟通解决方案,落实联动演练。最后是业务逻辑 bug、内部基础设施故障、服务隔离不合理等原因造成故障。
InfoQ:在 CNUTCon 全球运维技术大会上,你会重点为参会者分享哪些技术点?
张云柳:本次大会重点是给大家分享下滴滴在稳定性建设上的一些实践,主要是以下几点:
稳定性技术竞赛推进稳定性建设。 因为稳定性建设除基础的技术能力外,各团队的有效协作也是非常重要的点。所以首先给大家分享的是整个公司层面的稳定性技术竞赛,将会为大家分享滴滴稳定性建设如何做到全员参与?故障现场如何有组织、有纪律地救火。
从技术层面看故障的发现、定位、止损。该话题比较大,有如下几个细分方向:
从业务角度定义故障,智能诊断业务问题。各个系统级的故障比较常见,子系统故障不代表最终用户看到的业务是受损的。因此滴滴从用户的角度出发,定义各个产品线的重点业务指标,并采用预测算法等手段进行故障的智能发现。
基础监控能力建设,故障处理最佳流程梳理。监控能力将给大家介绍滴滴现有的整体监控建设情况,并详细分享上面提到的监控基础技术数据采集、实时聚合、数据存储及异常检测智能化等技术体系的建设以及其中遇到的教通用的数据处理问题。
事件统一收敛,智能定位探索。目前我们对报警类及操作类事件进行统一收敛,形成事件库。初步以 timeline 的方式展现便于大家发现可能的相关事件。另外在智能定位上基于时间、底层事件优先等规则进行的小规模实践也逐一分享给大家。