软件架构设计
软件架构的概念
软件架构 = 软件体系结构
架构设计就是需求分配,即将满足需求的职责分配到组件上
架构的本质
- 软件架构为软件系统提供了一个结构、行为和属性的高级抽象
- 软件架构风格是特定应用领域的惯用模式,架构定义一个词汇表和一组约束
架构的作用
- 软件架构是项目干系人进行交流的手段
- 软件架构是可传递和可复用的模型,通过研究软件架构可能预测软件的质量
- 软件架构使推理和控制的更改更加简单,有助于循序渐进的原型设计,可以作为培训的基础
架构的发展历程
- 无架构阶段 汇编语言
- 萌芽阶段 程序结构设计
- 初级阶段 统一建模语言
- 高级阶段 4+1视图
架构的4+1视图

软件架构风格
ADL
ADL是这样一种形式化语言,它再底层予以模型的支持下,为软件系统的概念体系结构建模提供了具体语法和概念框架。如:Aesop、MetaH、C2等
ADL的三个基本元素
- 构件:计算或数据存储单元
- 连接件:用于构件之间交互建模的体系结构构造块及其支配这些交互的规则
- 架构配置:描述体系结构的构件与链接件的连接图
基于架构的软件开发方法
概念
- ABSD方法是架构驱动,即强调由业务、质量和功能需求的组合驱动架构设计
- ABSD方法有三个基础。第一个基础是功能的分解。在功能分解中,ABSD方法使用已有的基于模块的内聚和耦合技术;第二个基础是通过选择架构风格来实现质量和业务需求;第三个基础是软件模板的使用
- 视角与视图:从不同的视角来检查,所以会有不同的视图
- 用例用来捕获功能需求、特定场景用来捕获质量需求
开发过程

软件架构风格
架构风格定义了用于描述系统的术语表和一组指导构建系统的规则

数据流风格

批处理序列:大量整体数据、无需用户交互 管道-过滤器:流式数据、弱用户交互
优点
- 松耦合【高内聚-低耦合】
- 良好的重用性/可维护性
- 可扩展性【标准接口适配】
- 良好的隐蔽性
- 支持并行
缺点
- 交互性较差
- 复杂性较高
- 性能较差(每个过滤器都需要解析与合成数据)
典型实例
传统编译器、网络报文处理
调用/返回风格

优点
- 良好的重用性,只要接口不变可用在其它处
- 可维护性好
- 可扩展性好,支持递增设计
缺点
- 并不是每个系统都方便分层
- 很难找到一个合适的、正确的层次抽象方法
- 不同层次之间耦合度高的系统很难实现
特点
各个层次的组件形成不同功能级别的虚拟机; 多层相互协同工作,而且实现透明
独立构件风格

优点
- 松耦合
- 良好的重用性/可修改性/可扩展性
缺点
- 构件放弃了对系统计算的控制。一个构件触发一个事件时,不能确定其他构件是否会响应它。而且即使它知道事件注册了哪些构件的过程,它也不能保证这些过程被调用的顺序。
- 数据交换的问题
- 既然过程的语义必须依赖于被处罚事件的上下文约束,关于正确性的推理就存在问题
特点
系统由若干子系统构成且成为一个整体;系统有统一的目标;子系统有主从之分;每一子系统有自己的事件收集和处理机制
事件处理器风格

虚拟机风格
解释器架构风格

规则系统体系结构风格
基于规则的系统构成:规则集、规则解释器、规则/数据选择及工作内存,一般用在人工智能领域和DSS(决策系统)中
仓库风格
闭环控制风格(过程控制)

适用于嵌入式系统,用于解决简单闭环控制问题 经典应用:空调温控,定速巡航
C2架构风格

