软件架构
什么是架构?
我们常听到的 Linux架构、MySQL架构、微信架构、微信登陆系统架构......
架构到底指什么?先了解下架构相关的概念:系统、模块、组件、框架、架构
1. 系统:系统由多个子系统组成,最小子系统包括模块和组件,系统能力不是子系统能力之和,而是产生新的能力
2. 模块&&组件:简单理解: 从逻辑角度区分就是“模块”,从物理角度区分就是组件,划分模块目的是职责分离,划分“组件”的目的是单元复用
3. 框架Framework&&架构Architecture(结构)
框架 是一种组件规范,例如MVC、MVP、MVVM, 提供基础功能的产品SpringMVC是MVC的开发框架
架构 关注的是结构,“模块”、“对象”、“组件”本质上是对达到一定规模的软件进行拆分,差别只在于随着软件的复杂度不断增加,拆分力度越来越粗,拆分角度越来越高,形成了架构
4. 重新定义架构:指软件系统的顶层结构,系统是由关联的个体组成,这些个体可以时“子系统”、“模块”、“组件”等,其次个体需要“根据某种规则”运作。
软件系统
计算机发展本质由硬件发展驱动的,尤其是CPU性能发展。而将硬件性能充分发挥出来的是操作系统;操作系统是软件系统的运行环境;
冯诺依曼结构:计算机的五大基本构件:运算器、存储器(内存)、输入设备(键盘,鼠标)、输出设备(打印机、显示器)和控制器。USB属于输入/输出设备。计算的硬件均由冯诺依曼结构构成。
架构设计的目的
架构主要目的是解决复杂度带来的问题,架构是一种方案之一
软件技术发展史
整个过程分为面向机器、面向过程、面向对象
如何理解面向对象?
举个例子:如何把大象装进冰箱? 冰箱打开open,关闭:close, 装进去fix。以冰箱定义为class F(), F定义三个方法open,close,fix。以面向过程的思考方式抽象成面向对象的思考方式。
复杂度来源
高性能的追求
技术发展带来了性能的提升,不一定带来复杂度提升。新技术淘汰旧技术,这种情况直接用新技术即可,但若是并不是取代而是开辟全新领域的技术,就会给软件带来复杂度。
软件系统的高性能带来的复杂度主要体现在两方面
- 单台计算机内部为了高性能带来的复杂度(单机复杂度)
- 集群复杂度(通过大量机器提升性能)
1. 单机复杂度
将硬件性能充分发挥出来的是操作系统;操作系统是软件系统的运行环境;操作系统的复杂性决定了软件系统的复杂性;操作系统和性能最相关的是进程和线程
如果要完成一个高性能的软件系统需要考虑如下技术点:多进程、多线程、进程间通信、多线程并发等
2. 集群复杂度
类似于红包 & 支付业务复杂度是单机的性能无法支撑的,必须采用集群。多台机器配合是个复杂的任务。主要解决方案分为任务分配、任务分解。 a. 任务分配 假设单台服务器每秒支持5000次请求,那么两台服务器每秒支持10000次请求,但实际的性能一般按照8折8000次左右折算,随着性能的增高,任务分配器本身又会成为瓶颈。同时状态管理和故障处理的复杂度也会大大增加
任务分配常见方法:DNS轮询、智能DNS、CDN(内容分发网络)、GSLB设备(全局负载均衡)。
多对多如图:
以上是以业务处理为样例,实际上“任务”涵盖很广,可以指完整的业务处理、某个具体任务(存、运算、缓存等。任务分配器)可能是物理机器、独立运行的程序、嵌入在其他程序的算法
b. 任务分解
如果业务本身越来越复杂,单纯通过任务分配的方式性能只会越来越低,随着业务复杂度升高,单台机器的性能会越来越低。系统功能越简单,影响性能的点越少。
任务分解后,代码量,和功能都不会少,唯一的区别是代码从内部调用改为服务器之间的接口调用。
可以针对单个业务进行扩展,各个业务分解到独立的子系统后,性能瓶颈更容易被发现,修改风险降低。
但也不能拆的太细,为了完成某个业务,系统之间的调用次数会呈指数型上升。系统间的调用渠道目前是通过网络方式传输,远比函数调用低的多,如果业务本身没有发生大的变化,理论上性能是有一个上限的。系统拆分能让性能逼近这个极限
高可用
高可用:系统无中断的执行功能,单个硬件/软件都会有故障/bug,本质上无法做到“无中断”。 高可用方案:本质上是通过冗余来实现的。而冗余本质和高性能一样是通过增加机器来达到目的。单其目的不同,高性能增加机器为了“扩展”处理性能,高可用目的在于“冗余”处理单元。
“冗余”增强可用性的同时会增加复杂性。
根据不同场景来分析
1. 计算高可用
2. 存储高可用
存储和计算的本质区别在于:数据从一台机器搬到另一台机器需要进行传输,这就意味着数据在某个时间点上,数据肯定是不一致的。
其难点不在于备份,而在于如何规避数据不一致对业务造成的影响,分布式领域著名CAP定理从理论上论证了存储高可用的复杂度。高可用不可能同时满足“一致性、可用性、分区容错性”,最多满足两个,这就要求我们做架构个设计时结合业务进行取舍
3.高可用状态决策
无论计算高可用还是存储高可用,其基础都是状态决策。系统判断当前状态是否异常,然后采取行动保证高可用。如果状态决策本身有偏差,那么后续任何行动和处理都是无意义的。通过“冗余“实现高可用,状态决策就不可能做到完全正确。
可扩展性
面向对象的提出就是为了解决可扩展性带来的问题,后来的设计模式将可扩展性发挥到了极致。良好的可获粘性包含两个方面预测变化、应对变化
1. 预测变化
“唯一不变的时变化”,如果每个点都要考虑扩展性,架构师会不堪重负,且难以落地。但也不可能不做预测,“预测”意味着不是100%准确的。如何把握预测没有统一的标准,需要凭借经验、直觉。
- 不能每个设计都考虑扩展性
- 不能不考虑可扩展性
- 所有的预测纯在出错的可能性
2. 应对变化
如果准确预测,是否意味着可扩展性很容易实现呢?否!预测和采取方案时两回事。常见方案如下两种架构模型
无论采取哪种方式玻璃变化层和稳定层,都会带两两个性能相关问题
- 拆分(不明确),不同的人有不同的理解
- 需要设计变化层和稳定层之间的接口
对于稳定层来说,接口肯定越稳定越好,对于变化层来说,在有差异的多个实现方式中找出共同点,还要保证新功能加入时不怎么修改,是一件复杂的事情。存储层如何向稳定层提供接口?自适应判断?
常见应对变化的方案时提炼出一个抽象层(稳定的)、和一个实现层(根据业务实现来定制),拿装饰者设计模式举例:规则一旦抽象出来则不能轻易修改
低成本
当我们满足高性能,高可用通常会增加服务器,但底层本恰巧相反。很多时候低成本不会时首要目标。
底成本的复杂度主要体现在:往往只有创新才能达到低成本目标。比如开创全新领域或引入新技术。比如NoSQL解决关系型数据库无法应对高并发访问带来的压力。比如引入新技术,就需要区学习新技术,并且和现有技术结合起来
安全
1.功能安全(代码层,防小偷)
XSS攻击、CSRF攻击、SQL注入、Windows漏洞、密码破解等。功能安全是个逐步完善的过程 2. 架构安全(防强盗)
传统的架构安全主要靠防火墙(隔离网络),将网络划分成不同区域,不同区域控制策略来控制不同信任程度去遇见传送的数据流。功能强大但性能一般,无法应对互联网的高并发。目前更多以来运营商和云服务商的带宽和流量的清洗能力。
规模
代码叠加,量变引起质变。
- 功能越来越多,导致系统复杂度上升
- 数据越来越多,系统复杂度发生质变
最近火热的“大数据”由此产生,数据太多,传统的数据收集、加工、存储、分析手段和工具无法适应,需要新技术才能解决
数据库存储,修改耗时、备份时长,拆表,整合数据等