乐趣区

关于后端:架构设计三原则

作为程序员,很多人都心愿成为一名架构师,但并非简略地通过编程技能就可能达成这一指标。事实上,优良的程序员和架构师之间存在一个显著的鸿沟——不确定性。

编程的实质是确定性的,也就是说,对于同一段代码,无论由谁编写,在何时执行,其后果应该是确定的(只管有可能存在 bug)。相比之下,架构设计实质上是不确定的。同一零碎,不同公司的架构可能存在较大的差别,但最终都能失常运行。在面对多种可能性时,架构师须要进行抉择,而这种抉择往往会让人陷入两难的地步。

例如:

是否抉择业界最先进的技术,还是抉择团队目前最相熟的技术?
是否抉择 Google 的 Angular 或 Facebook 的 React?
是否抉择 MySQL 或 MongoDB?
对于这些问题,架构师须要依赖本人的教训和直觉进行决策,因为架构设计畛域并没有一套通用的标准来领导架构师。然而,通过钻研架构设计的倒退历史和多个公司的架构倒退过程,能够发现一些共性准则,即适合准则、简略准则和演变准则。在遵循这些准则的根底上,架构师能够做出最好的抉择,克服不确定性。

适合准则

适合准则宣言:“适合优于业界当先”。

很多优良的技术人员都怀有强烈的技术情结,他们总想通过挑战自我,实现甚至超过业界领先水平,从而在年初 KPI 绩效总结中留下本人的印记。然而,这种做法往往会导致失败。在互联网行业,我见过许多“亿级用户平台”的失败案例,例如某个几个人规模的业务团队,雄心勃勃地想要和腾讯 QQ 一较高下,但最终却以失败告终。为什么会这样呢?

实现任何幻想都须要好高鹜远的付出。这里的“好高鹜远”次要体现在以下几个方面。

1. 将军难打无兵之仗。
大公司的分工比拟细,每个小零碎都可能由一个小组负责。但在大部分公司,整个研发团队可能只有 100 多人,某个业务团队可能只有十几个人。在这种状况下,想要实现相似于几十人团队能力实现的事件,并且还要做得更好,难度可想而知。

2. 罗马不是一天建成的。
业界当先的很多计划并不是一群蠢才某个期间眉头一皱; 计上心来就做进去的。它们通过了数年的倒退才逐步完善和初具规模。如果没有足够的积攒和历练,靠拍脑袋或者头脑风暴是无奈和真正的实战相比的。

3. 冰山上面才是要害。
业界当先的计划大多是“逼”进去的。随着业务的倒退和质变导致量变,新的问题呈现了,已有的形式无奈应答这些问题,须要用新的计划来解决。通过一直的翻新和尝试,业界当先的计划才得以造成。

如果没有相似于腾讯那样海量用户积攒、大量的人力资源、优良的业务场景,想要建设一个“亿级用户平台”就注定会失败。真正优良的架构应该是在企业以后的人力、条件、业务等各种束缚下设计进去的,可能将资源正当地整合在一起并施展出最大的效用,并且可能疾速落地。许多 BAT 公司的架构师到了小公司或守业团队却无奈发明出成绩,因为他们不足大公司平台、资源和积攒的反对,他们只是生吞活剥大公司的做法,这样的做法胜利的概率非常低。

因而,真正优良的架构设计应该是在理论条件下的切实可行的设计,将资源正当利用,并可能疾速落地。胜利的架构设计须要充分考虑公司以后的资源、团队能力和业务需要,一直优化和改良,一直学习和尝试新的办法和技术,以适应日益变动的市场和业务环境。同时,咱们也须要意识到,技术的倒退须要工夫,须要一直的积攒和教训,只有在大量的实际中能力一直晋升本人的技术水平。

简略准则

简略准则宣言:“简略优于简单”。

的确,在软件畛域,适度谋求复杂性往往会导致设计进去的零碎难以保护、扩大和调试,进而影响整个团队的工作效率。因而,咱们须要转变思路,不要将复杂性作为评估架构的次要指标,而是要以零碎的理论需要和问题为出发点,遵循适合准则、简略准则、演变准则,以最小的复杂度满足零碎的需要。简略来说,就是要“谋求简略”。

谋求简略不等于简略粗犷,而是在不升高零碎性能、稳定性和可扩展性的前提下,以起码的设计和实现来满足需要。这须要架构师具备全面的技术能力和对系统的深刻理解,可能对复杂性进行无效的把控和合成,从而将零碎的设计和实现变得更加简单明了。

此外,简略也不意味着架构师能够疏忽细节和考虑不周。实际上,谋求简略须要更加重视细节和全局的把握,要通过精心的设计和实现来防止各种潜在的问题和危险。只有在这样的根底上,才可能设计出真正适合的、简略的、可演变的软件架构。

软件畛域的复杂性体现在两个方面:

1. 构造的复杂性

