构建高可用系统之故障篇
对于构建高可用的系统而言,都希望尽可能的避免故障,但通常来说故障是不可避免的,要尽可能做到的应该是在故障出现时能快速的屏蔽故障对核心功能的影响或快速修复,在这篇blog中,来分析下该如何更好的面对程序故障(这里就不讨论人工操作造成的状况),保障系统的高可用,由于这些更多的来自自己对厂内的经验的总结,必然会有一定的狭隘性,希望大家多多拍砖。
所有的系统必然都有其核心功能,我们把核心功能相关的操作称为关键路径,其他的功能称为非关键路径,对于系统而言,通常只有关键路径故障了,我们才认为其不可用,以下为造成关键路径不可用的一些场景和我们的应对方法:
1、非关键路径的故障
非关键路径的故障为什么会造成关键路径的故障呢,我们看看一些典型的场景:
Case I
A、B两个系统为非关键路径的系统,C为关键路径的系统,A、B、C都依赖了D系统,A系统出现故障,导致了对D系统的访问太多,由于D系统的总体处理能力是有限的,导致D系统处理不过来C系统的请求了,从而出现故障。
Solution
对于这样的状况,我们选择的方法是在部署期间通过路由将关键路径的系统和非关键路径的系统隔离开,例如A/B访问的是D集群中的一组机器,C访问的是D集群中的另外一组机器。
同时还提供了D关闭某访问者的功能的支持,这样可以在未做隔离的情况下,一旦出现故障,可以考虑先将资源都提供给关键路径的系统使用,从而临时避免故障。
可能有些同学会提到干脆把提供给A/B的不同接口拆分出了做单独的系统得了,这在很多时候是不太好做的,一方面会带来很高的维护成本,另一方面很多时候有互相依赖的问题。
Case II
还是上面的场景,不同的是B系统调用D系统时,D系统在处理B的请求时需要消耗较多的资源,这样当B系统对D系统的访问稍微多一点后就导致了D系统处理C系统的请求的资源必然也将下降,从而出现故障。
Solution
对于这样的状况,我们选择的方法同样是通过路由来进行隔离。
Case III
E系统上同时有关键路径的功能和非关键路径的功能,出现了非关键路径耗资源导致关键路径资源不够用,从而出现故障,例如一个web应用,用于处理请求的线程数必然是有限的,如果非关键路径处理慢了,导致线程消耗多了,这样关键路径能用来处理的线程数必然也就少了,因此就故障了,另外的例子还有连接池、CPU、Memory、IO等资源。
Solution
对于这样的状况,我们选择的方法是采用开关的方式来控制非关键路径,例如一旦非关键路径处理慢并影响到关键路径了,我们就关闭此非关键路径的功能,实现方式其实非常土,但非常有效,就是提供一个servlet,通过传入一些参数来打开或关闭某些功能,这招在很多时候都非常有效,也就是James Hamilton在他那篇著名的论文中说到的Graceful Degrade。
还有一种在大型系统里也很容易出现,就是你认为的非关键路径其实成为了关键路径,这种现象就很杯具了,对于这种现象,只能说必须管理好依赖,一旦没管理好,就将会出现看似一个不相关的系统出问题,核心功能也受影响了,另外一方面就是要简化系统,尽可能让关键路径不要出现太多的依赖,否则就意味着关键路径的稳定性取决于众多的依赖,那就痛苦了。
由于非关键路径不是系统的核心功能,因此我们应该尽可能做到非关键路径不影响到关键路径。
2、关键路径上的故障
关键路径上的故障同样也分成很多种,有些是只能通过修改代码来fix的,例如程序bug导致关键路径执行出错等,对于这类故障通常来说只能靠提高代码质量来保证,在此就不讨论了,而有些关键路径上的故障还是有办法来应对的,来看看一些典型的场景:
Case I
关键路径的系统部署在一台机器上,机器出现故障后(硬件、网络等)导致核心功能出现故障。
Solution
对于这种状况,要么采取冷备,要么就是集群了。
Case II
关键路径的系统部署在一个机房,当机房出现故障后导致核心功能出现故障。
Solution
对于这种状况,就需要做多机房的容灾了,当然,这会带来其他很多的技术挑战。
Case III
A/B两系统均为关键路径,A需要调用B系统,B系统处理变慢,导致A系统请求处理线程耗光,从而出现严重故障,类似的状况还有连接池,A系统依赖数据库,数据库的访问突然变慢,导致A系统很多请求在等待连接池,有些时候可能会因此导致启动的线程太多,最终内存消耗完毕,进程退出,这是最悲惨的状况,这是典型的慢现象造成的严重后果。
Solution
对于这种状况,我们选择的方法是一方面是超时时间设置的不会太长,另外一方面是在能不等待的情况就不等待,而直接拒绝,但像连接池这类的情况,通常也不太好直接采用拒绝的方式,因此我们选择的是控制等待的线程个数,避免影响太多线程,当然,这不一定能挽救故障,但对恢复而言会有很大的帮助。
Case IV
请求的流量超过了系统所能支撑的量,从而导致故障。
Solution
对于这种状况,我们选择的方法一方面是控制请求的数量,另一方面是容量规划,评估好系统能够支撑的极限量,但接近极限量时即进行扩容或启动保护措施。
集群场景中还会出现个别很高危的应用,例如A访问集群B时,必然要走中间的硬件负载设备或通过地址列表的方式去访问,这时如中间这个高危点出现故障,就得有一些简单有效的挽救方法。
总结一下为了保障系统的高可用,通常可采取的措施:
1、监测 & 报警
监测和报警有助于帮助你立刻知道故障,在做的好的情况下甚至可以做到提前知道故障要发生,怎么样做监测最好呢,当然,实现一套这样的系统也不容易,maybe scribe可以看看,系统的开发人员做这个才能做的好,因为只有熟悉系统细节的人才知道到底要监测什么和什么情况下要报警,才能保证关键功能是正常运转的。
2、隔离
采用简单有效的办法隔离开核心的功能以及其他功能所依赖的共同资源。
3、简化系统
系统依赖越多,就意味着系统要保障可用性就需要所有依赖的系统都保持稳定,因此要尽可能的减少系统的依赖。
4、优雅降级
在系统中留好一些控制开关,尤其是对外部依赖的点,以便在资源不够时更简单的将资源让出给核心功能使用,在促销类的活动中这招尤其重要。
做的更好的话甚至可以做到按功能关闭,按资源消耗关闭,按功能重要的等级关闭,:)
5、容灾
容灾一方面是依靠多机器、多机房等来容灾,另一方面是依靠提供一些简单有效的措施来避免高危应用的故障。
6、自我保护
保护好自己,无论依赖的系统出现什么状况或外部的请求量大、边缘参数等,都应尽可能保证自己不挂,能提供服务就尽可能对外提供服务。
7、容量规划
对系统能支撑多高的量要有一定的把握,以便做到合理的流量控制以及扩容。
在保障系统高可用时,必然还会有其他很多的措施,但在做这些措施的时候,一定要考虑是否带来了更大的复杂性,抑或是为了极端偶尔的异常付出了很大的代价,一句话来说,就是要考虑收益比,有些时候看似简单、土的方法往往是最管用的,而漂亮的方法很多时候都会在最关键的时候华而不实,最终就是最后一根稻草都没了。
但要保障系统的高可用,以上的这些措施其实都不是最重要的,最重要的是所有的开发人员都能认为自己所负责的系统的稳定是他自己最重要的职责,千万不要出现生产环境一出问题,只有一小撮“专家”去查问题,而不是熟悉系统的人去查,最好的状况是每个负责系统的人都会立刻去看看自己的系统是否有问题,当人人都有了一颗“稳定”的心,要保障系统的高可用就会变得容易很多,否则依靠再多的方法措施都是没用的。
建议继续学习:
- 基于MySQL的高可用可扩展架构探讨 (阅读:3727)
- 利用MySQL Cluster 7.0 + LVS 搭建高可用环境 (阅读:3388)
- 寻找适合你的MySQL高可用解决方案 (阅读:2967)
- ORACLE Fusion-io最佳实践 (阅读:2582)
- Oracle+Fusionio+Dataguard的高可用方案 (阅读:2523)
- 分布式系统设计系列 -- 基本原理及高可用策略 (阅读:2491)
- Oracle高可用架构 (阅读:2278)
- 构建高可用和弹性伸缩的KV存储系统 (阅读:2246)
- puppetca 高可用性以及负载均衡配置 (阅读:2038)
- 《火星救援》中你应该知道的5个高可用系统故障恢复原则 (阅读:2053)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:bluedavy 来源: BlueDavy之技术Blog
- 标签: 高可用
- 发布时间:2011-03-27 23:57:41
- [43] IOS安全–浅谈关于IOS加固的几种方法
- [43] 如何拿下简短的域名
- [42] Oracle MTS模式下 进程地址与会话信
- [42] 图书馆的世界纪录
- [41] 界面设计速成
- [39] 【社会化设计】自我(self)部分――欢迎区
- [39] android 开发入门
- [37] 读书笔记-壹百度:百度十年千倍的29条法则
- [36] 视觉调整-设计师 vs. 逻辑
- [33] Go Reflect 性能