![ef6959882efaf9d97f05a219a337dc7b.png](https://img-blog.csdnimg.cn/img_convert/ef6959882efaf9d97f05a219a337dc7b.png)
在这个大家都在谈论大数据、云服务、AI( artificial intelligence )的时代,似乎都在关心自己的应用够不够“高大上”,有没有使用“最前沿的技术”,却忘记了万丈高楼平地起。夯实的“地基”才是一切“高大上”产物的基础。
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。
------《老子》
高质量应用程序的三大特性
- 可靠性 ( Reliability )
- 可扩展性 ( Scalability )
- 可维护性 ( Maintainability )
可靠性
Continuing to work correctly, even when things go wrong.
大家对应用程序可靠性的认知应该大致相同:
- 符合期望的结果( 应用程序将会在执行一系列操作后给用户一个期望的结果 )
- 容错 ( 应用程序可以容忍一些不正确的行为并正常运行 )
- 高性能 ( 应用程序要有低延迟和高速计算处理能力 )
- 安全 ( 应用程序将拒绝任何未得到授权的操作 )
可靠性有多重要?
这个问题其实很简单。在这个飞速发展的时代,自动化技术将应用在各行各业。飞机、高铁、汽车等交通出行也都会用到应用程序。想象一下,你坐在飞机上,飞机自动巡航系统由于低可靠性,开始不按照规划路线行驶。这将是一件多么可怕的事情。
![217f0bd7135e8acd3862aa74082d98b7.png](https://img-blog.csdnimg.cn/img_convert/217f0bd7135e8acd3862aa74082d98b7.png)
硬件故障
硬盘崩溃,RAM故障,停电,拔出了错误的网络电缆。都是日常非常常见的硬件故障,相信你也深有体会。
因此,备份数据、备用服务、备用发电机则成为预防硬件故障的“日常操作”。它们可以在故障发生时最快的时间内帮助你恢复系统。
软件故障
- 软件工程师们经常遇见的bug ( 可能由于一个标点或一个空格 )
- CPU,内存,硬盘的错误使用,导致StackOverflow等问题。
- 由于一个小的错误引发的整体崩溃 ( 缓存穿透、雪崩 等)
- ......
相比硬件故障,软件故障则令人头痛了。通常软件错误更加的难以发现和处理。并可能带来很坏的结果。例如 数据丢失,集群崩溃等。我想最重要的应该是你会被扣工资和绩效。
为了保住我们那“碎银几两”,我们可以为应用写全面的测试,建立软件监控、自查系统来保证软件正常运作。
人为操作失误
在一项关于互联网服务的研究(David Oppenheimer, Archana Ganapathi, and David A. Patterson: “Why Do Internet Services Fail, and What Can Be Done About It?,” at 4th USENIX Symposium on Internet Technologies and Systems (USITS), March 2003.)中发现。硬件故障(服务器或网络)仅占故障率的10–25%,运营商的配置错误则是中断的主要原因。
预防策略:
- 精心设计的软件架构和文档。
- 构建非真实数据的测试环境。
- 全面覆盖的自动化测试CI。
- 提供应用回滚机制。
- 全面的集群监控与报警机制。
- ......
可扩展性
If the system grows in a particular way, what are our options for coping with the growth?
How can we add computing resources to handle the additional load?
当公司越来越大,用户量从10000 -> 3000000的时候,随之而来的就是请求数量的增加、系统延迟增加,负载压力变大。我们日常说的QPS( Query Per Second ),QTS( Transactions Per Second ) 也就是用来描述系统性能的。这时应用程序的可扩展性起到相当重要的作用。
垂直扩展 ( Vertical scaling )
提升单机处理能力,提升单机的CPU算力和核数、带宽、硬盘内存等。
![b86863965a26b967135203e712541c7d.png](https://img-blog.csdnimg.cn/img_convert/b86863965a26b967135203e712541c7d.png)
水平扩展(Horizontal scaling)
将服务负载分布到多台机器上,实现负载均衡。
![e03d6523fdaff1f9047fb759cf2e40c7.png](https://img-blog.csdnimg.cn/img_convert/e03d6523fdaff1f9047fb759cf2e40c7.png)
应对负载
对于应用程序的扩展需要考虑的因素很多,例如垂直扩展虽然简便,但是一台高配置的机器的价格肯定也是非常昂贵的。水平扩展虽然价格不是很昂贵,但系统架构复杂度将显著提升,将会遇到分布式系统的一些问题,我们日后会进行讨论。
所以,现实世界中的优秀架构需要将这两种方法务实地结合。例如,我们现在都是“无状态服务( statusless service )”,跨多台机器部署该服务非常简单,但将带状态的数据系统从单节点变为分布式配置则可能引入许多额外复杂度。出于这个原因,常识告诉我们应该将数据库放在单个节点上(垂直扩展),直到扩展成本或可用性需求迫使其改为分布式。
一个良好适配应用的可扩展架构,是围绕着假设(assumption)建立的:哪些操作是常见的?哪些操作是罕见的?这就是所谓负载参数。如果假设最终是错误的,那么为扩展所做的工程投入就白费了,最糟糕的是适得其反。在早期创业公司或非正式产品中,通常支持产品快速迭代的能力,要比可扩展至未来的假想负载要重要的多。
可维护性
差的代码风格就像一座很大的屎山,每次你想修正一个bug,你的工作就是爬到屎山的正中心去。
大家是否有过这种经历,当你接手一个项目,打开代码,没有任何注释,满屏幕的if-else,糟糕的代码缩进和不知什么意思的英文方法名。然后你就很抓狂,开始骂上一任接手者。
或是当需要改动一个小小的配置时,由于你写死在代码里,导致需要升级系统才能实现而被领导责怪。
综上所述,我们为了保护软件的可维护性,我们可以以这样一种方式来设计软件:在设计之初就尽量考虑尽可能减少维护期间的痛苦,从而避免自己的软件系统变成遗留系统。为此,我们将特别关注软件系统的三个设计原则:
- 可操作性(Operability)
- 简单性(Simplicity)
- 可演化性(evolability)
可操作性
我们可以构建良好的监控和管理机制,例如:
- 对系统的全面监控,提供实时系统状况
- 自动化构建,版本发布系统
- 提供接口使得管理员可以控制系统状态
简单性
我们应使用好的设计模式来封装和抽象功能,例如高级编程语言是一种抽象,隐藏了机器码、CPU寄存器和系统调用。 SQL也是一种抽象,隐藏了复杂的磁盘/内存数据结构、来自其他客户端的并发请求、崩溃后的不一致性。
记住,在还没弄清楚需求未来的变化的走向的时候。过早优化不仅可能导致你无法很好地实现新的需求,而且对优化的预期的猜测有可能还是错的,导致实际上除了把代码变复杂以外什么都没得到。
可演化性
为了保证系统的可演化性,我们需要时刻进行一个重要的工作来保证系统的可靠性和低耦合。那就是“重构”。我记得在读《 Refactoring: Improving the Design of Existing Code》这本书的时候里面提到 :”当你想要添加一个功能时发现之前的逻辑很复杂,那么,你就需要重构它“。
为了保证重构的代码不会影响之前的逻辑,我们需要为系统添加全面的测试,来保证系统的可靠性。不知大家是否遇到过类似情节,“实在看不下去一段代码想去重构它,但是由于没有测试,害怕会影响之前逻辑,最后还是放弃了。”
总结
这次我们探讨偏向软件工程领域的如何构建高质量的应用程序,并提出三大特性:
- 可靠性
- 可扩展性
- 可维护性
在大家日后的软件工程师生涯中,将会发现这三个原则的重要性。虽然看起来只有短短三点,但实现起来却总是不那么理想。
欢迎关注,每周一篇高质量技术推文。Topic: 分布式系统 服务端开发 中间件
![f86fde3931e804d505b62a8799c1767a.png](https://img-blog.csdnimg.cn/img_convert/f86fde3931e804d505b62a8799c1767a.png)