架构设计 1-基础架构
扫描左侧二维码 关注公众号 回复 “架构设计” 获取架构设计笔记完整思维导图
架构设计的历史背景
机器语言(1940 年之前)
机器语言的主要问题是三难:太难写、太难读、太难改!
汇编语言(20 世纪 40 年代)
本质上还是面向机器的,因为写汇编语言需要我们精确了解计算机底层的知识。
高级语言(20 世纪 50 年代)
第一次软件危机与结构化程序设计(20 世纪 60 年代~20 世纪 70 年代)
- 典型表现有软件质量低下、项目无法如期完成、项目严重超支等,因为软件而导致的重大事故时有发生。
- 软件工程同样无法根除软件危机,只能在一定程度上缓解软件危机。
- 结构化程序设计的主要特点是抛弃 goto 语句,采取“自顶向下、逐步细化、模块化”的指导思想。结构化程序设计本质上还是一种面向过程的设计思想,但通过“自顶向下、逐步细化、模块化”的方法,将软件的复杂度控制在一定范围内,从而从整体上降低了软件开发的复杂度。
第二次软件危机与面向对象(20 世纪 80 年代)
- 第二次软件危机的根本原因还是在于软件生产力远远跟不上硬件和业务的发展。第一次软件危机的根源在于软件的“逻辑”变得非常复杂,而第二次软件危机主要体现在软件的“扩展”变得非常复杂。
- 结构化程序设计虽然能够解决(也许用“缓解”更合适)软件逻辑的复杂性,但是对于业务变化带来的软件扩展却无能为力,软件领域迫切希望找到新的银弹来解决软件危机,在这种背景下,面向对象的思想开始流行起来。
软件架构的历史背景
随着软件系统规模的增加,计算相关的算法和数据结构不再构成主要的设计问题;当系统由许多部分组成时,整个系统的组织,也就是所说的“软件架构”,导致了一系列新的设计问题。
- 系统规模庞大,内部耦合严重,开发效率低;
- 系统耦合严重,牵一发动全身,后续修改和扩展困难;
- 系统逻辑复杂,容易出问题,出问题后很难排查和修复
三组容易混淆的概念
系统与子系统
系统
- 系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。它的意思是“总体”“整体”或“联盟”
- 关系:系统是由一群有关联的个体组成的,没有关联的个体堆在一起不能成为一个系统。
- 规则:系统内的个体需要按照指定的规则运作,而不是单个个体各自为政。规则规定了系统内个体分工和协作的方式。
- 能力:系统能力与个体能力有本质的差别,系统能力不是个体能力之和,而是产生了新的能力。
子系统
子系统也是由一群有关联的个体所组成的系统,多半会是更大系统中的一部分。
总结
一个系统的架构,只包括顶层这一个层级的架构,而不包括下属子系统层级的架构。所以微信架构,就是指微信系统这个层级的架构。当然,微信的子系统,比如支付系统,也有它自己的架构,同样只包括顶层。
模块与组件
模块
软件模块(Module)是一套一致而互相有紧密关联的软件组织。它分别包含了程序和数据结构两部分。现代软件开发往往利用模块作为合成的单位。模块的接口表达了由该模块提供的功能和调用它时所需的元素。模块是可能分开被编写的单位。这使它们可再用和允许人员同时协作、编写及研究不同的模块。
组件
软件组件定义为自包含的、可编程的、可重用的、与语言无关的软件单元,软件组件可以很容易被用于组装应用程序中。
总结
- 模块和组件都是系统的组成部分,只是从不同的角度拆分系统而已。
- 从业务逻辑的角度来拆分系统后,得到的单元就是“模块”;从物理部署的角度来拆分系统后,得到的单元就是“组件”。划分模块的主要目的是职责分离;划分组件的主要目的是单元复用。
- 假设我们要做一个学生信息管理系统,这个系统从逻辑的角度来拆分,可以分为“登录注册模块”“个人信息模块”和“个人成绩模块”;从物理的角度来拆分,可以拆分为 Nginx、Web 服务器和 MySQL。
- 业务系统的架构师,首先需要思考怎么从业务逻辑的角度把系统拆分成一个个模块角色,其次需要思考怎么从物理部署的角度把系统拆分成组件角色,例如选择 MySQL 作为存储系统。但是对于 MySQL 内部的体系架构(是可以不用关注的,也不需要在你的业务系统架构中展现这些内容。
框架与架构
框架
软件框架(Software framework)通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
架构
软件架构指软件系统的“基础结构”,创造这些基础结构的准则,以及对这些结构的描述。
总结
- 框架关注的是“规范”,架构关注的是“结构”。
- 框架是一整套开发规范,架构是某一套开发规范下的具体落地方案,包括各个模块之间的组合关系以及它们协同起来完成功能的运作规则。
重新定义架构:4R 架构
- 软件架构指软件系统的顶层(Rank)结构,它定义了系统由哪些角色(Role)组成,角色之间的关系(Relation)和运作规则(Rule)
- Rank:它是指软件架构是分层的,对应“系统”和“子系统”的分层关系。通常情况下,我们只需要关注某一层的架构,最多展示相邻两层的架构,而不需要把每一层的架构全部糅杂在一起。无论是架构设计还是画架构图,都应该采取“自顶向下,逐步细化”的方式。
- Role:它是指软件系统包含哪些角色,每个角色都会负责系统的一部分功能。架构设计最重要的工作之一就是将系统拆分为多个角色。最常见的微服务拆分其实就是将整体复杂的业务系统按照业务领域的方式,拆分为多个微服务,每个微服务就是系统的一个角色。
- Relation:它是指软件系统的角色之间的关系,对应到架构图中其实就是连接线,角色之间的关系不能乱连,任何关系最后都需要代码来实现,包括连接方式(HTTP、TCP、UDP 和串口等)、数据协议(JSON、XML 和二进制等)以及具体的接口等。
- Rule:它是指软件系统角色之间如何协作来完成系统功能。
架构设计的目的
架构也是为了应对软件系统复杂度而提出的一个解决方案,即架构设计的主要目的是为了解决软件系统复杂度带来的问题。
- 遵循这条准则能够让“新手”架构师心中有数,而不是一头雾水。
- 遵循这条准则能够让“老鸟”架构师有的放矢,而不是贪大求全。
复杂性来源
高性能
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
- 操作系统和性能最相关的就是进程和线程
- 如果我们要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点,而且这些技术并不是最新的就是最好的,也不是非此即彼的选择。
集群复杂度
- 任务分配,负载均衡
- 任务分解,微服务
- 简单的系统更加容易做到高性能
- 可以针对单个任务进行扩展
指标
衡量软件性能包括了响应时间、TPS、服务器资源利用率等客观指标
高可用
系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
系统的高可用方案五花八门,但万变不离其宗,本质上都是通过“冗余”来实现高可用。
- 高性能增加机器目的在于“扩展”处理性能;
- 高可用增加机器目的在于“冗余”处理单元。
计算高可用
“计算”指的是业务的逻辑处理。计算有一个特点就是无论在哪台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的
存储高可用
- 整个系统的高可用设计关键点和难点就在于“存储高可用”。存储与计算相比,有一个本质上的区别:将数据从一台机器搬到到另一台机器,需要经过线路进行传输。
- 存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
高可用状态决策
无论是计算高可用还是存储高可用,其基础都是“状态决策”,即系统需要能够判断当前的状态是正常还是异常,如果出现了异常就要采取行动来保证高可用
- 独裁式
- 协商式
- 民主式
可扩展性
可扩展性是指,系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无需整个系统重构或者重建。
设计具备良好可扩展性的系统,有两个基本条件:
- 正确预测变化
- 原则:只预测 2 年内的可能变化,不要试图预测 5 年甚至 10 年后的变化。
- 完美应对变化
- 方案一:提炼出“变化层”和“稳定层”,核心思想是通过变化层来隔离变化
- 方案二:提炼出“抽象层”和“实现层”,核心思想就是通过实现层来封装变化。
- 原则:1 写 2 抄 3 重构原则
其他来源
成本
往往只有“创新”才能达到低成本目标
- 引进新技术
- 自研新技术
低成本本质上是与高性能和高可用冲突的,所以低成本很多时候不会是架构设计的首要目标,而是架构设计的附加约束。
安全
- 功能安全:其实就是“防小偷”
- 架构安全:就是“防强盗”
规模
- 规模带来复杂度的主要原因就是“量变引起质变”
- 数据越来越多,系统复杂度发生质变
架构设计原则
合适原则,“合适优于业界领先”
不适合的情况
- 没那么多人,却想干那么多活
- 没有那么多积累,却想一步登天
- 没有那么卓越的业务场景,却幻想灵光一闪成为天才
简洁原则,“简洁优于复杂”
复杂的表现
- 结构的复杂性
- 现象
- 组成复杂系统的组件数量更多
- 同时这些组件之间的关系也更加复杂
- 问题
- 组件越多,就越有可能其中某个组件出现故障
- 某个组件改动,会影响关联的所有组件
- 定位一个复杂系统中的问题总是比简单系统更加困难
- 现象
- 逻辑的复杂性
- 单个组件承担了太多的功能
- 采用了复杂的算法
演进原则,“演化优于一步到位”
对于软件来说,变化才是主题
软件架构设计其实更加类似于大自然“设计”一个生物,通过演化让生物适应环境,逐步变得更加强大
避坑
- 时刻提醒自己不要贪大求全
- 避免盲目照搬大公司的做法
最佳实践
- 应该认真分析当前业务的特点,明确业务面临的主要问题,设计合理的架构,快速落地以满足业务需要
- 在运行过程中不断完善架构,不断随着业务演化架构
架构设计流程
识别复杂度
- 将主要的复杂度问题列出来,然后根据业务、技术、团队等综合情况进行排序,优先解决当前面临的最主要的复杂度问题。
- 设计的目标应该以峰值来计算。峰值一般取平均值的 3 倍,
- 设计目标设定为峰值的 4 倍是根据业务发展速度来预估的,不是固定为 4 倍,不同的业务可以是 2 倍,也可以是 8 倍,但一般不要设定在 10 倍以上,更不要一上来就按照 100 倍预估。
设计备选方案
新技术都是在现有技术的基础上发展起来的。``````
常见错误
- 设计最优秀的方案
- 只做一个方案
- 弊端
- 心里评估过于简单,可能没有想得全面
- 单一方案设计会出现过度辩护的情况,
- 架构师再怎么牛,经验知识和技能也有局限
- 实践
- 备选方案的数量以 3 ~ 5 个为最佳
- 备选方案的差异要比较明显
- 备选方案的技术不要只局限于已经熟悉的技术
- 弊端
- 备选方案过于详细
- 耗费了大量的时间和精力
- 将注意力集中到细节中,忽略了整体的技术设计,导致备选方案数量不够或者差异不大。
- 评审的时候其他人会被很多细节给绕进去,评审效果很差。
- 正确的做法是备选阶段关注的是技术选型,而不是技术细节,技术选型的差异要比较明显
评估和选择备选方案
常见指导思想
- 最简派
- 最牛派
- 最熟派
- 领导派
评估策略
- 列出我们需要关注的质量属性点,然后分别从这些质量属性的维度去评估每个方案,再综合挑选适合当时情况的最优方案
- 常见的方案质量属性点有:性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩展性等
- 通常情况下,如果某个质量属性评估和业务发展有关系(例如,性能、硬件成本等),需要评估未来业务发展的规模时,一种简单的方式是将当前的业务规模乘以 2 ~4 即可,如果现在的基数较低,可以乘以 4;如果现在基数较高,可以乘以 2。
- 即架构师综合当前的业务发展情况、团队人员规模和技能、业务发展预测等因素,将质量属性按照优先级排序,首先挑选满足第一优先级的,如果方案都满足,那就再看第二优先级……以此类推。
详细设计方案
详细设计方案阶段可能遇到的一种极端情况就是在详细设计阶段发现备选方案不可行。
- 架构师不但要进行备选方案设计和选型,还需要对备选方案的关键细节有较深入的理解
- 通过分步骤、分阶段、分系统等方式,尽量降低方案复杂度,方案本身的复杂度越高,某个细节推翻整个方案的可能性就越高,适当降低复杂性,可以减少这种风险。