从0开始学架构
架构基础
架构设计的关键思想是判断和取舍,程序设计的关键思维是逻辑和实现。
架构到底是什么?
系统与子系统
- 系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。它的意思是“总体”“整体”或“联盟”。 —— 维基百科
- 子系统也是有一群由关联的个体所组成的系统,多半会是更大系统中的一部分。
其实子系统的定义和系统定义是一样的,只是观察的角度有差异,一个系统可能是另外一个更大系统的子系统。
模块与组件
- 软件模块(Module)是一套一致而互相有紧密关联的软件组织。它分别包含了程序和数据结构两部分。现代软件开发往往利用模块作为合成的单位。模块的接口表达了由该模块提供的功能和调用它时所需的元素。模块是可能分开被编写的单位。这使它们可在用和允许人员同时协作、编写及研究不同的模块。
- 软件的组件(Component)定义为自包含的、可编程的、可重用的、与语言无关的软件单元,软件组件可以很容易被用于组装应用程序中。
模块和组件都是系统的组成部分,只是从不同的角度拆分系统而已。
从逻辑角度来拆分系统,得到的单元是“模块”;从物理的角度来拆分系统,得到的单元就是“组件”。
划分模块的主要目的是职责分离;划分组件的主要目的是单元复用。
框架与架构
- 软件框架(Software framework)通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
- 软件架构(Software architecture)是软件系统的“基础结构”,创造这些结构的准则,以及对这些结构的描述。
框架关注的是“规范”,架构关注的是“结构”。
架构设计的历史背景
- 1940年之前 机器语言,直接使用二进制0和1来表示机器可以识别的指令和数据
- 20世纪40年代 汇编语言,面向机器的符号语言
- 20世纪50年代 高级语言
- 20世纪60年代~20世纪70年代 第一次软件危机与结构化设计,第一次软件危机的根源在于软件的“逻辑”变得非常复杂
- 20世纪80年代 第二次软件危机与面向对象,第二次软件危机主要体现在软件的“扩展”变得非常复杂
第一次软件危机引出了“结构化编程”,创造了“模块”的概念;第二次软件危机引出了“面向对象编程”,创造了“对象”的概念;到了20世纪90年代“软件架构”开始流程,创造了“组件”概念。可以看到,”模块”“对象”“组件”本质上都是对达到一定规模的软件进行拆分,差别是在在于随着软件的复杂度不断增高,拆分的粒度越来越粗,拆分的层次越来愈高。
架构设计的目的
整个软件技术发展的历史,其实就是一部与“复杂度”斗争的历史,架构的出现也不例外。通过回顾架构产生的历史背景和原因,架构设计的目的是为了解决软件系统的复杂度带来的问题。
心中有数,而不是一头雾水
- 通过熟悉和理解需求,识别系统复杂性所在的地方,然后针对这些复杂点进行架构设计。
- 架构设计并不是面面俱到,不需要每个架构都具备高性能、高可用、高扩展等特点,而是识别出复杂点然后有针对性地解决问题。
- 理解每个架构方案背后所需要解决的复杂点,然后才能对比自己的业务复杂点,参考复杂点相似的方案。
有的放矢,而不是贪大求全
- 如果系统的复杂度不是在性能这部分,TPS做到10万并没有什么用。
- 淘宝的架构是为了解决淘宝业务的复杂度而设计的,淘宝的业务复杂度并不就是我们的业务复杂的,绝大多数业务的用户都不可能有淘宝那么大。
- Docker不是万能的,只是为了解决资源重用和动态分布而设计的,如果我们的系统复杂度根本不是这方面,引入Docker没有什么意思。
复杂度来源:高性能
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
计算机内部复杂度最关键的地方就是操作系统。操作系统和性能最相关的就是进程和线程。
集群复杂度
进入互联网时代后,业务的发展速度远远超过了硬件的发展速度,必须采用机器集群的方式来达到高性能。
复杂度来源:高可用
系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
本质上都是通过“冗余”来实现高可用。
高可用的“冗余”解决方案,单纯从形式上来看,和之前讲的高性能是一样的,都是通过增加更多机器来达到目的,但其实本质上是有根本区别的:高性能增加机器目的在于“扩展”处理性能;高可用增加机器目的在于“冗余”处理单元。
- 计算高可用
- 存储高可用
存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
复杂度来源:可扩展性
可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。
设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化
预测变化的复杂性在于:
- 不能每个设计点都考虑可扩展性。
- 不能完全不考虑可扩展性。
- 所有的预测都存在出错的可能性。
第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。
复杂度来源:低成本、安全、规模
规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度就会发生质的变化。
系统复杂度 = 功能数量 + 功能之间的链接数量,通过计算可以看出:
This chapter requires login to view full content. You are viewing a preview.
Login to View Full Content