作为程序员,很多人都希望成为一名架构师,但并非简单地通过编程技能就能够达成这一目标。事实上,优秀的程序员和架构师之间存在一个明显的鸿沟——不确定性。
编程的本质是确定性的,也就是说,对于同一段代码,无论由谁编写,在何时执行,其结果应该是确定的(尽管有可能存在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多平台发布
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net