基本规则
- 构件和连接件都有一个顶部和一个底部
- 构件的顶部要连接到连接件的底部,构件的地步要连接到连接件的顶部,构件之间不允许直连
- 一个连接件可以和任意数目和其他构件和连接件连接
- 当两个连接件进行直接连接时,必须由其中一个的底部到另一个的顶部
MDA
- 平台独立模型:具有高抽象层次、独立于任何实现技术的模型
- 平台相关模型:为某种特定实现技术量身定做,让你用这种技术中可用的实现构造来描述系统的模型。PIM会被换变换成一个或多个PSM
- 代码Code:用源代码对系统的描述(规约)。每个PSM都将被变换成代码
特定领域软件架构(DSSA)
基本概念
定义:特定领域软件架构以一个特定问题领域为对象,形成领域参考模型、参考需求、参考架构等组成的开发基础架构,支持一个特定领域中多个应用的生成

参与人员

建立过程

三层次模型

软件架构评估
质量属性

性能
性能是指系统的相应能力,即要经过多长时间才能对某个事件做出响应,或者在某段时间内系统所能处理的事件的个数
例如:
- 同时支持1000并发
- 响应时间小于1s
- 显示分辨率达到4k

可用性
可用性是系统能够正常运行的时间比例。经常用两次故障之间的时间长度或者出现故障时能够恢复正常的速度来表示
例如:
- 主服务器故障,1分钟内切换至备用服务器
- 系统故障,1小时内修改
- 系统支持7*24小时工作

安全性
安全性是指系统在向合法用户提供服务的同时能够阻止非授权用户使用的企图或拒绝服务的能力。安全性又可划分为机密性【信息不泄露给未授权的用户】、完整性【防止信息被篡改】、不可否认性【不可抵赖】及可控性【对信息的传播及内容具有控制的能力】等特性
例如:
- 可抵御SQL注入攻击
- 对计算机的操作都有完整记录
- 用户信息数据库授权必须保证99.9%可用

可修改性
可修改性是指能够快读地以较高的性能价格比对系统进行变更能力。通常以某些具体的变更为基准,通过考察这些变更的代价衡量可修改性
例如:
- 更改系统报表模块,必须在2人周内完成
- 对web界面风格进行修改,修改必须在4人月内完成

易用性
易用性关注的是对用户来说完成某个期望任务的容易程度和系统所提供的用户支持的种类
例如
- 界面友好
- 新用户学习使用系统时间不超过2小时
可测试性
软件可测试性是通过测试揭示软件缺陷的容易程度
例如:
- 提供远程调试接口,支持远程调试
软件架构评估
- 敏感点是一个或多个构件(和/或构件之间的关系)的特征
- 权衡点是影响多个质量属性的特征,是多个质量属性的敏感点
- 风险点是指架构设计中潜在的、存在问题的架构决策所带来的隐患
- 非风险点是指不会带来隐患,一般以“XXX要求是可以实现(或接收)的”方式表达
例如:
- 对交易请求处理时间的要求将影响系统的数据传输协议和处理过程的设计
- 假设每秒中用户交易请求的数量是10个,处理请求的时间为30毫秒,则“在1秒内完成用户的交易请求”这一要求是可以实现的
- 目前对系统信用卡支付业务逻辑的描述尚未达成共识,这可能导致部分业务功能模块的重复,影响系统的可修改性
- 更改加密的级别将对安全性和性能产生影响
评估方法
- 基于调查问卷(检查表)的方式
- 基于度量的方式
- 基于场景的方式

【场景】是从风险承担者的角度与系统交互的简短描述 场景可从六个方面进行描述:刺激源、刺激、制品、环境、响应、响应度量
【性能场景示例】

【软件架构分析法(SAAM)】 最初关注可修改性,后扩充到可移植性、可扩充性等

【架构权衡分析法(ATAM)】 在SAAM发展而来,主要针对:性能、实用性、安全性可修改性,在系统开发之前,对则这些质量属性进行评价和折中
【成本效益分析法(CBAM)】 在ATAM基础上建立的,软件的“经济”模型
质量效用树

软件产品线
概念

双生命周期模型

建立产品线

