分享

《从零开始学架构》一:基础架构

 书剑秋 2022-03-16

keeplived和zookeeper的对比:https://blog.csdn.net/vtopqx/article/details/79066703

架构设计的关键思维是判断和取舍,程序设计的关键思维是逻辑和实现。

1 架构是什么

  • 系统

系统泛指由一群有关联的个体组成,根据某种规则运作,完成个别元件不能单独完成的工作的群体

  1. 关联:系统是由一群有关联的个体组成的,没有关联的个体堆在一起不能成为一个系统。
  2. 规则:系统内的个体需要按照指定的规则运作,而不是单个个体各自为政,规则规定了系统内个体分工和协作的方式。
  3. 能力:系统能力与个体能力有本质的差别,系统能力不是个体能力之和,而是产生了新的能力。
  • 子系统

子系统也是由一群有关联的个体所组成的系统,一般是更大系统中的一部分。

  • 模块与组件

软件模块(Module)是一套一致而互相有紧密关连的软件组织。它分别包含了程序和数据结构两部分;
现代软件开发往往利用模块作为合成的单位;
模块的接口表达了由该模块提供的功能和调用它时所需的元素;
模块是可能分开被编写的单位,这使得它们可再用和允许人员同时协作、编写及研究不同的模块。

软件组件定义为自包含的、可编程的、可重用的、与语言无关的软件单元,软件组件可以很容易被用于组装应用程序中。

模块和组件都是系统的组成部分,只是从不同的角度拆分系统而已:
逻辑的角度拆分了系统后,得到的单元就是“模块”;
物理的角度来拆分系统后,得到的单元就是“组件”;
划分模块的主要目的是职责分离;
划分组件的主要目的是单元复用;
“组件”component也可翻译成“零件”,更容易理解一些:“零件”是一个物理的概念,并且具备独立且可替换的特点。

以学生信息管理为例:
从逻辑的角度来拆分,可以分为“登录注册模块”“个人信息模块”“个人成绩模块”;
从物理的角度来拆分,可以拆分为 Nginx、Web 服务器、MySQL。

  • 框架与架构

软件框架(Software framework)通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品

  1. 框架是组件规范:例如MVC、MVP、MVVM、J2EE等框架
  2. 框架提供基础功能的产品:例如,Spring MVC 是 MVC 的开发框架,除了满足 MVC 的规范,Spring 提供了很多基础功能来实现功能,如注解(@Controller 等)、Spring Security、Spring JPA 等很多基础功能。

软件架构指软件系统的基础结构,创造这些基础结构的准则,以及对这些结构的描述。

从定义的角度看:框架(Framework)关注的是“规范”,架构(Architecture)关注的是“结构”

架构定义中的”基础结构“这个概念通过采用不同的角度或者维度,可以将系统划分为不同的结构

以学生信息管理为例:
业务逻辑的角度分解,“学生管理系统”的架构是
在这里插入图片描述
物理部署的角度分解,学生管理系统”的架构是:
在这里插入图片描述
开发规范的角度分解,“学生管理系统”可以采用标准的 MVC 框架来开发,因此架构又变成了 MVC 架构
在这里插入图片描述

可以重新定义架构:软件架构指软件系统的顶层结构,基本上把系统、子系统、模块、组件、架构等概念都串起来了

首先,“系统是一群关联个体组成”,这些“个体”可以是“子系统”“模块”“组件”等;
架构需要明确系统包含哪些“个体”。

其次,系统中的个体需要“根据某种规则”运作;
架构需要明确个体运作和协作的规则。

第三,架构原定义用到了“基础结构”,改为“顶层结构”,可以更好地区分系统和子系统,避免将系统架构和子系统架构混淆在一起导致架构层次混乱。

  • 总结

架构是顶层设计

框架是面向编程或配置的半成品

组件是从技术维度上的复用

模块是从业务维度上职责的划分

系统是相互协同可运行的实体

模块、对象、组件是对软件在不同粒度和层次上的“拆分”;
软件架构是一种对软件的“组织”。

2 架构的目的

整个软件技术发展的历史,其实就是一部与“复杂度”斗争的历史,架构的出现也不例外,架构设计的主要目的是为了解决软件系统复杂度带来的问题,预判可能遇到的困难,用最小的代价解决它,由需求去驱动架构

以学生管理系统为例:基本功能包括登录、注册、成绩管理、课程管理等,接下来找出复杂度体现在哪里:

