2.架构复杂度来源
1.复杂度来源:高性能
软件系统中高性能带来的复杂度主要体现在两方面,
一方面是单台计算机内部为了高性能带来的复杂度;
另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度与集群的复杂度:
单机复杂度:计算机内部复杂度最关键的地方就是操作系统。
集群的复杂度:虽然计算机硬件的性能快速发展,
但和业务的发展速度相比,还是小巫见大巫了,
尤其是进入互联网时代后,业务的发展速度远远超过了硬件的发展速度。
集群部署涉及到以下几个方面:
1.任务分配
2.任务分解
通过这种任务分解的方式,能够把原来大一统但复杂的业务系统,
拆分成小而简单但需要多个系统配合的业务系统。
从业务的角度来看,任务分解既不会减少功能,
也不会减少代码量(事实上代码量可能还会增加,
因为从代码内部调用改为通过服务器之间的接口调用),
那为何通过任务分解就能够提升性能呢?
主要有以下几方面的因素:
简单的系统更容易做到高性能:
系统的功能越简单,影响性能的点就越少,就更加容易进行有针对性的优化。
可以针对单个任务进行拓展:
当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现,
而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,
不需要改动整个系统,风险会小很多。
2.复杂度来源:高可用
高可用的定义:系统无中断地执行其功能的能力,代表系统的可用性程度,
是进行系统设计时的准则之一。
高性能增加机器目的在于“扩展”处理性能;高可用增加机器目的在于“冗余”处理单元。
通过冗余增强了可用性,但同时也带来了复杂性,
高可用的不同应用场景:
计算高可用:这里的“计算”指的是业务的逻辑处理。
计算有一个特点就是无论在哪台机器上进行计算,同样的算法和输入数据,
产出的结果都是一样的,所以将计算从一台机器迁移到另外一台机器,
对业务并没有什么影响。
存储高可用:对于需要存储数据的系统来说,
整个系统的高可用设计关键点和难点就在于“存储高可用”。
分布式领域里面有一个著名的 CAP 定理,从理论上论证了存储高可用的复杂度。
也就是说,存储高可用不可能同时满足“一致性、可用性、分区容错性”,
最多满足其中两个,这就要求我们在做架构设计时结合业务进行取舍。
高可用状态决策:
无论是计算高可用还是存储高可用,其基础都是“状态决策”,
即系统需要能够判断当前的状态是正常还是异常,
如果出现了异常就要采取行动来保证高可用。
如果状态决策本身都是有错误或者有偏差的,
那么后续的任何行动和处理无论多么完美也都没有意义和价值。
但在具体实践的过程中,恰好存在一个本质的矛盾:
通过冗余来实现的高可用系统,状态决策本质上就不可能做到完全正确。
下面我基于几种常见的决策方式进行详细分析。
1. 独裁式:独裁式决策指的是存在一个独立的决策主体,
我们姑且称它为“决策者”,负责收集信息然后进行决策;
所有冗余的个体,我们姑且称它为“上报者”,都将状态信息发送给决策者。
独裁式的决策方式不会出现决策混乱的问题,因为只有一个决策者,
但问题也正是在于只有一个决策者。当决策者本身故障时,
整个系统就无法实现准确的状态决策。
如果决策者本身又做一套状态决策,那就陷入一个递归的死循环了。
2. 协商式:协商式决策指的是两个独立的个体通过交流信息,
然后根据规则进行决策,最常用的协商式决策就是主备决策。
综合分析,协商式状态决策在某些场景总是存在一些问题的。
3. 民主式:民主式决策指的是多个独立的个体通过投票的方式来进行状态决策。
例如,ZooKeeper 集群在选举 leader 时就是采用这种方式。
3.复杂度来源:可拓展性
可拓展性的定义:可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,
当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,
无须整个系统重构或者重建。
设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化。
预测变化:软件系统与硬件或者建筑相比,有一个很大的差异:
软件系统在发布后还可以不断地修改和演进,这就意味着不断有新的需求需要实现。
综合分析,预测变化的复杂性在于:
不能每个设计点都考虑可扩展性。
不能完全不考虑可扩展性。
所有的预测都存在出错的可能性。
应对变化:
第一种应对变化的常见方案是将“变化”封装在一个“变化层”,
将不变的部分封装在一个独立的“稳定层”。
1. 系统需要拆分出变化层和稳定层
2. 需要设计变化层和稳定层之间的接口
第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。
抽象层是稳定的,实现层可以根据具体业务需要定制开发,
当加入新的功能时,只需要增加新的实现,无须修改抽象层。
这种方案典型的实践就是设计模式和规则引擎。
4.复杂度来源:低成本 安全 规模
低成本:低成本给架构设计带来的主要复杂度体现在,
往往只有“创新”才能达到低成本目标。
这里的“创新”既包括开创一个全新的技术领域(这个要求对绝大部分公司太高),
也包括引入新技术,如果没有找到能够解决自己问题的新技术,
那么就真的需要自己创造新技术了。
相比来说,创造新技术复杂度更高,
因此一般中小公司基本都是靠引入新技术来达到低成本的目标;
而大公司更有可能自己去创造新的技术来达到低成本的目标,
因为大公司才有足够的资源、技术和时间去创造新技术。
安全:安全本身是一个庞大而又复杂的技术领域,
并且一旦出问题,对业务和企业形象影响非常大。
从技术的角度来讲,安全可以分为两类:一类是功能上的安全,一类是架构上的安全。
功能安全:--"防小偷" 减少系统潜在的缺陷,阻止黑客破坏行为
从实现的角度来看,功能安全更多地是和具体的编码相关,与架构关系不大。
现在很多开发框架都内嵌了常见的安全功能,
能够大大减少安全相关功能的重复开发。
所以功能安全是一个逐步完善的过程,
而且往往都是在问题出现后才能有针对性的提出解决方案,
我们永远无法预测系统下一个漏洞在哪里,也不敢说自己的系统肯定没有任何问题。
我们需要逐步完善,不可能在系统架构设计的时候一劳永逸地解决。
架构安全:--"防强盗" 保护系统不受恶意访问和攻击,保护系统的重要数据不被窃取。
由于是蓄意破坏系统,因此对影响也大得多。架构设计时需要特别关注架构安全。
传统的架构安全主要依靠防火墙,防火墙最基本的功能就是隔离网络,
通过将网络划分成不同的区域,
制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。
互联网系统的架构安全目前并没有太好的设计手段来实现,
更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,
较少自己来设计和实现。
规模:规模带来复杂度的主要原因就是“量变引起质变”,
当数量超过一定的阈值后,复杂度会发生质的变化。
常见的规模带来的复杂度有:
1.功能越来越多,导致系统复杂度指数级上升
(业务功能越来越多,调用逻辑越来越复杂)
2.数据越来越多,系统复杂度发生质变
(数据容量、类型、关联关系越来越多)
规模问题需要与高性能、高可用、高扩展、高伸缩性统一考虑。
常采用“分而治之,各个击破”的方法策略。
伸缩性度量指标包括:(1)处理更高并发;(2)处理更多数据;(3)处理更高频次的用户交互。
其复杂度体现在:(1)伸——增强系统在上述三个方面的处理能力;(2)缩——缩减系统处理能力;
(3)上述伸缩过程还必须相对低成本和快速
。