- 将现有产品演化为产品线
- 用软件产品线替代现有产品集
- 全新软件产品线的演化
- 全新软件产品线的开发
组织结构类型
- 设立独立的核心资源小组
- 不设立独立的核心资源小组
- 动态的组织结构
要成功实施产品线,主要取决于以下因素
- 对该领域具备长期和深厚的经验
- 一个用于构建产品的好的核心资源库
- 好的产品线架构
- 好的管理(软件资源、人员组织、过程)支持
构件与中间件技术
构件
构件概念
- 定义1:软件构件是一种组装单元,它具有规范的接口规约和显示的语境依赖。软件构件可以被独立地部署并由第三方任意地组装
- 定义2:构件是某系统中有价值的、几乎独立的并可替换的一个部分,它在良好定义的体系结构语境内满足某清晰的功能
- 定义3:构件是一个独立发布的功能部分,可以通过其接口访问它的服务

构件特征
- 构件系统体系结构由一组平台决策、一组构件框架和构件框架之间的互操作设计组成
- 构件框架是一种专门的体系结构(通常围绕一些关键的机制),同时,也是一组固定地作用于构件层次机制的策略
- 概念框架的互操作设计包括系统体系结构连接的所有框架间的互操作的规则
- 构件是一组通常需要同时部署的原子构件。构件和原子构件之间的区别在于大多数原子构件永远都不会被单独部署,尽管它们可以被单独部署
- 一个原子构件是一个模块和一组资源
- 模块是一组类和可能的非面向对象的结构提,比如过程或者函数
- 资源是一个类型化的项的固定集合
- 资源这个概念可以包含代码资源,进而包含模块。问题在于除了编译器编译一个模块或包生成的资源外,还可能存在其他的资源。在“纯对象”的方法中,资源是外部的不可改变的对象,不可改变是因为构件没有持久化的标志,而且复制不能被区分
构件标准

CORBA

中间件
中间件概念

中间件优点
- 面向需求。即设计师集中精力于业务逻辑本身
- 业务的分割和包容性。应用开发人员可以按照不同的业务进行功能的划分,体现为不同的接口或交互模式
- 设计与实现隔离。构件对外发声作用或构件间的交互,都是通过接口进行的,构件使用者只需要知道构件的接口,而不必关系其内部实现,这是设计与实现分离的关键
- 隔离复杂的系统资源。架构很重要的一个功能就是将系统资源与应用构件隔离,这是保证构件可复用甚至“即插即用”的基础,与中间件的意图也是一致的
- 符合标准的交互模型。中间件则实现了架构的模型,实现了标准的协议
- 软件复用。中间件提供了构件封装、交互规则、与环境的隔离等机制,这些都为软件复用提供了方便的解决方案
- 提供对应用构件的管理。基于中间件的软件可以方便地进行管理,因为i构件总可以通过标识机制进行划分
中间件分类

软件复用
软件复用【重用】是多次不同的软件开发过程中重复使用相同或相似【软件元素】的过程
构件的复用
- 检索与提取构件
- 基于关键字的检索
- 特点:树形或有向无回路图结构
- 刻面检索法
- 特点:利用Facet描述构件执行的功能、被操作的数据、构件应用的语境或任意其他特征
- 例如:分多个刻面:1、应用领域;2、使用环境;3、功能
- 超文本检索法
- 特点:按照人类的联想思维方式任意跳转到包含相关概念或构件的文档
- 理解与评价构件
- 要复用构件,准确地理解构件至关重要。特别是对构件修改使用时
- 为达到目的,必须要求构件的开发过程遵循公共标准
- 一般构件库的文档中全面而准确地说明一下内容:构件的功能与行为、相关的领域知识、可适应性约束条件与例外情形、可以预见的修改部分及修改方法
- 修改构件
- 理想状态时直接复用构件库中现成的构件,但大多数情况下,必须对构件进行或多或少的修改,以应对新需求
- 为了减少构件修改的工作量,要求开发人员尽量使构件的功能、行为和接口设计更为抽象化、通用化和参数化。这样,复用者即可通过对实参的选取来调整构件的功能或行为。如果这种调整仍不足以使构件适用于新系统,复用者就必须借助设计信息和文档来修改构件
- 构件库中若无可修改使用的构件,则按需求开发构件,并存入构件库
- 组装构件
- 组装的三种方式
- 基于功能的组装:采用子程序调用和参数传递的方式将构件组装起来
- 基于数据的组装:仍然使传统的子程序调用与参数传递。但它所依赖的软件设计方法不再是功能分解,而是面向数据的设计方式,例如,Jackson系统开发方法
- 面向对象的组装:如果从类库中检索出来的基类能够完全满足新系统的需求,则可以直接应用。否则,必须以基类为父类,生成响应的子类,以满足新系统的需求
- 构件组装失配问题
- 由构件引起的失配,包括由于系统对构件基础设施、构件控制模型和构件数据模型的假设存在冲突引起的失配
- 由连接子引起的失配,包括由于系统对构件交互协议、连接子数据模型的假设存在冲突引起的失配
- 由于系统成分对全局体系结构的假设存在冲突引起的失配等。要解决失配问题,首先需要检测出失配问题,并再次基础上通过适当的手段消除检测出的失配问题
- 组装的三种方式
C/S架构与B/S架构