性能:一个学校的学生大约 1 ~ 2 万人,学生管理系统的访问频率并不高,平均每天单个学生的访问次数平均不到 1 次,因此性能这部分并不复杂,存储用 MySQL可以解决,缓存、队列等可以不用,Web 服务器用 Nginx 可以解决

可扩展性:学生管理系统的功能比较稳定,可扩展的空间并不大,因此可扩展性也不复杂。

高可用:学生管理系统即使宕机 2 小时,对学生管理工作影响并不大,因此可以不做负载均衡,更不用考虑异地多活这类复杂的方案;

高可靠:如果学生的数据全部丢失,修复是非常麻烦的,只能靠人工逐条修复,这个很难接受,因此存储高可靠是此系统的复杂度之一;
需要考虑多种异常情况:机器故障机房故障
针对机器故障,需要设计 MySQL 同机房主备方案;
针对机房故障,我们需要设计 MySQL 跨机房同步方案。

安全性:学生管理系统存储的信息有一定的隐私性,例如学生的家庭情况,但并不是和金融相关的,也不包含强隐私的信息,因此安全性方面只要做 3 个事情就基本满足要求了:Nginx 提供 ACL 控制、用户账号密码管理、数据库访问权限控制。

成本:由于系统很简单,基本上几台服务器就能够搞定,对于一所大学来说完全不是问题,可以无需太多关注。

3 复杂度来源

3.1 性能

操作系统的复杂度直接决定了软件系统的复杂度。

操作系统和性能最相关的就是进程线程

  • 单机系统

单处理-》批处理-》进程-》线程

  • 多机系统

首先需要任务分配:使用任务分配器将任务分给多台机器;随着业务的复杂提高,任务分配器也需要越来越多

要进一步提高性能,则有任务分解:把系统分为不同的业务模块,因为:
简单的系统更加容易做到高性能
可以针对单个任务进行扩展
当然也不是越细越好,如果系统拆分得太细,为了完成某个业务,系统间的调用次数会呈指数级别上升,而系统间的调用通道目前都是通过网络传输的方式,性能远比系统内的函数调用要低得多。

3.2 高可用

定义:

系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。

难点在“无中断”上面:
无论是单个硬件还是单个软件,都不可能做到无中断;
硬件会出故障,软件会有 bug;
硬件会逐渐老化,软件会越来越复杂和庞大等等

高可用方案本质上都是通过“冗余”来实现高可用

通过冗余增强了可用性,但同时也带来了复杂性

计算高可用的复杂度:增设机器形成一主n(n看需求)备,此时:
需要增加任务分配器;
任务分配器和真正的业务服务器之间有连接和交互,需要选择合适的连接方式,并且对连接进行管理;
任务分配器需要增加分配算法

存储高可用的复杂度:难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响,这时候需要使用CAP理论进行取舍

计算和存储两种高可用的基础都是“状态决策”:
系统需要能够判断当前的状态是正常还是异常,如果出现了异常就要采取行动来保证高可用;
如果状态决策本身是有错误或者有偏差的,那么后续的任何行动和处理都没有意义和价值;
但是在具体实践的过程中,存在一个本质的矛盾:通过冗余来实现的高可用系统,状态决策本质上就不可能做到完全正确

高可用常见的决策方式:独裁式、协商式、民主式

1 独裁式

存在一个独立的决策主体,负责收集信息然后进行决策,所有冗余的个体都将状态信息发送给决策者,不负责决策。

此方式不会出现决策混乱的问题,因为只有一个决策者;
问题也在于只有一个决策者,当决策者本身故障时,整个系统就无法实现准确的状态决策;
如果决策者本身又做一套状态决策,那就陷入一个递归的死循环了。

2 协商式

两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策

难点在于,如果两者的信息交换出现问题(比如主备连接中断),此时状态决策应该怎么做,因为当前主机不知道其他主机的状态,因此会不知所措

3 民主式

多个独立的个体通过投票的方式来进行状态决策,如Zookeeper的Leader选举算法

难点在于算法复杂和脑裂问题

脑裂的根本原因:原来统一的集群因为连接中断,造成了两个独立分隔的子集群,每个子集群单独进行选举,于是选出了 2 个主机,此时整个系统就出现了两个主节点。这个状态违背了系统设计的初衷,两个主节点会各自做出自己的决策,整个系统的状态就混乱了。

