构建可靠系统的原则与实践

随着阿里技术的发展,我们的技术系统越来越成为社会的基础设施,对于这些系统的可靠性要求也就越来越高。但是实际上很多的基础的产品和系统确仍然会出现一些稳定性问题,那么如何才能构建可靠的系统呢?是不是制定非常严格而细致的规则就可以做出可靠的系统呢?

航空业的教训

在回答这个问题之前,我们先来看看对于系统可靠性要求非常高的航空业是怎么做的?美国的FAA是在航空安全领域事实上的权威,为了保证航空器的安全,FAA制订了非常详细而复杂的航空器认证规则,而这些规则是否就保证了航空器的安全了呢?

让我们来了解一下最近的两起空难:

2019年3月10日,埃塞俄比亚航空ET302航班在起飞六分钟后坠毁,飞机上载有149名乘客和8名机组人员。

2018年10月29日,印尼狮航JT610航班在起飞后约十分钟坠毁,飞机上载有181名乘客,和8名机组人员。

几百条鲜活生命的消逝,这是多么严重的后果啊!究竟是什么原因导致了这样的灾难呢?

这两起空难的共同点是都是波音的737MAX机型,并且都是在起飞后不久发生的空难。那么这个背后的原因是什么呢?虽然官方的调查还没有结束,但是民间的分析指向了同一个原因,那就是这款机型的设计问题。

上图展示了Boeing 737 MAX的CFM LEAP引擎,值得注意的是和一般民航飞机不同的是,引擎的上沿和机翼平面几乎齐平。为什么会这么设计呢?这是因为波音737系列最早是上世纪60年代设计的,当时的引擎的直径小很多,外形更加细长,而机翼的高度是和引擎直径相匹配的。但是随着技术的发展,更新更省油的引擎直径变得越来越大,这时候原来的机翼高度无法满足更大直径引擎的安装空间,要想调整机翼的高度则需要改变起落架的设计,改变起落架的设计则需要改变起落架收起时相关机体位置的设计,而机体设计的变化会带来更多的变化从而会被FAA认为是一款全新型号的飞机,而全新型号的飞机则需要经历完整的FAA认证流程,会带来巨大的时间和经济成本。为了避免这样的成本波音选择了将引擎前移并且提升高度,但是这样带来了另外一个问题,由于空气动力学方面的原因,飞机会变得静不稳定,特别是在起飞阶段,引擎的推力会导致飞机迎角过高进入危险的失速状态。为了回避这个问题,波音引入了一个自动控制程序MCAS,通过读取迎角传感器的数据判断飞机是否迎角过高,如果过高的话自动控制飞机降低迎角,从而保证飞机的安全。

那么这么一套保证飞行安全的系统和空难有什么关系呢?事实上MCAS系统工作得非常好,根据波音自己的统计,Boeing 737 MAX系列已经完成了数十万次的安全起降。但是问题在于当传感器工作不正常时,MCAS有可能会根据错误的迎角数据做出错误的判断和动作,也就是在不应该降低迎角的时候降低迎角,导致飞机直冲地面。

一起后果扩大的故障

回到我们的工作中,前不久我们碰到了一起系统故障,其过程有一定典型的意义,为了描述方面,这里隐去一些具体细节,简单说一下故障的过程。
开始的时候,由于某些原因导致缓存命中率有所下降,而缓存命中率下降导致了数据库load升高,而数据库load升高以及可能的慢SQL导致了部分请求在获取DB connection的时候超时,从而引发了exception。当exception发生的时候,为了保证系统的可用性,系统逻辑进入了一段兜底逻辑,而这段兜底逻辑在特定的条件下产生了错误的返回,从而导致线上脏数据,而这些脏数据带来了业务资损和大量的人工订正数据的成本。

这个故障处理的过程并不是重点所以不再赘述,我们要问的是为什么一个简单的exception会导致这么严重的后果呢?

两个事例的共同点

如果我们仔细去观察上述两个事例,我们会发现其中有如下几个共同点:

  • 为了好的目的而引入了非常简单的备用逻辑直接保证“效果”
  • 这些备用逻辑在绝大部分情况下都能正常工作
  • 但是在极端情况下这样的逻辑失效了,并且产生了严重的后果