常用层次式架构

MVC架构风格
- Model(模型):应用程序的主题部分。模型表示业务数据和业务逻辑。一个模型通为多个视图提供数据。提高应用的可重用性
- View(视图):用户看到并与之交互的界面。接收用户输入数据,向用户展示数据
- Controller(控制器):用户界面与Model的接口。解释视图的输入,将其解释为系统能够理解的对象,同时识别用户运作,将其解释为模型特定方法的调用。处理来自于模型的事件和模型逻辑执行的结果,调用适当的视图为用户提供反馈
J2EE体系结构中:
- 视图:JSP
- 控制器:Servlet
- 模型:Entity Bean、Session Bean

MVP架构风格
MVP是MVC的变种,其优点包括
- 模型与视图完全分离,可以修改视图而不影响模型
- 可以更高效地使用模型,因为所有交互都发生在一个地方【Presenter】内部
- 可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑
- 如果把逻辑放在Presenter中,就可以脱离用户接口来测试这些逻辑(单元测试)

MVVM架构风格

RIA架构风格

优点
- 反应速度快
- 易于传播
- 交互性强
数据访问层设计
ORM:对象与关系数据之间的映射

物联网分层架构
大数据分层架构

Wen Service(WEB服务)

REST(表属性状态转移)
REST是一种通常使用HTTP和XML进行基于Web通信的技术,可以降低开发的复杂性,提高系统的可伸缩性
添加用户 GET /api/addUser -> POST /api/User 删除用户 GET /api/deleteUser -> DELETE /api/User 修改用户 GET /api/updateUser -> PUT /api/User
REST的5个原则
- 网络上的所有事务都被抽象为资源
- 每个资源对应一个唯一的资源标识
- 通过通用的连接件接口对资源进行操作
- 对资源的各种操作不会改变资源的标识
- 所有的操作都是无状态的
ESB(企业服务总线)

- 提供位置透明性的消息路由和寻址服务
- 提供服务注册和命名的管理功能
- 支持多种的消息传递范型
- 支持多种可以广泛使用的传输协议
- 支持多种数据格式及其相互转换
- 提供日志和监控功能
微服务
顾名思义,就是很小的服务,所以它属于面向服务架构中的一种
优点

模式方案

微服务与SOA的区别
- 微服务比SOA更精细,可以独立进程方式存在
- 微服务接口更通用化,如用HTTP RESTful,各种终端都可调用,语言无关,平台无关
- 更倾向于分布式部署,互联网场景更适合
云计算
定义
云计算是集合了大量计算设备和资源,对用户屏蔽底层差异的分布式处理架构,其用户与提供实际服务的计算资源是相分离的
优点
超大规模、虚拟化、高可靠性、高可伸缩性、按需服务、成本低【前期投入低、综合使用成本也低】
分类
按服务类型分类
- Sass【软件即服务】:基于多租户技术实现,直接提供应用程序
- Paas【平台即服务】:虚拟中间件服务器、运行环境和操作系统
- Iaas【基础设施即服务】:包括服务器、存储和网络等服务
按部署方式分类
- 公有云:面向互联网用户需求,通过开放网络提供云计算服务
- 私有云:面向企业内部提供云计算服务
- 混合云:兼顾以上两种情况的云计算服务
架构