结构复杂的零碎简直毫无例外具备两个特点:

  • 组成简单零碎的组件数量更多;
  • 同时这些组件之间的关系也更加简单。

我以图形的形式来阐明复杂性:

2 个组件组成的零碎:

3 个组件组成的零碎:

4 个组件组成的零碎:

5 个组件组成的零碎:

为了防止这些问题,咱们须要尽量简化架构。简化架构并不意味着就义可用性或者性能,而是在保障性能和可用性的前提下,尽量减少组件的数量和组件之间的关系复杂度。

一个好的架构须要在多方面衡量,如零碎可用性、开发效率、保护老本、可扩展性等等。因而,在进行架构设计时,须要从多个角度登程,思考零碎的理论需要和限度条件,逐渐迭代优化设计方案,而不是一开始就试图设计出最完满的计划。

另外,一个好的架构设计须要在实践中一直迭代和优化。零碎运行过程中会呈现各种各样的问题,须要一直地对架构进行调整和优化。同时,随着业务的倒退和变动,架构设计也须要随之变动,及时做出调整和优化,才可能保证系统的稳定性和可继续倒退。

2. 逻辑的复杂性

意识到构造的复杂性后,咱们的第一反馈可能就是“升高组件数量”,毕竟组件数量越少,系统结构越简。最简略的构造当然就是整个零碎只有一个组件,即零碎自身,所有的性能和逻辑都在这一个组件中实现。

可怜的是,这样做是行不通的,起因在于除了构造的复杂性,还有逻辑的复杂性,即如果某个组件的逻辑太简单,一样会带来各种问题。

逻辑简单的组件,一个典型特色就是单个组件承当了太多的性能。以电商业务为例,常见的性能有:商品治理、商品搜寻、商品展现、订单治理、用户治理、领取、发货、客服……把这些性能全副在一个组件中实现,就是典型的逻辑复杂性。

逻辑简单简直会导致软件工程的每个环节都有问题,假如当初淘宝将这些性能全副在繁多的组件中实现,能够设想一下这个恐怖的场景:

  • 零碎会很宏大,可能是上百万、上千万的代码规模,“clone”一次代码要 30 分钟。
  • 几十、上百人保护这一套代码,某个“菜鸟”不小心改了一行代码,导致整站解体。
  • 需要像雪片般飞来,为了应答,开几十个代码分支,而后各种分支合并、各种分支笼罩。
  • 产品、研发、测试、项目管理不停地开会讨论版本打算,协调资源,解决抵触。
  • 版本太多,每天都要上线几十个版本,零碎每隔 1 个小时重启一次。
  • 线上运行呈现故障,几十个人扑上去定位和解决,一间小黑屋都装不下所有人,整个办公区闹翻天。
  • ……

不必多说,必定谁都无法忍受这样的场景。

然而,为什么简单的电路就象征更弱小的性能,而简单的架构却有很多问题呢?根本原因在于电路一旦设计好后进入生产,就不会再变,复杂性只是在设计时带来影响;而一个软件系统在投入使用后,后续还有源源不断的需要要实现,因而要一直地批改零碎,复杂性在整个零碎生命周期中都有很大影响。

性能简单的组件,另外一个典型特色就是采纳了简单的算法。简单算法导致的问题次要是难以了解,进而导致难以实现、难以批改,并且出了问题难以疾速解决。

以 ZooKeeper 为例,ZooKeeper 自身的性能次要就是选举,为了实现分布式下的选举,采纳了 ZAB 协定,所以 ZooKeeper 性能尽管绝对简略,但零碎实现却比较复杂。相比之下,etcd 就要简略一些,因为 etcd 采纳的是 Raft 算法,相比 ZAB 协定,Raft 算法更加容易了解,更加容易实现。

综合后面的剖析,咱们能够看到,无论是构造的复杂性,还是逻辑的复杂性,都会存在各种问题,所以架构设计时如果简略的计划和简单的计划都能够满足需要,最好抉择简略的计划。《UNIX 编程艺术》总结的 KISS(Keep It Simple, Stupid!)准则一样适应于架构设计。

在架构设计中,咱们应该遵循 KISS 准则,尽量让架构设计简略而易于了解和保护。另外,咱们还能够通过以下几种形式来防止复杂性问题:

将零碎合成为小的组件。每个组件都应该尽可能地简略,只实现一个性能。这样能够防止单个组件变得过于简单,同时也不便组件的保护和降级。

  • 采纳标准化的组件。通过采纳标准化的组件,能够防止从新创造轮子,也能够缩小对简单组件的依赖。
  • 防止适度设计。适度设计往往会导致代码过于简单,难以保护。在设计时,应该尽量避免不必要的设计,只实现必要的性能。
  • 尽量采纳简略的算法。尽量应用简略的算法,能够使零碎更加易于了解和保护。对于一些比较复杂的算法,能够思考采纳现成的库,防止反复造轮子。
  • 借鉴已有的成功经验。在架构设计时,能够参考已有的胜利案例,借鉴其中的教训和教训,防止犯相似的谬误。