换句话说,系统设计者在尝试用非常简单的逻辑去解决一个实际上复杂的问题,虽然实际上并没有完全解决问题,但是因为这样的逻辑能够通过大量的测试(或者合规检查),所以系统设计者“假定”问题得到了解决,从而放心地应用到了生产环境。

那么一个非常复杂的问题是否真的能够通过一个简单巧妙的办法解决吗?

没有银弹

在系统设计领域,我们通常会把问题的复杂性分为两类,分别是偶得复杂性,实质复杂性。

偶得复杂性 Accidental Complexity

所谓偶得复杂性是指由开发者自己在尝试解决问题时引入的复杂性挑战,一般而言是由解决问题的方法和手段带来的,对于特定的问题,不同的方法会带来不同的偶得复杂性。

实质复杂性 Essential Complexity

所谓实质复杂性是由事务本身所决定的,和解决方法无关。

对于偶得复杂性,通过变换解决办法是有可能用简单的办法来解决的,但是对于实质复杂性,我们是无法通过改变手段来解决的,而必须采用相应复杂的方法来解决问题。换句话说对于实质复杂的问题,不要指望有银弹

上述事例中,实际的环境和问题是存在比较大的实质复杂性的,然而我们却试图通过一些非常简单的逻辑去解决问题,从而带来了严重的后果。

快速失败 Fail Fast

那么要想防止这类问题,设计高可靠的系统要怎么做呢?
这里我想介绍一条反直觉的软件设计原则,快速失败(Fail Fast):

In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure.

这是一条反直觉的原则,大部分人听说这条原则的第一反应是这样不是让系统变得更加脆弱了吗?

实际上并不是,原因在于我们不能停留在某一次的失败(failure),而是需要观察完整的过程,如下图所示:

当问题发生时,系统立即停止工作,由人工介入找到并以合理的方式解决根本的问题,然后系统恢复运作。通过这样的选择,我们就能够更早更容易地暴露问题,每当系统发生问题之后,真正的根因会更快得以解决,所以最后我们就能得到一个更加可靠的系统。

需要说明的是,快速失败不是说系统设计不处理任何问题到处失败,而只是在面对essential complexity的时候,不要尝试用一个简单粗暴的方案去解决,要么就用一套合理的机制设计去解决它,要么就fail fast把控制权交给系统上层决策,通常来说最终可能会回归到人,由人来分析和处理问题。

根据实际的工作经验,我还发现一个有意思的现象,很多系统的设计者往往高估一些能轻易想到的问题的严重性,而低估那些想不到的问题的严重性。上述的软件系统故障的例子中,如果异常往外抛出,问题的后果可能仅仅是某些操作人员的部分操作失败,用户可能会重试,也可能会开工单把问题反馈上来。只要我们处理工单的同学及时响应并且解决根本问题,这个问题就不会演变成一个比较严重的问题。但是不抛出异常通过备用逻辑来兜底一旦失败,会导致比较严重的后果。如果系统的设计者能够认真衡量和计算这些后果的差别,就会做出更加合理的选择。

当然还有一点就是快速失败会把更多数量的问题暴露在用户面前,让用户在心理层面有不好的影响,但是需要注意的是不暴露问题不等于解决问题,暴露问题只是让大家看到了问题而已。为了更好更早地暴露问题,一方面我们需要引入更完备的测试防止问题进入生产环境,另外一方面也需要引导系统使用者以更加客观实际的心态来接受系统中的问题。

不要重复自己 Don't Repeat Yourself

和构建稳定系统相关的另外一条原则是软件工程中常说的DRY原则,也就是:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

这一条和稳定系统的关系在于,通过合理的复用设计,能够大幅度提高系统的可测性,降低调试问题的难度,提高系统的可维护性,背后的逻辑还是比较简单的,这里不再赘述。

如何实践上述原则

那么要想实践上述原则构建可靠的系统需要注意哪些方面呢?
结合自己的工作经验,我认为主要是这么几个方面:

所有的原则都是有代价的

世界上没有免费的午餐,借用之前Choice课程老师的一句话,坚持价值观都是有代价的,我们也可以说
坚持原则都是要付出代价的。
这里的代价包括工作量,短期结果,解决问题的难度,带来的项目风险等等,系统的设计者需要做出合理的权衡,付出一定的代价才可能应用上述的原则。