【管理层】:提供对所有层次云计算服务的管理功能 【用户访问层】:方便用户使用云计算服务所需的各种支撑服务,针对每个层次的云计算服务都需要提供响应的访问接口 【应用层】:提供软件服务,如:财务管理,客户关系管理,商业智能 【平台层】:为用户提供对资源层服务的封装,使用户可以构建自己的应用 【资源层】:提供虚拟化的资源,从而隐藏物理资源的复杂性。如:服务器,存储
云原生架构
使基于分布部署和统一管理的分布式云,以容器、微服务、DevOps等技术为基础建立的一套云技术产品体系
设计原则
- 服务化原则:使用微服务
- 弹性原则:可根据业务变化自动伸缩
- 可观测原则:通过日志、链路跟踪和度量
- 韧性原则:面对异常的抵御能力
- 所有过程自动化原则:自动化交付工具
- 零信任原则:默认不信任网络内部和外部的任何人/设备/系统
- 架构持续演进原则:业务高速迭代情况下的架构与业务平衡
架构模式
- 服务化架构模式:典型代表【微服务】,服务拆分使维护压力大增
- Mesh化架构模式:把中间件框架(RPC、缓存、异步信息)从业务进程中分离,由Mesh进程完成
- Serverless模式:非常适合于事件驱动的数据计算任务
- 存储计算分离模式:各类暂态数据用云服务保存
- 分布式事务模式:解决微服务模式中多数据源事务问题
- 可观测架构:包括Logging、Tracing、Metrics三个方面
- 事件驱动架构:本质上是一种应用/组件间的集成架构模式
反模式
- 庞大的单体应用【需要多人开发的业务模块,考虑通过服务化进行拆分,并让组织与架构匹配】
- 单体应用“硬拆”为微服务(服务拆分要适度)【小规模软件的服务拆分(为拆而拆)、数据依赖(服务间数据依赖)、性能降低】
- 缺乏自动化能力的微服务【手动维护大龄微服务是不现实的】
微服务设计约束
- 微服务个体约束【每个微服务都是独立的,修改一个微服务不能影响另一个微服务】
- 微服务与微服务之间的横向关系【通过第三方服务注册中心来满足服务的可发现性】
- 微服务与数据层之间的纵向约束【数据是微服务的“私产”,访问时需要通过微服务】
- 全局视角下的微服务分布式约束【高效运维整个系统】
边缘计算
【边缘计算】是指在靠近物或数据源头的一侧,采用网络、计算、存储、应用核心能力为一体的开放平台,就近提供最近端服务 【边缘计算的本质】计算处理职能的本地化

大型网站系统架构演化

第一阶段:单体架构到第二阶段:垂直架构

第三阶段:使用缓存改善网站性能

常见缓存技术
- MemCache:Memcache是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负载。Memcache通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等
- Redis:Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
- Squid:Squid是一个高性能的代理缓存服务器,Squid支持FTP、gopher、HTTPS和HTTP协议
缓存与数据库的数据一致性问题
缓存技术对比

Redis分布式存储方案

Redis集群切片的常见方式

Redis数据分片方案

Redis数据类型

Redis数据淘汰算法

Redis的持久化
Redis的持久化主要有两种方式:RDB和AOF
- RDB:传统数据库中快照的思想。指定时间间隔将数据进行快照存储
- AOF:传统数据库中日志的思想,把每条改变数据集的命令追加到AOF文件末尾,这样出问题了,可以重新执行AOF文件中的命令来重建数据集

