写点什么

极客大学架构师训练营 --- 习题

用户头像
李朋
关注
发布于: 2020 年 08 月 26 日

导致系统不可用的原因有哪些?保障系统稳定高可用的方案有哪些?



要论如何搞垮一家互联网公司,速度最快的不是产品经理的胡乱决策,运营的无休止的烧钱,客服人员对客户的冷漠,一定是系统核心功能持续不可用,一次大规模的故障就可能造成几千万的损失,市值大量蒸发,PR风险,甚至工信部的约谈。



一般的系统对全年可用性要求是99%或者99.9%,而影响着千万人日常生活的系统一般都要求全年系统可用性在4个9以上,这可不是一个容易达到的目标,那么如何避免故障,保障系统的稳定性与高可用,根据总结别人的方法和自身的经验,想要做好稳定性与高可用有16字心决

完善基础,做好自身,容错下游,防备上游

说起来影响稳定性的因素无非内部变化与外部变化,应对的心决也只有短短的16个字,然而魔鬼就在细节中,稳定性与高可用的建设中需要完善的基础设施支持,高效的制度规范,无数程序员的付出,上千万真金白银的投入。

完善基础

所谓工欲善其事,必先利其器。完善的基础设施建设是一切的开始,对于稳定性建设不可或缺的三个基础设施就是监控,压测,降级。

监控系统

监控能够量化系统的运行情况,是问题发现和系统优化的基础,没有监控就不能知道系统发生了什么问题,不能知道系统运行指标也就是无法做出针对性的优化。完善的监控需要将系统,应用,数据库,缓存,核心组件,第三方依赖纳入进来,做到面面俱到,并且能够第一时间就看到系统的核心指标,还需要对重要的应用指标,业务指标,投诉指标都设置好报警规则。分布式追踪系统也是发现问题的重要手段,需要核心功能链路上的服务都进行接入。

压测

如果说监控系统是系统的体检报告,那么压测就是体能测试。监控系统可以看到过去和当前的指标,压测则是对未来的预测。一般来说压测分为单机压测和系统的压测,单机压测比较合适压测某个特定的场景和项目,好处是压测实施简单,可以压测出一个系统极限值,缺点是和系统的真实运行情况有一定差距。系统间压测的最好形式就是全链路压测,全链路压测更接近系统的真实运行情况,可以找出应用,存储,缓存,依赖服务,网络情况的瓶颈。

降级

优雅降级是应对突发情况的重要手段,当系统发生故障时如何快速止损就是第一优先级的事情,如果没有相应的措施只能眼睁睁看着系统的故障发展,甚至造成多米诺骨牌效应。主要的降级方式有限流和熔断,可降级点有核心功能的非核心依赖,非核心服务,高并发接口,高资源消耗服务,对于高并发应用日志级别的动调调整也是重要的一个功能点。关于降级最重要的事情就是要日常演练一定要测试正确性,一定要测试正确性,一定要测试正确性,没有被验证正确的降级反而可能引起更严重的事故。

做好自身

控制资源合理使用

做系统设计,代码实现时一定要意识到所有的资源都是有限的,CPU,内存,磁盘,网络,线程,一旦某个资源超过负载,很容易出现线上问题。

CPU

良好的系统CPU的高峰期空闲率应该在40%以上,线程,锁,GC,代码循环这些因素都会影响CPU的负载,一般来说要重点预防的是死锁的情况(例如多线程情况下hashmap死锁),一旦死锁会出现CUP使用率骤增导致系统故障。

内存

内存的使用最重要的是避免OOM和内存泄露,一旦发生很可能就是线上事故。其次的重点是大数据量下不要使用内存缓存(Guava,Ehcache),频繁的GC会造成系统的可用性大幅降低,堆外内存的使用也需要重点关注,合适的数据压缩算法可以提供内存的利用率。

磁盘