刨根问底,5 whys找到根因

当问题发生时,最重要的事情在于找到问题的根因,只有我们解决了根本的问题,系统才会真正变得健壮起来,否则都只是假象。我们可以用 5 whys 的办法来找到问题的根本原因。

回归测试保障

个人认为自动化回归测试相当于汽车的安全带,我们需要构建覆盖度高的自动化回归测试保障体系,从而更早更好地发现问题,减少对于最终用户的冲击,把问题扼杀在萌芽状态。

让团队养成好的习惯

不管是一个开发者,还是一个开发团队,坚持原则并不是临时起意,而需要成为习惯。只有把原则变成习惯的个人或者团队,才能够真正贯彻这些原则。所以平常工作中某些看起来没有必要的坚持原则,实际上有助于习惯的养成,而当原则成为了团队的习惯,这些原则才能在需要的时候得以实践,获得回报。

不要把fail fast曲解为快速试错

可能有人会认为fail fast就是快速试错,也就是不断尝试,碰到正确的为止。需要强调的是快速失败需要很好的设计和机制保证。

后记

以上是我对于构建可靠系统的思考与实践总结,最近做了一次分享但是感觉没有讲好所以写下来,欢迎讨论和拍砖。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

看完就能独自把集群搭起来!Hadoop HDFS完全分布式环境搭建以及技术详解

作者 | 慢慢变成大佬责编 | Carol出品 | CSDN云计算(ID:CSDNcloud)在文章开始之前,作者想要告诉大家:读懂本篇文章,能让小白快速入门,并且能够搭建完全分布式的集群,以及能对Hadoop …

使用idea创建JavaWeb项目

【第一步】 File—New—Project 【第二步】 选择Java Enterprise版本,然后配置tomcat 注意:这里关联的tomcat home指的是tomcat的解压目录(bin目录的上一级目录); 【第三步】 选择使用模板创建项目 【第四步】 填写…

阿里云栖开发者沙龙PHP技术专场-聊聊服务稳定性保障这些事