Redis常见问题
缓存雪崩
大部分缓存失效 -》 数据库崩溃
解决方案
- 使用锁或队列:保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上
- 为key设置不同的缓存失效时间:在固定的一个缓存时间的基础上+随机一个时间作为缓存失效时间
- 二级缓存:设置一个有时间限制的缓存+一个无时间限制的缓存。避免大规模访问数据库
缓存穿透
查询无数据返回 =》 直接查数据库
解决方案
- 如果查询结果为空,直接设置一个默认值放到缓存,这样第二次到缓存中获取就有值了,设置一个不超过5分钟的过期时间,以便能正常更新缓存
- 设置布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
缓存预热
系统上线后,将相关需要缓存数据直接加到缓存系统中
解决方案
- 直接写个缓存刷新页面,上线时手工操作
- 数据量不大时,可以在项目启动的时候自动进行加载
- 定时刷新缓存
缓存更新
除Redis系统自带的缓存失效策略,常见采用一下两种
- 定时清理过期的缓存
- 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的化就去底层系统得到新数据并更新缓存
缓存降级
降级的目的是保证核心服务可用。即使是有损的,而且有些服务是无法降级的(如电商的购物流程等);在进行降级之前要对系统进行梳理,从而梳理出哪些必须保护,哪些可降级
第四阶段:使用服务集群改善网站并发处理能力

系统演变到这里,将会出现下面几个问题: 1、用户的请求由谁来转发到具体的应用服务器 2、用户如果每次访问到的服务器不一样,那么如何维护session的一致性
解决方案
- 负载均衡
- 有状态与无状态问题
负载均衡

应用层负载均衡
- http重定向。HTTP重定向就是应用层的请求转发。用户的请求其实已经到了HTTP重定向负载均衡服务器,服务器根据算法要求用户重定向,用户收到重定向请求后,再次请求真正的集群
- 特点:实现简单,但性能较差
- 反向代理服务器。在用户的请求到达反向代理服务器时(已经到达网站机房),由反向代理服务器根据算哒转发到具体的服务器。常用的apache、nginx都可以充当反向代理服务器
- 特点:部署简单,但代理服务器可能成为性能瓶颈
传输层负载均衡
- DNS域名解析负载均衡。DNS域名解析负载均衡就是在用户请求DNS服务器,获取域名对应的IP地址时,DNS服务器直接给出负载均衡后的服务器IP
- 特点:效率比HTTP重定向高,减少维护负载均衡服务器成本。但一个应用服务器故障,不能及时通知DNS,而且DNS负载均衡的控制权在域名服务商那里,网站无法做更多的改善和更强大的管理
- 基于NAT的负载均衡。基于NAT的负载均衡将一个外部IP地址映射为多个IP地址,对每次连接请求动态地转换为一个内部节点的地址
- 特点:技术较为成熟,一般在网关位置,可以通过硬件实现。像四层交换机一般就采用了这种技术
算法
- 静态算法(不考虑动态负载)
- 轮转算法:轮流将服务请求(任务)调度给不同的节点(即:服务器)
- 加权轮转算法:考虑不同节点处理能力的差异
- 源地址哈希散列算法:根据请求的源IP地址,作为散列键从静态分配的散列表找出对应的节点
- 目标地址哈希散列算法:根据请求目标IP做散列找出对应节点
- 随机算法:随机分配,简单,但不可控
- 动态算法(考虑动态负载)
- 最少连接数算法:新请求分配给当前活动请求数量最少的节点,每个节点处理能力相同的情况下
- 加权最小连接数算法:考虑节点处理能力不同,按最小连接数分配
- 加权百分比算法:考虑了节点的利用率、硬盘速率、进程个数等,使用利用率来表现剩余处理能力
有状态与无状态
- 无状态服务对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求里,要么可以从外部获取到(比如说数据库),服务器本身不存储任何信息
- 有状态服务则相反,他会在自身保存一些数据,先后的请求时有关联的
Session共享机制

第五阶段:数据库读写分离

主从数据库结构特点:
- 一般:一主多从,也可以多主多从
- 主库做写操作,从库做读操作
主从复制步骤:
- 主库更新数据完成前,将操作写binlog日志文件
- 从库打开I/O线程与主库连接,做binlog dump process,并将事件写入中继日志
- 从库执行中继日志事件,保持与主库一致
第六阶段:使用反向代理和CDN加速网站响应

CDN的全程是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的性能瓶颈和环节,使内容传输得更快、更稳定
第七阶段:使用分布式文件系统和分布式数据库系统

第八阶段:使用NoSQL和搜索引擎

第九阶段:业务拆分

第十阶段:分布式服务