解决:采用“投票节点数必须超过系统总节点数一半”规则来处理,分隔的相对较小的子集群因为无法达到原集群数超过一半的投票,因此不会进行选举;
这种方式虽然解决了脑裂问题,但同时降低了系统整体的可用性,即如果系统不是因为脑裂问题导致投票节点数过少,而真的是因为节点故障,较大的子集群机器全出故障了,此时系统也不会选出主节点,整个系统就相当于宕机了,尽管此时还有较小集群的机器是正常的

可以看到,没有完美的决策方式,需要进行取舍

3.3 可扩展性

可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。

设计具备良好可扩展性的系统,有两个基本条件:正确预测变化完美封装变化

第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”

复杂度:
系统需要拆分出变化层和稳定层,有时候并不明确哪些属于变化层,哪些属于稳定层;
需要设计变化层和稳定层之间的接口,在有差异的多个实现方式中找出共同点,并且还要保证当加入新的功能时原有的接口设计不需要太大修改,是一件很复杂的事情

第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”
抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层;
这种方案典型的实践就是设计模式和规则引擎。

复杂度:规则引擎和设计模式都是通过灵活的设计来达到可扩展的目的,但“灵活的设计”本身就是一件复杂的事情。

3.4 成本、安全、规模

  • 成本

低成本在服务器数量上看是与高性能和高可用冲突的,因此如果要在保证高性能和高可用的前提下尽可能地低成本,那么就要“创新”吗,这也是成本的复杂度来源,“创新”既包括开创一个全新的技术领域,也包括引入新技术。

  • 安全

安全可以分为两类:一类是功能上的安全,一类是架构上的安全。

功能安全:
如XSS 攻击、CSRF 攻击、SQL 注入、Windows 漏洞、密码破解等;
本质上是因为系统实现有漏洞,黑客会利用各种漏洞潜入系统,就像“小偷”一样;

架构安全:
跟“强盗一样”,直接强力攻击;
传统的架构安全主要依靠防火墙,功能虽然强大,但性能一般,银行和传统企业会使用;
互联网领域因为性能问题和攻击类型的问题(防火墙对DDos没有作用,而DDos是互联网最常见的攻击)并没有采用防火墙,目前并没有太好的设计手段来实现,更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,较少自己来设计和实现。

  • 规模

常见于企业级系统,功能、数据非常多,量变引起质变

数据越来越多,这也就是大数据、数据库的分库分表产生的原因,也是复杂度所在(大数据复杂,分库分表也复杂)

4 架构设计的三个原则

分别是合适原则、简单原则、演化原则

4.1 合适原则

“合适优于业界领先”

根据团队人数、团队人员的技术水平选用难度合适的技术

根据业务场景选择合适的技术

4.2 简单原则

“简单优于复杂”

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

  1. 结构的复杂性

结构复杂的系统的特点:组成复杂系统的组件数量更多,同时这些组件之间的关系也更加复杂。

​ 2.逻辑的复杂性

如果想降低结构复杂性,可以降低组件数量,将功能和逻辑聚在一个组件上,但是这带来了新问题:某个组件的逻辑太复杂,一样会带来各种问题。

复杂的电路意味更强大的功能,而复杂的架构却有很多问题的根本原因在于电路一旦设计好后进入生产,就不会再变,复杂性只是在设计时带来影响;
而一个软件系统在投入使用后,后续还有源源不断的需求要实现,因此要不断地修改系统,复杂性在整个系统生命周期中都有很大影响。

4.3 演化原则

“演化优于一步到位”

建筑一旦完成(甚至一旦开建)就不可再变,而软件却需要根据业务的发展不断地变化,因此需要进行演化,一开始业务没那么复杂,性能要求没那么高,就没必要做得太复杂,建议如下:

  • 首先,设计出来的架构要满足当时的业务需要。
  • 其次,架构要不断地在实际应用过程中迭代,保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计,使得架构逐渐完善。
  • 第三,当业务发生变化时,架构要扩展、重构,甚至重写;代码也许会重写,但有价值的经验、教训、逻辑、设计等(类似生物体内的基因)却可以在新架构中延续。

4.4 总结

三个原则其实是一体的,核心就是因为软件系统的复杂就意味着问题,变化才是主题,因此”适当“才是最好地应对变化的方式,而不是“过度”

合适原则第一考虑,优先满足业务需求;
简单原则第二考虑,挑选简单方案快速落地验证;
演进原则第三考虑,适当预测业务发展,感觉预测不准就不预测,等真的出现问题的时候演进即可

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多