本文主要带大家了解服务稳定性的重要性和相关策略。策略大概分两部分,第一方面从架构层面介绍保障服务稳定性的常见策略(限流,降级,隔离,超时,重试和集群)。第二个方面是从流程方面(…

ccs读取dat文件c语言程序,TMS320DM642学习----第六篇(CCS中.dat文件类型详解)

1、如下为.dat文件中文件头的基本格式:MagicNumber Format StartingAddress PageNum Length [NewFormat]下面是分别的解释:MagicNumber:1651.Format:a number from 1 to 4, indicating the format of the samples in the file. Th…

阿里开发者招聘节 | 面试题06-07: MySQL的数据如何恢复到任意时间点

为帮助开发者们提升面试技能、有机会入职阿里,云栖社区特别制作了这个专辑——阿里巴巴资深技术专家们结合多年的工作、面试经验总结提炼而成的面试真题这一次将陆续放出(面试题官方参考答案将在专辑结束后统一汇总分享,点此进入答题并围观他…

通过接口操作MyBatis及数据库配置文件

优点: 不用每次实例化SqlSession配置优于硬编码减少sql书写错误的概率规范代码,面向接口服务 文章目录一、回顾二、如何通过接口操作MyBatis2.1. 文件结构2.2. 依赖jar包2.3. 表结构2.4. mybatis配置文件2.5. db配置文件2.6. 获取SqlSession工具类2.7. 基…

厉害!中国AI企业50强榜单!看完员工待遇,网友:我酸了!

有自媒体说,现在各大国对AI的部署,就像对“核武器”的部署一样,这份脑力和智力的比拼,超级大国都绝对不能输。在最近,赛迪研究院就发布了「2019人工智能企业综合实力100强名单」,BAT毫无疑问霸榜&#xff0…

蚂蚁金服SOFA开源负责人鲁直:不只是中间件,未来会开源更多

近日,技术媒体Linux中国的创始人王兴宇对蚂蚁金服SOFA开源负责人鲁直,就SOFA 5、ServiceMesh、Serverless、Seata等技术内容进行了探讨,以下为专访文章。 虽然我和鲁直在微信上已经联系很久了,但这还是第一次见面。交谈中&#x…

Redis radix tree源码解析

Redis实现了不定长压缩前缀的radix tree,用在集群模式下存储slot对应的的所有key信息。本文将详述在Redis中如何实现radix tree。 核心数据结构 raxNode是radix tree的核心数据结构,其结构体如下代码所示: typedef struct raxNode {uint32…

针对提高48V 配电性能的诸多思考!

作者:Phil Davies 众所周知配电网络 (PDN) 是所有电源系统的主干部分,但随着系统电源需求的不断上升,传统 PDN 承受着提供足够性能的巨大压力。 对于功耗和热管理而言,主要有两种方法可以改善 PDN 对电源系统性能的影响&#x…

android线性布局快捷键,【整理】Android图形界面知识学习与总结之:Linear Layout线性布局...

【背景】之前已经学习了:现在接着去学习:整理如下:Linear Layout1.LinearLayout是一个视图组合2.LinearLayout中的子视图只能已单个方向排列,要么是水平,要么是垂直;4.所有如果是垂直的列表,则每…

收藏!企业数据安全防护5条建议

引言:数据安全对企业生存发展有着举足轻重的影响,数据资产的外泄、破坏都会导致企业无可挽回的经济损失和核心竞争力缺失,而往往绝大多数中小企业侧重的是业务的快速发展,忽略了数据安全重要性。近年来,企业由于自身的…

容器安全拾遗 - Rootless Container初探

近期Docker 19.03中发布了一个重要的特性 “Rootless Container支持”。趁着五一假期,快速验证一下。本文参考了Experimenting with Rootless Docker 一文的内容,并且补充了更多的细节和上手内容。 Rootless容器背景与架构 Docker和Kubernetes已经成为…

android 参数 attrs.xml,使用attrs.xml自定义属性

控件有很多属性,如android:id、android:layout_width、android:layout_height等,但是这些属性都是系统自带的属性。使用attrs.xml文件,可以自己定义属性。本文在Android自定义控件的基础上,用attrs.xml文件自己定义了属性。首先&a…

直面PHP微服务架构挑战

在4月20日的阿里云栖开发者沙龙PHP技术专场上,云智慧Technical VP高驰涛为大家介绍了微服务的前世今生,分享了微服务架构实践中所面对的诸多挑战以及相应的应对策略。 本次直播视频精彩回顾,戳这里! 直播回顾:https://…

5亿整数的大文件,怎么排序 ?面试被问傻!

来源 | 程序员追风编辑 | Carol出品| CSDN云计算(ID:CSDNcloud)最近一家公司,面试官一上来,就问了我这么一个问题,我一脸懵逼,决定记录一下。问题给你1个文件bigdata,大小4663M&…

RabbitMQ 的延时队列和镜像队列原理与实战

在阿里云栖开发者沙龙PHP技术专场上,掌阅资深后端工程师、掘金小测《Redis深度历险》作者钱文品为大家介绍了RabbitMQ的延时队列和镜像队列的原理与实践,重点比较了RabbitMQ提供的消息可靠与不可靠模式,同时介绍了生产环境下如何使用RabbitMQ…

深入浅出网络编程与Swoole内核

在阿里云PHP技术沙龙专场中,阿里云邀请到php-nsq作者,pecl、Swoole开发组成员吴振宇分享了Swoole进程模型的原理与Swoole协程实现的原理。并结合具体开发案例讲解了Swoole在网络编程中的应用。 本次直播视频精彩回顾,戳这里! 直播…

Spark大数据分布式机器学习处理实战 | 博文精选

作者| 数挖小飞飞编辑 | Carol出品| CSDN云计算(ID:CSDNcloud)Spark是一种大规模、快速计算的集群平台,本文试图通过学习Spark官网的实战演练笔记提升作者实操能力以及展现Spark的精彩之处。本文的参考配置为:Deepin 1…

五四,阿里巴巴新青年了解下?

今天,橙子挖掘了几位程序员小哥的故事,他们是淘宝技术节上涌现出的“高手”。为了追求极致,代码也能成为一种艺术,看完之后也许你会对技术人有完全不同的认识。 1 伯灵:“技术作品代表我对技术的态度:解决…