总之,架构设计是一门艺术,须要一直地思考和实际,能力设计出简略、易于了解和保护的零碎。

演变准则

演变准则宣言:“演变优于一步到位”。

软件架构从字面意思了解和修建构造十分相似,事实上“架构”这个词就是修建畛域的专业名词,维基百科对“软件架构”的定义中有一段话形容了这种相似性:

从和目标、主题、资料和构造的分割上来说,软件架构能够和建筑物的架构相比较。

例如,软件架构形容的是一个软件系统的构造,包含各个模块,以及这些模块的关系;修建架构形容的是一幢修建的构造,包含各个部件,以及这些部件如何有机地组成成一幢完满的修建。

然而,字面意思上的相似性却覆盖了一个实质上的差别:修建一旦实现(甚至一旦开建)就不可再变,而软件却须要依据业务的倒退一直地变动!

  • 古埃及的吉萨大金字塔,4000 多年前实现的,到当初还是当初的架构。
  • 中国的明长城,600 多年前实现的,当初保留下来的长城还是当年的构造。
  • 美国白宫,1800 年建成,200 年来进行了几次扩大,但整体构造并无变动,只是在旁边的空地扩建或者革新外部的布局。

比照一下,咱们来看看软件架构。

Windows 零碎的倒退历史:

如果比照 Windows 8 的架构和 Windows 1.0 的架构,就会发现它们其实是两个不同的零碎了!

Android 的倒退历史:

(http://www.dappworld.com/wp-content/uploads/2015/09/Android-History-Dappworld.jpg)

同样,Android 6.0 和 Android 1.6 的差别也很大。

软件架构须要依据业务倒退一直变动,所以在做架构设计时应该采纳迭代的形式,而不是一步到位。这样做的益处是,能够让架构师随着业务的变动逐步深刻理解业务需要,同时也能够防止设计出适度简单、不切实际的计划。

另外,预测和剖析确实是不牢靠的,然而咱们能够采纳一些技术手段来帮忙咱们应答变动。比方,能够采纳微服务架构,将零碎划分成若干个小服务,每个服务都能够独立开发、测试、部署和扩大。这样一来,当业务需要发生变化时,只须要批改波及到的服务,而不必对整个零碎进行批改。此外,还能够采纳容器技术,通过容器化应用程序来实现疾速部署和扩大,以应答业务变动带来的挑战。

总之,软件架构须要依据业务倒退一直变动,咱们应该采纳迭代的形式来设计架构,并采纳一些技术手段来帮忙咱们应答变动。同时,咱们也须要明确,软件架构永远不可能一劳永逸,只有一直地学习和改良,能力放弃软件架构的衰弱和持续性。

思考到软件架构须要依据业务倒退一直变动这个实质特点, 软件架构设计其实更加相似于大自然“设计”一个生物,通过演变让生物适应环境,逐渐变得更加弱小:

  • 首先,生物要适应过后的环境。
  • 其次,生物须要一直地滋生,将无利的基因传递上来,将不利的基因剔除或者修复。
  • 第三,当环境变动时,生物要可能疾速扭转以适应环境变动;如果生物无奈调整就被天然淘汰;新的生物会保留一部分原来被淘汰生物的基因。

软件架构设计同样是相似的过程:

  • 首先,设计进去的架构要满足过后的业务须要。
  • 其次,架构要一直地在理论利用过程中迭代,保留优良的设计,修复有缺点的设计,改正错误的设计,去掉无用的设计,使得架构逐步欠缺。
  • 第三,当业务发生变化时,架构要扩大、重构,甚至重写;代码兴许会重写,但有价值的教训、教训、逻辑、设计等(相似生物体内的基因)却能够在新架构中连续。

因而,架构师在设计软件架构时,须要始终关注业务需要,一直调整和改良架构以适应业务的倒退。架构设计不应该是一次性的,而是一个继续演变的过程。同时,要重视简略性,防止适度简单的架构设计,这样能力更好地满足业务需要,进步零碎的可靠性和可维护性。

小结

以上的这三个准则都是为了应答“不确定性”而提出的,帮忙架构师在简单、疾速变动的业务环境下做出更好的决策。

须要留神的是,这些准则并不是刻板的规定,而是指导思想。在实践中,架构师须要依据具体情况进行衡量和调整。有时候,一个简单的计划可能的确更适宜以后的业务需要;有时候,一个一步到位的计划也可能更合乎业务倒退的须要。架构师须要依据本人的教训和专业知识,综合思考各种因素,做出最适宜以后业务的决策。

最初,须要强调的是,架构设计并不是一项孤立的技术流动,而是须要和业务、运维、开发等各个环节协同配合的。只有将架构设计和整个软件开发、运维流程无缝连接,能力真正实现架构的价值,为业务的胜利提供松软的反对。

本文由 mdnice 多平台公布

退出移动版