日志打印要精简,需要定期对日志进行归档清理,并且要对磁盘的使用量设置监控,建议接入动态调整日志级别功能在发生问题时可以进行日志降级,对于高并发的系统建议异步输出日志。

线程池

根据多年的经验线程池是最容易出问题的点之一,使用线程池一定要设置队列大小,对于要求快速响应的系统来说队列长度设置过大也没有意义。线程池的core size和max size一定要合理设置,至少需要冗余高峰期3倍以上的流量,如果下游系统响应变慢/超时导致线程池打满,而线程池服务于核心功能的代码时非常容易出现故障。能否合理的设置线程池是判定程序员是否有资源规划,容错设计意识的重要标准。

MySQL

和其他无状态的业务服务不同,数据库作为有状态服务很难通过加机器解决容量问题,考虑到业务的发展,流量爆发的突然性,业界有着系统架构支持10倍增长,系统设计支持5倍增长,系统实现支持2倍增长的说法,最好在设计时就做好垂直拆分,水平拆分的工作,单个数据库的IO,网络,磁盘,CPU都有限制。另外一件重要的事情就是一定要建立合适的索引,避免慢查询,而过多的索引也会影响写性能。

避免单点

一定不要把鸡蛋都放在一个篮子里,不要求大的架构层面的异地多活,单元化,最基本要部署多个节点,通过负载均衡访问。

容错下游

被下游服务拖垮是1024种线上事故中非常经典的一种,显然我们不希望因为下游服务挂了自己的业务也跟着挂掉。为了保护自己的业务需要做以下几件事情。

有兜底,有熔断

对于外部依赖,一定要做到兜底和熔断,通过检测超时情况或者异常情况,当依赖服务不可用时可以使用本地的兜底策略,向上提供有损服务,保证业务柔性可用。

一定要设置超时时间

假设某服务的响应时间是50ms,线程池是200,假设系统没有超时时间设置,响应时间从50ms突变到5000ms,瞬间就能打满线程池导致服务不可用。为了保证系统不被下游的变慢拖垮一定要设置超时时间,建议内部接口都设置在200ms以下,依赖第三方公网服务的接口设置在1500ms以下。

谨慎设置重试次数

合理的设置重试次数即是保护下游也是保护自己,假设重试次数过多会导致服务响应时间的变慢(超时时间*重试次数),并且很容易引起下游的雪崩,导致下游长时间不可用。建议重要的接口设置2次重试,不必要的接口都不要设置重试次数。

防备上游

被上游陡增的流量打垮是1024种线上事故中非常经典的另一种,这里区分2种情况来讨论,一种是活动流量,需要预测活动预期的流量峰值,对高资源消耗的非核心功能提前降级,做好压测和降级预案,并设置一定的用户流控。另一种是内部的猪队友作案,可能顺手写个循环就带来10倍的流量增长,这种情况也要设置好系统的流量阀值,并且需要做到流量的按需分配,能够弄清楚流量的来源。

最后自己画了一张图,对可能的情况和故障做一个总结。



通过上文可以发现监控(量化过去和现在),压测(预测未来),降级(预防故障,快速止损)对系统的重要性。其中容错下游(熔断)和防备上游(限流)同样需要依靠降级服务实现,提到降级服务我们会想到Hystrix,除此之外滴滴出行有经验的老司机都会选择SDS(service downgrade system),SDS是一个由多位资深工程师开发的纯Java语言降级系统,支持访问量,并发量,异常量,超时时间等多个监控指标的降级点,支持滑动窗口,令牌桶等多种算法,在数据可以化方面支持多种数据源(MySQL,MongoDB,Elasticsearch,InfluxDB)的存储和展示,可以选择喜欢的数据源进行降级可视化配置。相信在不久的未来就会开源和大家见面。



用户头像

李朋

关注

还未添加个人签名 2018.04.09 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
请添加“极客大学架构师训练营”标签,便于分类
2020 年 08 月 27 日 09:55
回复
没有更多了
极客大学架构师训练营---习题