软件系统架构分层:
架构由三个要素组成:
- 组件:一组代码或独立的程序
- 连接件:过程调用、管道和消息,用于表示组件之间的相互关系
- 约束:组件连接时的条件
模块化:指的是一种软件开发方法,即把一个待开发的软件分解成若干小的简单的部分,成为模块。每一个模块都独立地开发、测试,最后再组装出整个软件。
模块的粒度小于服务组件的粒度。服务组件可以在应用之间复用。服务的调用通常是基于分布式协议(SOAP、HTTP、RMI/IIOP等),且通常是远程调用
软件架构在开发过程中的应用:
- 需求阶段:根据需求决定系统的功能,收集目标对象的基本信息—>软件规格说明
- 从需求模型向软件架构模型的转换:
- 设计阶段:该阶段需要细化至对系统进行模块化并选定描述各个部件间的详细接口、算法和数据类型,对上支持建立架构阶段形成的框架,对下提供实现基础
- 实现阶段
- 维护阶段
考点汇总(最重要的章节)
本章节每年考察20分左右,案例题必考1-2题
案例第一题是质量属性+架构风格
软件架构的概念
基于架构的软件开发:
软件架构风格(考风格匹配)
特定领域软件架构
软件质量属性
软件架构评估
软件产品线
构件与中间件技术
一、软件架构的概念
软件架构,即软件体系结构,是从需求分析到软件设计之间的过渡过程
架构的本质:
- 软件架构为软件系统提供了一个结构、行为和属性的高级抽象,由构件的描述、构件的相互作用(连接件)、指导构件集成模式以及这些模式的约束组成(哪些组成要记住)
- 软件架构风格是特定领域的惯用模式,架构定义了一个词汇表和一组约束
架构的作用:
- 软件架构是项目干系人进行交流的手段,明确了对系统实现的约束条件,决定了开发和维护组织的组织结构,制约着系统的质量属性。
- 软件架构是可传递和可复用的模型,通过研究软件架构可能预测软件的质量。
- 软件架构使推理和控制的更改更加简单,有助于循序渐进的原型设计,可以作为培训的基础。
软件架构设计的根本目的:支撑软件系统的全生命周期,解决好软件的复用、质量和维护问题(用最小的人力成本来满足构件和维护系统的需求)
架构设计就是需求分配,即将满足需求的职责分配到组件上
软件架构不仅指定了系统的组织结构和拓扑结构,并且显示了系统需求和构件之间的对应关系,提供了一些设计决策的基本原理
软件架构设计包括提出架构模型,产生架构设计和进行设计评审等活动,是一个迭代的过程。架构设计主要关注软件组件的结构、属性和交互作用,并通过多种视图全面描述特定系统的架构。
软件架构能够在设计变更相对容易的阶段,考虑系统结构的可选方案,便于技术人员与非技术人员就软件设计进行交互,能够展现软件的结构、属性与内部交互关系。
架构的发展历程
1.4+1视图
Q1:为什么要搞出这么多视图?用一个图不香吗
A:为了便于不同的人员进行分工。如果只用一张图太复杂
Q2:为什么要叫4+1不叫5?
A:中间的用例视图可以用于设计其他视图和测试其他视图
架构4+1视图与UML4+1视图是一一对应的
逻辑视图:系统的功能。类和对象的分解
- 表示法:UML(类图、交互图、顺序图、状态图)、E-R图
开发视图:软件在其开发环境中的静态组织结构。模块和子系统的分解
- 表示法:Booch标记法、UML(包图)
进程视图:并发和同步特性,任务与任务间的配合关系
- 表示法:UML(交互图、活动图、状态图)
部署视图:软件和硬件的映射关系
- 表示法:UML(部署图)
用例视图:重要的需求的抽象
架构描述语言(ADL)
Architecture Description Language,ADL,是一种为明确说明软件系统的概念结构和对这些概念结构建模提供功能的语言
ADL的三个基本元素(记住三个元素是什么)
- 构件:计算或数据存储单元
- 连接件:用于构件之间交互建模的体系结构构造块及其支配这些交互的规则
- 架构配置:描述体系结构的构件与连接件的链接图
基于架构的软件开发方法(ABSD)
Architecture-Based Software Design,ABSD,是一种架构驱动方法,即强调由业务、质量和功能需求的组合驱动架构设计
ABSD方法有三个基础:
- 功能的分解:ABSD方法使用已有的基于模块的内聚和耦合技术
- 通过选择架构风格来实现质量和业务需求
- 软件模版的使用
视角与视图:从不同的视角来检查,所以会有不同的视图。
利用视角与视图来描述软件架构,用例用来捕获功能需求、特定场景(质量场景)用来捕获质量需求
- 用例:是一种描述系统功能需求的方法,它描述了用户(Actor)与系统之间的交互过程,以实现特定的目标或任务。用例侧重于描述系统应该如何工作,即系统应该提供哪些功能和服务
- 特定场景:强调的是在某些特定情况下,系统应该如何表现或响应。如银行系统的高负载情况
开发过程:
2.软件架构设计与生命周期
需求分析阶段:需求分析和SA设计面临的是不同的对象:一个是问题空间;另一个是解空间。从软件需求模型向SA模型的转换主要关注两个问题:如何根据需求模型构建SA模型。如何保证模型转换的可追踪性。
- 保证模型转换的可追踪性:这意味着在整个设计过程中,必须保持需求与设计之间的一致性和可追溯性。每一个需求项都应该能够在设计文档中找到对应的设计实现方案。这样做的目的是确保最终的产品确实满足了最初的需求,并且在需求变更时能够快速定位到受影响的设计部分
设计阶段:是SA研究关注的最早和最多的阶段,这一阶段的SA研究主要包括:SA模型的描述、SA模型的设计与分析方法,以及对SA设计经验的总结与复用等。有关SA模型描述的研究分为3个层次:SA的基本概念(构件和连接子)、体系结构描述语言ADL、SA模型的多视图表示。
实现阶段:最初SA研究往往只关注较高层次的系统设计、描述和验证。为了有效实现SA设计向实现的转换,实现阶段的体系结构研究表现在以下几个方面:
- 研究基于SA的开发过程支持,如项目组织结构、配置管理等
- 寻求从SA向实现过渡的途径,如将程序设计语言元素引入SA阶段、模型映射、构件组装、复用中间件平台等
- 研究基于SA的测试技术
构件组装阶段:在SA设计模型的指导下,可复用构件的组装可以在较高层次上实现系统,并能够提高系统实现的效率。在构件组装的过程中,SA设计模型起到了系统蓝图的作用。研究内容包括如下两个方面:
- 如何支持可复用构件的互联,即对SA设计模型中规约的连接子的实现提供支持
- 在组装过程中,如何检测并消除体系结构失配问题
在构件组装阶段的失配问题主要包括:由构件引起的失配、由连接子引起的失配、由于系统成分对全局体系结构的假设存在冲突引起的失配等。
(了解)体系结构失配问题由David Garlan等人在1995年提出。失配是指在软件复用的过程中,由于待复用构件对最终系统的体系结构和环境的假设(Assumption)与实际状况不同而导致的冲突。在构件组装阶段的失配问题主要包括3个方面:
- 由构件引起的失配,包括由于系统对构件基础设施、构件控制模型和构件数据模型的假设存在冲突引起的失配
- 由连接子引起的失配,包括由于系统对构件交互协议、连接子数据模型的假设存在冲突引起的失配
- 由于系统成分对全局体系结构的假设存在冲突引起的失配等。要解决失配问题,首先需要能够检测出失配问题,并在此基础上通过适当的手段消除检测出的失配问题。
部署阶段:SA对软件部署作用如下:
- 提供高层的体系结构视图来描述部署阶段的软硬件模型
- 基于SA模型可以分析部署方案的质量属性,从而选择合理的部署方案
后开发阶段:是指软件部署安装之后的阶段。这一阶段的SA研究主要围绕维护、演化、复用等方面来进行。典型的研究方向包括动态软件体系结构、体系结构恢复与重建等。
- **动态软件体系结构:**现实中的软件具有动态性,体系结构会在运行时发生改变。
运行时变化包括两类:软件内部执行所导致的体系结构改变;软件系统外部的请求对软件进行的重配置。
包括两个部分的研究:体系结构设计阶段的支持、运行时刻基础设施的支持。 - 体系结构恢复与重建:对于现有系统在开发时候没有考虑SA的情况,从这些系统中恢复或重购体系结构。从已有的系统中获取体系结构的重建方法分为4类:手工体系结构重建、工具支持的手工重建、通过查询语言来自动建立聚集、使用其他技术(如数据挖掘等)。
编程范式
编程三大范式:结构化编程、面向对象编程、函数式编程
测试只能展示Bug的存在,并不能证明不存在Bug——Dijkstra
为什么不可变性是软件架构设计需要考虑的重点?——所有的竞争问题、死锁问题、并发更新问题都是由可变变量导致的
软件架构师应该着力于将大部分处理逻辑都归于不可变组件中,可变状态组件的逻辑应该越少越好
- 事件溯源:只存储事物记录,不存储具体状态。当需要具体状态时,只要从头开始计算所有的事物即可。(e.g.银行不保存具体账户余额,仅仅保存事物日志)
设计原则
SOLID原则:SRP、OCP、LSP、ISP、DIP
1.SRP
Single Responsibility Principle,单一职责原则,任何一个软件模块都应该只对某一类行为者负责。即只做一件事,只负责一类行为者。
-
主要是解决函数和类之间的关系
-
单一职责
- 高内聚:避免大而全,避免不相关功能的耦合
- 低耦合:减少所需要依赖和被依赖的类
-
哪种设计最好?
- 需求 => 职责 => 设计粒度
- 没有最好的设计,只有最合适的设计
SRP反面案例:
这个类的三个函数对应了三类非常不同的行为者
解决方案:
2.OCP
Open Close Principle,OCP,开闭原则。一个设计良好的软件系统应该在不需要修改的前提下就可以轻易被扩展。应当对扩展开放,对修改关闭
思想:用抽象构建框架,用实现扩展细节
- 不“开闭”的坏处:牵一发而动全身
- 开闭原则的好处:
- 测试简单
- 可复用性变强
- 稳定性变高
- 实现方式
- 接口和抽象类
3.LSP
Liskov Substitution Principle, LSP,里氏替换原则,任何基类可以出现的地方,子类一定可以出现。通俗理解:只要有父类出现的地方,都可以用子类来替代,反过来则不行,有子类出现的地方,不能用其父类替代
原则:
- 保证基类所拥有的性质在子类中仍然成立
- 子类扩展父类的功能,但是不能改变父类原有的功能
如何遵守LSP:
- 子类必须完全实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类可以实现自己特有的方法
- 当子类的方法实现父类的抽象方法时,方法的后置条件要比父类更严格
- 子类的实例可以替代任何父类的实例,但反之不成立
使用LSP的原因:
- 继承关系给程序带来侵入性
- 保证程序升级后的兼容性
注意事项:
- 聚合/组合 > 继承(合成复用原则)
4.ISP
Interface Segreation Principle, ISP,接口隔离原则
原则:
- 客户端不应该依赖它不用的接口 => 低藕合
- 如果客户依赖了不需要的接口,就要面临不需要接口变动带来的风险
- 提倡客户不应被迫使用对其而言无用的方法或功能
- 类之间的依赖应该建立在最小的接口上面 => 高内聚
- 把没有关系的接口合并在一起,会形成一个臃肿的大接口,这是对职责分配和接口的污染
- 最小接口:满足项目需求的相似功能
- 把庞大臃肿的接口拆成更小和更具体的接口
- 接口隔离:多个专门的接口 > 单一的总接口
注意事项:
- 设计粒度太小会适得其反
- 设计粒度参考,一个接口只服务于一个子模块或者业务逻辑
ISP vs SRP:
- 约束偏向:ISP偏向约束架构设计,SRP偏向约束业务
- 细化程度:ISP注重相似接口的隔离,SRP职责更精细
5.DIP
Dependency Inversion Principle, 依赖反转原则
优秀的软件设计师和架构设计师会花费很大精力来设计接口,以减少未来对其进行改动
典型应用—抽象工厂模式
思想:应针对接口编程,而不应该针对实现实现编程
原则:
- 高层不应该依赖低层,两者应该基于抽象
- 抽象不应该以来细节,细节依赖抽象
注意事项:
- 在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类
- 不要在具体实现类上创建衍生类
- 不要覆盖(override)包含具体实现的函数
- 避免在代码写入与任何具体实现相关的名字,或者是其他容易变动的事物的名字
软件架构
软件生命周期:开发、部署、运行、维护
设计良好的系统架构应该可以使开发人员对系统的运行过程一目了然。架构应该起到揭示系统运行过程的作用
软件架构的特征:
- 注重可重用性
- 利益相关者较多
- 关注点分离
- 质量驱动(可靠性、可扩展性需求等)
- 提倡概念完整性
- 循环风格
所有的软件系统都可以降解为策略与细节这两种主要元素:
- 策略:软件中所有的业务规则与操作过程,是系统真正的价值所在
- 细节:让操作该系统的人、其它系统以及程序员们与策略进行交互,但是又不会影响到策略本身的行为
软件系统的细节与策略应该脱离关系,例如:
- 开发的早期阶段,软件的高层策略不用关心数据库的类型
- 开发的早期阶段,不应该选定使用的Web服务
- 开发的早期阶段,不应该过早地才有REST
- 开发的早期阶段,不应过早地采用依赖注入框架(dependency injection framework)
因为一旦采用了某些技术细节,就会限制软件地发展方向
软件架构风格
软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式
- 特定应用领域:软件架构风格通常是针对某个特定类型的应用或系统的。例如,C/S架构适用于分布式计算环境,而微服务架构适用于高度模块化和可扩展的Web服务。
- 系统组织方式:架构风格定义了系统如何被组织起来,包括系统的主要组成部分(构件)、这些构件之间的交互方式(接口)以及组件是如何协作完成任务的(行为)。
- 惯用模式:这些组织方式通常是经过验证的、广泛认可的最佳实践或标准方法,反映了在特定领域中解决常见问题的经验总结。
软件架构风格包含一个架构定义、一个词汇表和一组约束:
- 词汇表:包含一些构件和连接件类型。指的是架构风格中使用的术语集,包括组件、接口、连接器等基本元素的名称和定义。词汇表定义了这些术语的意义,确保所有参与者在设计和实现系统时有一个共同的语言基础。
- 约束:指出系统是如何将这些构件和连接件组合起来的
架构风格定义了:用于描述系统的术语表和一组指导构建系统的规则
架构设计的一个核心问题是能否到达架构级的软件复用
软件架构风格分类:
-
数据流风格
- 批处理序列
- 管道-过滤器
-
调用/返回风格:
- 主程序/子程序
- 面向对象
- 层次结构
-
独立构件风格:
- 进程通信
- 事件驱动系统(隐式调用)
-
虚拟机风格:
- 解释器
- 基于规则的系统(应用在哪里记一下)
-
仓库(数据共享)风格:
- 数据库系统
- 黑板系统
- 超文本系统:应用在互联网领域
- 现代编译器的集成开发环境一般采用数据仓库
-
闭环控制(空调)
-
C2体系结构风格:通过连接件绑定在一起的按照一组规则运作的并行构件网络
五大架构风格 | 架构风格名 | 常考关键字及实例 | 简介 | 应用 |
---|---|---|---|---|
数据流风格 | 批处理 | 传统编译器,每个阶段产生的结果作为下一个阶段的输入,区别在于整体 | 一个接一个,以整体为单位 | |
管道-过滤器 | 一个接一个,前一个输出是后一个输入 | Shell,编译器模型 | ||
调用/返回风格风格 | 主程序/子程序(面向过程) | / | 显示调用,主程序直接调用子程序 | |
面向对象 | 对象是构件,通过对象调用封装的方法和属性 | 图书馆系统 | ||
层次结构 | 分层,每层最多影响其上下两层,有调用关系 | TCP/IP层次模型 | ||
独立构件风格 | 进程通信 | 进程间独立的消息传递,同步异步 | ||
事件驱动(隐式调用) | 事件触发推动动作,如程序语言的语法高亮、语法错误提示 | 不直接调用,通过事件驱动 | 调试器中的断点处理,Win32 GUI程序 | |
虚拟机风格 | 解释器 | 自定义流程,按流程执行,规则随时改变,灵活定义,业务灵活组合 | 解释自定义的规则,解释引擎、存储区、数据结构 | Java虚拟机、一些高级语言解释执行器 |
规则系统 | 一般与人工智能、机器人有关 | 自定义、规则集、规则解释器、选择器和工作内存,用于DSS和人工智能、专家系统 | ||
仓库风格 | 数据库 | 现代编译器的集成开发环境IDE,以数据为中心,又称为数据共享风格。 | 构件主要有两大类,一类是中央共享数据源,保存当前系统的数据状态;另一类是多个独立处理单元,处理单元对数据元素进行操作 | 中央共享数据源,独立处理单元 |
超文本 | 网状链接,多用于互联网 | 最著名的超文本系统——万维网 | ||
黑板 | 包括知识源、黑板数据结构和控制三部分。知识源包括若干独立计算的不同单元,提供解决问题的知识。知识源响应黑板的变化,也只修改黑板;黑板是一个全局数据库,包含问题域解空间的全部状态,是知识源相互作用的唯—媒介;知识源响应是通过黑板状态的变化来控制的。黑板系统通常应用在对于解决问题没有确定性算法的软件中(信号处理、问题规划和编译器优化等)。 | 语音识别、模式识别、图像处理、知识推理等问题复杂、解空间很大、求解过程不确定的这一类软件系统,黑板、知识源、控制 | ||
闭环风格 | 过程控制 | 汽车巡航定速,空调温度调节,设定参数,并不断调整 | 发出控制命令并接受反馈,循环往复达到平衡 | |
C2风格 | C2风格 | 构件和连接件、顶部和底部 | 通过连接件绑定在一起按照一组规则运作的并行构件网络 | 图形化用户界面的应用程序 |
1.数据流风格
所有的数据按照流的形式在执行过程中前行,不存在结构的反复和重构
优点:
- 松耦合(高内聚—低耦合)
- 良好的重用性/可维护性
- 可扩展性(标准接口适配)
- 良好的隐蔽性
- 支持并行
缺点:
- 交互性较差
- 复杂性较高
- 性能较差(每个过滤器都需要解析与合成数据)
1.批处理风格
每一步都是独立的,并且每一步都是顺序执行的,只有当前一步处理完毕后,后一步处理才开始。数据必须是完整的,作为一个整体进行传递。
2.管道—过滤器风格
每个组件都有一组输入和输出
过滤器必须是独立的实体,不能与其他的过滤器共享数据,而且一个功率起不知道它上游和下游的标识
适用于对于有序数据进行一系列已经定义的独立计算的应用程序
特征:
- 每个组件都有一组输入集和输出集
- 每个组件行为不受其他组件的影响
- 支持功能模块的重用,任意两个过滤器只要相互间所传输的数据格式上达到一致,就可以连接在一起
- 系统易于维护和扩展
- 支持并发执行
应用:
- Shell
- 传统编译器模型
- 网络包解析
2.调用/返回风格
分类:
- 主程序/子程序:面向过程
- 面向对象:对象的方法调用
- 分层:层与层之间的方法调用
优点:
- 良好的重用性,只要接口不变可用在其他处
- 可维护性好
- 可扩展性好,支持递增设计
缺点:
- 并不是每个系统都方便分层
- 很难找到一个合适的、正确的层次抽象方法
- 不同层次之间耦合度高的系统很难实现
特点:
- 各个层次的组件形成不同功能级别的虚拟机
- 多层相互协同共协作,而且实现透明
1.主程序/子程序风格
这种风格一般采用单线程控制,把问题划分为若干处理步骤,构件即为主程序和子程序
早期的计算机程序是非结构化的,即所有的程序代码均包含在一个主程序文件中。这种程序结构产生很多缺陷,易导致逻辑不清,且代码无法复用,难以与其他代码合并,修改和调试都非常困难
3.独立构件风格
优点:
- 松耦合
- 良好的重用性/可修改性/可扩展性
缺点:
- 构件放弃了对系统计算的控制。一个构件触发一个事件时,不能确定其他构件是否会影响它。而且即使它知道事件注册了哪些构件的过程,它也不能保证这些过程被调用的顺序
- 数据交换的问题
- 既然过程的语义必须依赖于被触发事件的上下文约束,关于正确性的推理就存在问题
特点:
- 系统由若干子系统构成且成为一个整体
- 系统有统一的目标
- 子系统有主从之分
- 每一个子系统有自己的事件收集和处理机制
1.进程通信架构风格
构件是独立的过程,连接件是消息传递
消息的方式可以是点到点、异步及远过程调用
2.事件驱动风格(隐式调用)
基于隐式调用,不直接调用一个过程,而是发布或广播一个或多个事件。当事件发生时,系统本身会调用所有与这个事件有关的过程,导致其他模块中过程的隐式调用
特点:
- 事件发布者不知道哪些组件会受到事件的影响
- 事件驱动系统的组件提供了一个过程集合和一组事件,过程可以使用显示的方法进行调用,也可以由组件在系统事件中注册
优点:
- 时间生命这不需要知道那些组件会影响事件,组件之间关联较弱
- 提高软件复用能力,只要在系统事件中注册组件的过程,就可以将该组件集成到系统中
- 系统便于升级
缺点:
- 放弃了对计算的控制权,完全由系统来决定
- 存在数据交换问题
- 该风格中正确性验证成为一个问题
4.虚拟机风格
分类:
- 解释器风格
- 以规则为中心的风格
优点:
- 可以灵活应对自定义场景
缺点:
- 复杂度较高
1.解释器风格
解释器是一个用来执行其他程序的程序,针对不同的硬件平台实现了一个虚拟机,将高抽象层次的程序翻译为低抽象层次所能理解的指令,以弥补程序语义所期望的与硬件提供的计算引擎之间的差距
解释器包括两个部分:
- 伪码:需要被解释的源代码和解释引擎分析所得到的中间代码组成
- 解释引擎:包括语法、解释器的定义和解释器当前执行状态
优点:
- 有利于实现程序的可移植性和语言的跨平台能力
- 可以对未来的硬件进行模拟和仿真,降低测试所带来的复杂性和昂贵花费
缺点:
- 额外的间接层次导致了系统性能的下降
2.基于规则的系统风格
把频繁变化的业务逻辑抽取出来,形成独立的规则库。这些规则可独立于软件系统存在,可被随时更新。由于规则都是类似于自然语言的形式书写,无法被系统直接执行。故而需要提供用来解释规则的“解释器”
基于规则的系统构成:规则集、规则解释器、规则/数据选择及工作内存,一般用在人工智能领域和DSS(决策支持系统)中
基于规则的系统:使用模式匹配搜索来寻找规则并在正确的时候应用正确的逻辑知识的虚拟机。基于规则的系统提供了易中基于专家系统解决问题的手段,将知识表示为“条件—行为”的规则,当满足条件时触发响应的行为,而不是将这些规则直接写在程序源代码中
适用于专家库
5.仓库风格(以数据为中心的风格)
仓库风格中,有两种不同的构件:中央数据结构说明当前状态,独立构件在中央数据存储上执行,仓库与外构件间的相互作用在系统中会有大的变化
子系统:
- 数据库系统
- 黑板系统
- 超文本系统
1.数据库风格
- 传统的数据库型知识库:由输入流中事务触发系统相应的进程执行。构件有两大类,一个是中央共享数据源,保存当前系统的数据状态;另一个是多个独立处理元素,处理元素对数据元素进行操作
- 黑板知识库:由中心数据结构的当前状态触发系统相应的进程执行
优点:
- 便于模块间的数据共享
- 方便模块的增删改
- 避免了知识源的不必要的重复存储
缺点:
- 各个模块需要一定的同步/加锁机制以保证数据结构的完整性和一致性
2.黑板风格
存在一个对公共数据结构进行协同操作的独立程序集合。每个这样的程序专门解决一个子问题,但需要协同工作共同完成整个问题的求解。
存在一个中心控制部件(黑板),这是一个数据驱动或状态驱动的控制机制,保存着系统的输入、问题求解各个阶段的中间结果和反映整体问题求解进程的状态
优点:
- 可扩充性强,耦合松散,便于扩充
- 便于多客户共享数据,无需关心数据来源
- 便于新的作为知识源代理的应用程序,便于扩展共享的黑板数据结构
- 知识源可重用,容错性和健壮性
缺点:
- 需要同步/加锁机制保证数据结构的完整性和一致性
- 测试困难
- 不能保证有好的解决方案
- 抵消,开发困难
- 缺少并行机制
6.C2风格
Chiron-2,是基于组件和消息的架构风格,适用于GUI软件开发,用以构建灵活和可扩展的应用系统,所有组件之间的交互必须通过异步消息机制来实现
异步消息机制:发送和接收消息时,发送方和接收方不需要同时执行,也就是同步执行
特点:
- 系统中的构件和连接件都有一个顶部和一个底部
- 组件的顶部应连接到某连接件的底部,组件的底部则应连接到某连接件的顶部,而组件与组件之间的直接连接是不允许的
- 一个连接件可以与任意数目的其他组件和连接件连接
- 当两个连接件进行直接连接时,必须由其中一个的底部连接到另一个的顶部
优点:
- 可使用任何编程语言开发组件,组件重用和替换易实现
- 由于组件之间相对独立,依赖较小,因而该风格具有一定扩展能力,可支持不同粒度的组件
- 组件无需共享地址空间
- 可实现多个用户和多个系统之间的交互
- 可使用多个工具集和多种媒体类型,动态更新系统框架结构
缺点:
- 不太适合大规模流式风格系统以及对数据库使用比较频繁的应用
C/S架构和B/S架构
1.C/S风格
客户机/服务器(Client/Server)风格,主要针对资源不对等问题提出的一种共享策略
客户机:完成特定的工作向服务器发出请求
服务器:处理客户机的请求并返回结果
两层CS架构——胖客户端
特点:
- 在客户端安装软件,业务处理安排在客户端中进行
- 软件升级维护困难
三层架构设计——瘦客户端
三层C/S架构:将处理功能独立出来,表示层和数据层都变得简单。表示层在客户机上,功能层在应用服务器上,数据层在数据库服务器上。即将两层C/S架构中的数据从服务器中独立出来了。其优点下面四点:
- 各层在逻辑上保持相对独立,整个系统的逻辑结构更为清晰,能提高系统和软件的可维护性和可扩展性
- 允许灵活有效的选用相应的平台和硬件系统,具有良好的可升级性和开放性
- 各层可以并行开发,各层也可以选择各自最适合的开发语言
- 功能层有效的隔离表示层与数据层,为严格的安全管理奠定了坚实的基础,整个系统的管理层次也更加合理和可控制
三层C/S架构设计的关键在于各层之间的通信效率,要慎重考虑三层间的通信方法、通信频度和数据量,否则即使分配给各层的硬件能力很强,性能也不高。
CS架构优点:
- 客户机组件和服务器组件分别运行在不同的计算机上,有利于分布式数据的组织和处理
- 组件之间的位置是透明的,客户机程序和服务器程序都不必考虑对方的实际存储位置
- 组件之间彼此独立和隔离,易于系统功能的扩展
- 将大规模的业务逻辑分布到多个通过网络连接的低成本的计算机上,降低了系统的整体开销
缺点:
- 开发成本较高
- 大部分工作都集中在客户机程序的设计上,增加了设计的复杂度
- 信息内容和形式单一
2.B/S架构
浏览器/服务器(Browser/Server)是三层C/S风格的一种实现方式,主要包括浏览器、Web服务器和数据库服务器
B/S架构核心是Web服务器,可以将应用程序以网页的形式存放在Web服务器上。当用户运行某个程序时,只需要在客户端的浏览器中输入相应的URL,向Web服务器提出HTTP请求。Web服务器接收HTTP请求后会调用相关的应用程序,同时向数据库服务器发送数据操作请求。数据库服务器对数据操作请求进行响应,将结果返回给Web服务器的应用程序。Web服务器应用程序执行业务处理逻辑,利用HTML来封装操作结果,通过浏览器呈现给用户。在B/S架构中,数据请求、网页生成、数据库访问和应用程序执行全部由Web服务器来完成
优点:
- 客户端只需要安装浏览器,操作简单
- 运用HTTP协议和统一客户端软件,可以实现跨平台通信
- 开发成本低,只需要维护Web服务器程序和中心数据库
- 不需要依赖客户端,升级维护容易
缺点:
- 个性化程度低,客户端程序的功能都是一样的
- 客户端数据处理能力比较差,加重了Web服务器的工作负担,影响系统的整体性能
- 不利于在线事务处理
- B/S架构的可扩展性比较差,系统安全性难以保障
- B/S架构的应用系统查询中心数据库的速度要远低于C/S架构
常用层次式架构
层次架构是最通用的架构,常作为初始架构
【关注分离】每层只负责本层的工作
1.MVC风格(非常重要)
模型-视图-控制器(Model-View-Controller)风格,
模型(Model):是应用程序中用于处理应用程序数据逻辑的部分。一个模型通常为多个视图提供数据。通常模型对象负责在数据库中存取数据。模型表示业务数据和业务逻辑。模型接收视图请求的数据,并返回最终的处理结果。业务模型的设计是MVC的核心。目前流行的EJB模型就是一个典型的应用例子
视图(View):是应用程序中处理数据显示的部分。接收用户输入数据,向用户展示数据。通常视图是依据模型数据创建的。代表用户交互界面。视图向用户显示相关的数据,并能接收用户的输入数据,但是它并不进行任何实际的业务处理。视图模块包含UI渲染
控制器(Controller):是应用程序中处理用户交互的部分。是用户与Model的接口。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。控制器的作用很明显,主要是告诉你:该选择什么样的模型、视图,以及可以完成什么样的用户请求等。控制器不做任何数据处理
MVC最重要的特性之一—关注分离,每个组件几乎不了解其他组件,只是通过抽象接口来处理应用程序的其他区域(松耦合)
J2EE体系结构中:
- 视图:JSP
- 控制:Servlet
- 模型:Entity Bean、Session Bean
MVC脆弱性分析
- 缺少对调用者进行安全验证的方式
- 数据传输不够安全
主要的不足:
- 增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使得模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率
- 视图与控制器间过于紧密的连接。视图与控制器是相互分离但确实联系紧密的部件,没有控制器的存在,视图应用是很有限的,反之亦然,这样就妨碍了它们的独立重用
- 视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问也将损害操作性能
MVP架构风格
MVP是MVC的变种,优点在于:
- 模型与视图完全分离,可以修改视图而不影响模型
- 可以更高效地使用模型,因为所有交互都发生在Presenter内部
- 可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑
- 如果把逻辑放在Presenter中,就可以脱离用户接口来测试这些逻辑(单元测试)
MVVM架构风格
特点:
- 双向数据绑定:ViewModel和View之间通过双向数据绑定实现数据的自动同步。当Model的数据发生变化时,ViewModel会自动更新View;同样,当用户在View上进行操作时,ViewModel也会自动更新Model的数据
- 事件驱动:ViewModel中定义了View可能触发的时间,并在这些事件发生时执行相应的逻辑。这使得View和ViewModel之间的交互更加清晰和可控
- 命令绑定:ViewModel中定义了一系列命令(Command),这些命令可以在View中通过特定的方式进行绑定。当用户触发这些命令时,ViewModel会执行相应的逻辑。
RIA富互联网
分层架构
分层架构讲应用系统正交地划分为若干层,每一层只解决问题的一部分,通过各层的协作提供整体解决方案。
分层架构的缺点:
- 一旦某个底层发生错误,可能会导致整个程序无法正常运行
- 将系统隔离为多个独立的层,这就要求在层与层之间引入通信机制,降低性能
物联网分层架构
以下主要出现在案例分析:
混合架构风格
富互联网应用RIA
MVC架构(非常重要)
MVP架构
MVVM架构
SOA架构(面向服务的架构风格)(重要)
SOA风格
Service-Oriented-Architecture,面向服务架构模型。是一种粗粒度、松耦合服务架构,通过这些服务之间定义良好的接口和契约联系起来。其中服务是一个粗粒度、可发现的软件实体。应用组件通过网络通讯协议提供和访问服务
核心概念:
- 服务:是一种为了满足某项业务需求的操作、规则等的逻辑组合,它包含一系列有序活动的交互,为实现用户目标提供支持
- SOA并不仅仅是一种开发方法,还具有管理上的优点,管理员可直接管理开发人员所构建的相同服务。多个服务通过企业服务总线提出服务请求,由应用管理来进行处理。
实施SOA的关键目标是实现企业IT资产重用的最大化,在实施SOA过程中要牢记以下特征:
- 可从企业外部访问、随时可用(服务请求能被及时响应)
- 粗粒度接口(粗粒度提供一项特定的业务功能,而细粒度服务代表了技术构件方法)
- 服务分级、松散耦合(服务提供者和服务使用者分离)
- 可重用的服务及服务接口设计管理、标准化的接口(WSDL、SOAP、XML是核心)
- 支持各种消息模式、精确定义的服务接口。
实现SOA的关键方面:
- 服务的定义与封装:决定哪些业务功能应该被封装为服务,以及如何定义这些服务的接口和行为
- 服务的交互:定义服务之间的通信协议和数据交换格式,确保服务能够有效地相互操作
- 服务的注册与发现:通过服务注册表(如UDDI)来注册和发现服务,使服务易于被发现和重用
- 企业服务总线(ESB):提供中间件来支持服务的集成和通信,简化服务之间的接口和协议转换
SOA的好处:
- 封装性:服务封装了业务逻辑,对外部隐藏了实现细节,增加了代码的安全性和隔离性
- 可重用性:服务一旦被创建,可以在多个场合被重用,减少了开发成本和时间
- 易维护性:由于服务是独立的,修改一个服务的内部实现不会直接影响其他服务
- 易扩展性:在SOA中,可以通过添加新服务或扩展现有服务来满足业务需求的变更
SOA体系架构中实体之间的操作:
- 发布:为了便于服务请求者发现,服务提供者将对服务接口的描述信息发布到服务注册中心上
- 发现:服务请求者通过查询服务注册中心的数据库来找到需要的服务,服务注册中心能够通过服务的描述对服务进行分类,使服务请求者更快定位所需要的服务范围
- 绑定和调用:服务请求者在查询到所需要的服务描述信息后,根据这些信息服务请求者能够调用服务
实施SOA的关键目标是实现企业IT资产重用的最大化,在实施SOA过程中要牢记以下特征:可从企业外部访问、随时可用(服务请求能被及时响应)、粗粒度接口(粗粒度提供一项特定的业务功能,而细粒度服务代表了技术构件方法)、服务分级、松散耦合(服务提供者和服务使用者分离)、可重用的服务及服务接口设计管理、标准化的接口(WSDL、SOAP、XML是核心)、支持各种消息模式、精确定义的服务接口。
从基于对象到基于构件再到基于服务,架构越来越松散耦合,粒度越来越粗,接口越来越标准。
基于服务的构件与传统构件的区别有四点:
- 服务构件粗粒度,传统构件细粒度居多
- 服务构件的接口是标准的,主要是WSDL接口,而传统构件常以具体API形式出现
- 服务构件的实现与语言是无关的,而传统构件常绑定某种特定的语言
- 服务构件可以通过构件容器提供QoS的服务,而传统构件完全由程序代码直接控制
服务
服务契约:服务消费方与服务提供方交互的所有约定的集合,通常由如下元素构成:
- 服务接口:接口定义,描述服务做什么、出入参定义等,通常通过接口文档的形式呈现。围绕着一个核心的业务功能操作以及相关联的操作
- 服务策略: 服务双方的隐性的共识,描述适应场景、约束等
- 服务水平:通过服务质量、可用性、性能等各个方面衡量服务本身,基本等价于 SLA,包含了服务的两个重要方面的指标 - 业务指标和技术指标(RT、吞吐量、可用性、可靠性等;业务指标 - 完成的业务功能的质量或者完成度,e.g. 补货建议是否满足业务预期的周转缺货 KPI)
- 服务实现:对业务功能的实现,是对SOA核心本质的映射
2.SOA关键技术
功能 | 协议 |
---|---|
发现服务 | UDDI、DISCO |
描述服务 | WSDL、XML Schema |
消息格式层 | SOAP、REST |
编码格式层 | XML(DOM,SAX) |
传输协议层 | HTTP、TCP/IP、SMTP等 |
1.WSDL
Web Service Description Language,Web服务描述语言,是对服务进行描述的育英,有一套基于XML的语法定义
WSDL描述的重点是服务,它包含服务实现定义和服务接口定义
2.REST
Rrepresentational State Transfer,表述性状态转移,是一种通常使用HTTP和XML进行基于Web通信的软件架构风格,可以降低开发的复杂性,提高系统的可伸缩性
REST五原则:
- 资源导向:将系统的功能抽象为资源的集合,并通过唯一的标识符(URI)来访问和操作这些资源。客户端通过对资源的增删改查操作来完成系统的业务需求
- 统一接口:REST要求使用统一的接口规范来定义资源的操作方式,包括使用HTTP动词(GET、POST、PUT、DELETE等)进行操作、使用URI标识资源位置、使用媒体类型(如JSON、XML等)进行数据交互等。
- 分层系统:REST支持将网络中的组件分为多个层次,每个层次都只关注自己的功能,降低了子系统之间的耦合性,提高了系统的可扩展性和灵活性。
- 可缓存:REST非常鼓励使用缓存来改善系统的性能和可伸缩性。服务器可以通过在响应中添加Cache-Control头部来指示响应是否可以被缓存。
- 无状态性:要求服务器端不保存客户端的状态信息,每个请求都必须包含足够的信息以便服务器理解和处理
3.SOAP
Simple Object Access Protocol,简单对象访问协议,定义了服务请求者和服务提供者之间的消息传输规范
SOAP用XML来格式化消息,用HTTP来承载消息;通过SOAP,应用程序可以在网络中进行数据交换和远程过程调用(Remote Procedure Call, RPC)
SOAP主要包含以下四个部分:
- 封装:定义了一个整体框架,用来表示消息中包含什么内容,谁来处理这些内容,以及这些内容是可选的还是必需的;
- 编码规则:定义了一种序列化的机制,用于交换系统所定义的数据类型的实例;
- RPC表示:定义了一个用来表示远程过程调用和应答的协议;
- 绑定:定义了一个使用底层传输协议来完成在节点之间交换SOAP 封装的约定。
SOAP消息基本上是从发送端到接收端的单项传输,但它们常常结合起来执行类似于请求/应答的模式;所有的SOAP消息都使用XML进行编码
WSDL与SOAP的关联:
WSDL与SOAP协议紧密相关,因为WSDL定义了如何与SOAP消息交互。WSDL中的 binding 元素定义了如何将 portType 中定义的操作映射到SOAP消息协议。
<wsdl:binding name="HelloWorldSOAP" type="ns:HelloWorld">
<soap:binding style="document" transport="***" />
<wsdl:operation name="greet">
<soap:operation soapAction="urn:greet" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
在WSDL文档中通过 <soap:address>
元素指定服务的端点地址,这是客户端实际访问服务的地方。这样,开发者就能够在WSDL的帮助下,轻松地了解如何与SOAP服务进行交互
4.UDDI
Universal Description, Discovery and Integration,通用描述、发现和集成服务
UDDI是一个基于XML的规范,用于描述和发布网络服务,并允许其他服务或应用程序发现和调用这些服务。企业可以使用它对Web services进行注册和搜索
UDDI充当了服务的目录或注册中心,在SOA环境中帮助服务提供者和服务消费者相互发现和交互。
UDDI核心功能:
- 服务注册(Registry):允许服务提供者讲服务描述信息注册到UDDI注册中心,作为其他企业或服务消费者发现服务的来源
- 服务查找(Discovery):服务消费者可以查询UDDI注册中心,寻找并获得所需的服务描述信息,从而实现对Web服务的发现
- 服务绑定(Binding):用户获得服务的描述信息后,能够通过UDDI提供的绑定信息,进行实际的服务调用
UDDI的数据结构:
- 商业实体(Business Entity) :包含企业基本信息,例如名称、地址等。
- 服务(Business Service) :描述一个商业实体提供的具体Web服务。
- 绑定模板(Binding Template) :提供服务的技术细节,如WSDL文档的位置和访问方法。
- 技术模型(Technical Model) :描述服务的技术信息,如使用协议、消息格式等。
- 发布者主张(Publisher Assertion) :用于声明两个商业实体之间的关联。
3.SOA实现方法
1.Web Service(Web服务)
三种角色:
- 服务请求者:可以使用服务或者第三方的用户,通过查询服务提供者在服务注册中心发布的服务接口的描述,通过服务接口描述、RPC 或者SOAP绑定和调用服务提供者所提供的业务或服务
- 服务提供者:作为服务管理者和创建者,必须将服务描述的接口发布到服务注册中心才能被潜在的服务请求发现,能够为合适的服务请求者提供服务
- 服务注册中心:一个目录,相当于服务接口的管理中心,服务提供者在其中注册其服务,服务请求者可以发现这些服务
Web服务模型中的操作(可以单次或反复出现):
- 发布:为了使用户能够访问服务,服务提供者需要发布服务描述,以便服务请求者可以查找它;
- 查找:在查找操作中,服务请求者直接检索服务描述或在服务注册中心查询所要求的服务类型;对服务请求者而言,可能会在生命周期的两个不同阶段中涉及查找操作,首先是在设计阶段,为了程序开发而查找服务的接口描述;其次是在运行阶段,为了调用而查找服务的位置描述;
- 绑定:在绑定操作中,服务请求者使用服务描述中的绑定细节来定位、联系并调用服务,从而在运行时与服务进行交互;绑定可以分为动态绑定和静态绑定:在动态绑定中,服务请求者通过服务注册中心查找服务描述,并动态地与服务交互;在静态绑定中,服务请求者已经与服务提供者达成默契,通过本地文件或其他方式直接与服务进行绑定。
2.ESB
Enterprise Service Bus,企业服务总线,是传统中间件技术与XML、JSON、API、WEB服务等技术相结合的产物,用于实现企业应用不同消息和信息的准确、高效和安全传递。ESB提供了网络中最基本的连接中枢,是构筑企业神经系统的必要元素
ESB提供了一种基础设施, 消除了服务请求者与服务提供者之间的直接连接,使得服务请求者与服务提供者之间进一步解藕
作用:ESB是构建SOA解决方案时所使用基础架构的关键部分,是由中间件技术实现并支持SOA的一组基础架构功能,用于实现企业应用不同消息和信息的准确、高效和安全传递
特点:
- 面向服务的元数据管理:必须了解服务的请求以及请求者对服务的要求,以及服务的提供者和他所提供的服务的描述
- 通信:服务的发布/订阅、响应/请求、同步/异步消息、路由和寻址等
- 服务交互:服务接口定义、服务实现的置换、服务消息模型,服务目录和发现等
- 服务安全:认证和授权、不可否认和机密性、安全标准的支持等
4.微服务
微服务是一种架构模式,提倡将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值
每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的Restful API):每个服务都围绕着具体业务进行构件,并且能够被独立的部署到生产环境、类生产环境等
1.微服务的特点
- 小型化和轻量级: 每个服务都相对较小,只关注特定的业务功能。
- 独立部署: 各个微服务可以独立部署,不需要依赖其他服务。
- 独立扩展: 可以根据需求独立扩展或收缩特定的服务。
- 技术多样性: 团队可以根据服务的特定需求选择最适合的技术栈,包括编程语言和数据存储等。
- 业务中心化: 每个服务都围绕一项业务能力构建,易于理解和维护。
- 敏捷性: 微服务架构提高了开发和部署的速度,使得快速迭代和持续交付成为可能。
- 容错性: 系统中某个服务的故障不会导致整个系统的崩溃,提高了系统的稳定性。
- 去中心化治理: 没有统一的控制中心,服务之间的调用和数据流是去中心化的。
- 去中心化数据管理: 每个服务可以有自己的数据库,实现数据的封装和隔离。
- 基础设施自动化: 自动化的部署、扩展、监控和故障恢复是微服务架构的关键。
微服务与SOA的区别:
微服务 | SOA |
---|---|
能拆分就拆分 | 能放一起就放一起,是一个整体 |
总想业务拆分 | 水平划分 |
单一组织负责 | 按层级划分不同部门组织负责 |
细粒度 | 粗粒度 |
一两句话可解释清楚 | SOA的目录就有几百字 |
独立的子公司 | 类似大公司中的业务单元(Business Unit,BU),也就是独立完成一件事情的小组或部门 |
小组件 | 较复杂组件 |
业务逻辑存在于每一个服务中 | 业务逻辑横跨多个业务领域 |
使用轻量级通信方式,如HTTP | ESB,充当服务之间的通信角色 |
实现方式之间的差异:
微服务 | SOA |
---|---|
团队级,自底向上开展 | 企业级,自顶向下开展 |
一个系统被拆分成多个服务,细粒度 | 服务由多个子系统组成,粗粒度 |
松散的服务架构 | ESB,集中式服务架构 |
继承方式简单 | 集成方式复杂 |
服务能独立部署 | 单块架构系统,相互依赖,部署复杂 |
数据访问层设计
Object Relational Mapping,ORM:对象与关系数据之间的映射
映射关系表
面向对象 | 关系数据库 |
---|---|
类(class) | 数据库的表(table) |
对象(object) | 记录(record,行数据) |
属性(attribute) | 字段(field) |
实现技术对比表
维度 | Hibernate | Mybatis(iBatis) |
---|---|---|
简单对比 | 强大,复杂,间接,SQL无关 | 小巧,简单,直接,SQL相关 |
可移植性 | 好(不关心具体数据库) | 差(根据数据库SQL编写) |
复杂多表关联 | 不支持 | 支持 |
云计算
优点:超大规模、虚拟化、高可靠性、高可伸缩性、按需服务、成本低【前期投入低、综合使用成本也低】
分类
1.按服务类型分类
SaaS:软件即服务,基于多租户实现,直接提供应用程序
PaaS:平台即服务,虚拟中间件服务器、运行环境和操作系统
IaaS:基础设施及服务,包括服务器、存储和网络等服务
2.按部署方式分类
公有云:面向互联网用户需求,通过开放网络提供云计算服务
私有云:面向企业内部提供云计算服务
混合云:兼顾以上两种情况的云计算服务
云计算架构
管理层:提供对所有层次云计算服务的管理功能
用户访问层:方便用户使用云计算服务所需的各种支撑服务,针对每个层次的云计算服务都需要提供相应的访问接口
应用层:提供软件服务,如:财务管理,客户关系管理,商业智能
平台层:为用户提供对资源层服务的封装,使用户可以构建自己的应用
资源层:提供虚拟化的资源,从而隐藏物理资源的复杂性。如:服务器,存储
云原生架构
是基于分布部署和统一运营的分布式云,以容器、微服务、DevOps等技术为基础建立的一套云技术产品体系
Oncloud—>Incloud:土生土长的云技术
云原生架构设计原则
服务化原则:使用微服务
弹性原则:可根据业务变化自动伸缩
可观测原则:通过日志、链路跟踪和度量
韧性原则:面对异常的抵御能力(可用性)
所有过程自动化原则:自动化交付工具
零信任原则:默认不信任网络内部和外部的任何人/设备/系统
架构持续演进原则:业务高速迭代情况下的架构与业务平衡
云原生架构模式
服务架构模式:典型代表—微服务,服务拆分使维护压力大增
Mesh化架构模式:把中间件框架(RPC、缓存、异步消息)从业务进程中分离,由Mesh进程完成
Serverless:非常适合于事件驱动的数据计算任务
存储计算分离模式:各类暂态数据(如session)用云服务保存
分布式事务模式:解决微服务模式中多数据源事务问题
可观测架构:包括Loggion、Tracing、Metrics三个方面
事件驱动架构:本质上是一种应用/组件间的继承架构模式
MDA
Model Driven Architecture,MDA
MDA起源于分离系统规约和平台实现的思想
MDA三种核心模型:
- 平台独立模型(PIM):具有高抽象层次、独立于任何实现技术的模型
- 平台相关模型(PSM):为某种特定实现技术量身定做,让你用这种技术中可用的实现构造来描述系统的模型。PIM会被变换成一个或多个PSM
- 代码Code:永远代码对系统的描述(规约)。每个PSM都将被变换成代码
软件架构模式
DSSA(知识点比较固定,属于送分)
领域驱动设计
Domain-Driven Design,DDD,领域驱动设计
领域模型是一种特殊的业务模型,它分析范围是整个行业,抽象出行业里共性和内在规律性的业务,比业务模型更加抽象,它不属于软件开发范畴的概念,与软件开发无关
是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,设计团队、开发团队和领域专家一起通过“通用语言(Ubiquitous Language)”去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。
DSSA
Domain Specific Software Architecture,特定领域软件架构,以一个特定问题领域为对象,形成由领域参考模型、参考需求、参考架构等组成的开发基础架构,支持一个特定领域中多个应用的生成
DSSA就是专用于一类特定类型的任务(领域)的、在整个领域中能有效地使用的、为成功构造应用系统限定了标准的组合结构的软件构件的集合。
DSSA就是一个特定的问题领域中支持一组应用的领域模型、参考需求、参考架构等组成的开发基础,其目标就是支持在一个特定领域中多个应用的生成。
- 垂直域:在一个特定领域中的通用的软件架构,是一个完整的架构。
- 水平域:在多个不同的特定领域之间的相同的部分的小工具(如购物和教育都有收费系统,收费系统即是水平域)。
DSSA的三个基本活动:
-
**领域分析:**这个阶段的主要目标是获得领域模型(领域需求)。识别信息源,即整个领域工程过程中信息的来源,可能的信息源包括现存系统、技术文献、问题域和系统开发的专家、用户调查和市场分析、领域演化的历史记录等,在此基础上就可以分析领域中系统的需求,确定哪些需求是领域中的系统广泛共享的,从而建立领域模型
- 领域模型(Domain Model),又称概念模型、领域对象模型,是完成从需求分析到面向对象设计的一座桥梁,领域模型是指对需求所设计的领域的建模,是描述业务用力实现的对象模型
-
**领域设计:**这个阶段的目标是获得DSSA。DSSA描述在领域模型中表示的需求的解决方案,它不是单个系统的表示,而是能够适应领域中多个系统的需求的一个高层次的设计。建立了领域模型之后,就可以派生出满足这些被建模的领域需求DSSA。
- DSSA并不是直接描述一个具体的系统或应用程序,而是描述了解决方案,这些解决方案对应于通过领域模型所识别和定义的需求。也就是说,DSSA是基于领域模型的需求来进行设计的
- DSSA提供了一种通用的设计模式或框架,使得不同系统可以共享某些设计原则和结构,从而更高效地开发和维护。
-
**领域实现:**这个阶段的主要目标是依据领域模型和DSSA开发和组织可重用信息。这些可重用信息可能是从现有系统中提取得到,也可能需要通过新的开发得到。
- 可重用信息包括但不限于:代码库、服务接口、数据访问对象、时间模型、微服务等可重复使用的构件、服务、代码片段或其他资源
参与DSSA的四种角色人员(重要)
**领域专家:**包括该领域中系统的有经验的用户、从事该领域中系统的需求分析、设计、实现以及项目管理的有经验的软件工程师等。提供关于领域中系统的需求规约和实现的知识,帮助组织规范的、一致的领域字典,帮助选择样本系统作为领域工程的依据,复审领域模型、DSSA等领域工程产品,等等
**领域分析人员:**由具有知识工程背景的有经验的系统分析员来担任。控制整个领域分析过程,进行知识获取,将获取的知识组织到领域模型中
**领域设计人员:**由有经验的软件设计人员来担任。根据领域模型和现有系统开发出DSSA,并对DSSA的准确性和一致性进行验证
**领域实现人员:**由有经验的程序设计人员来担任。根据领域模型和DSSA,开发构件
建立DSSA的过程(几个过程背一下):
-
**定义领域范围:**领域中的应用要满足用户一系列的需求。
-
**定义领域特定的元素:**建立领域的字典,归纳领域中的术语,识别出领域中相同和不相同的元素。
-
**定义领域特定的设计和实现需求的约束:**识别领域中的所有约束,这些约束对领域的设计和实现会造成什么后果。
-
**定义领域模型和架构:**产生一般的架构,并描述其构件说明。
-
**产生、搜集可复用的产品单元:**为DSSA增加复用构件,使可用于新的系统。
以上过程是并发的、递归的、反复的、螺旋型的。
三层次模型:
**领域开发环境:**领域架构师决定核心架构,产出参考结构、参考需求、架构、领域模型、开发工具
**领域特定的应用开发环境:**应用工程师根据具体环境来将核心架构实例化
**应用执行环境:**操作员实现实例化后的架构
人员与职责对应关系要掌握
软件架构评估(重要)
软件系统的质量属性分为开发期质量属性和运行期质量属性
为什么要进行架构评估?
1.软件架构的质量属性
系统的质量属性 | 定义 | 架构层次的问题 | 子特性 | 架构策略 | |
---|---|---|---|---|---|
性能 | 系统的响应能力 | 如何提高组件间的通信性能 | 响应时间、吞吐量 | 优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度,动态优先级 | |
可靠性 | 在意外或错误使用的情况下维持软件系统的功能特性的基本能力 | 如何通过使用冗余组件实现容错 | MTTF、MTBF、MTTR | 心跳、Ping/Echo、冗余、选举 | |
可用性 | 能够正常运行的时间比例 | 如何保证专用组件的可用性 | 故障间隔时间 | 心跳、Ping/Echo、冗余、选举 | |
易用性 | 关注软件产品的使用效率、易学性、用户满意度等方面。一个具有高易用性的软件,能够让用户在最短的时间内,以最小的努力完成任务,并取得满意的结果 | 如何保障组件和架构的易用性问题 | 界面友好,新用户学习系统时间不超过2小时 | ||
安全性 | 系统在向合法用户提供服务的同时能够阻止非授权用户使用的企图或拒绝服务的能力 | 如何保障组件的安全交互 | 保密性、完整性(防止信息被篡改)、不可抵赖性、可控性(对信息的传播及内容具有控制的能力) | 身份验证、用户授权、入侵检测、用户认证、用户授权、追踪审计 | |
可修改性 | 以较高的性价比对系统进行变更的能力 | 组件和架构的可更改行如何得到保障 | 可维护性、可扩展性、结构重组、可移植性 | 接口-实现分类、抽象、信息隐藏 | 更改系统报表模块,必须在2人周内完成 |
可测试性 | 通过测试揭示软件缺陷的容易程度 | 组件和连接件的测试难度如何,测试环境的配置是否比较困难等 | 远程调试接口 | ||
功能性 | 系统所能完成所期望的工作的能力 | ||||
可变性 | 体系结构经扩充或变更而成为新体系结构的能力 |
- 注意,可用性与可靠性定义类似,考试中有可用性优先选可用性
心跳:有一个中心节点来存储集群元数据和管理子节点,中心节点采用主备模式来实现HA。当中心节点主故障后,备节点接管业务成为主节点。
信息隐藏:get(),set()
质量属性场景
面向特定质量属性的需求
场景要素 | 英文 | 定义 | 可能的情况 |
---|---|---|---|
刺激源 | Source | 生成该刺激的实体 | 最终用户、开发人员、系统管理员 |
刺激 | Stimulus | 当刺激到达系统时需要考虑的条件 | 希望增加、删除、修改、改变功能、质量属性、容量等 |
环境 | Environment | 该刺激在某些条件内发生 | 系统设计时、编译时、构件时、运行时 |
制品 | Arifact | 某个制品被激励 | 系统用户界面、平台、环境或目标系统交互的系统 |
响应 | Response | 该响应是在激励到达后所采取的行动 | 查找架构中需要修改的位置,进行修改且不会影响其他功能,对所做的修改进行测试,部署所做的修改 |
响应度量 | Measurement | 当响应发生时,应当能够以某种方式对其进行度量,以对需求进行测试 | 根据所影响元素的数量度量的成本、努力、资金;该修改对其他功能或质量属性所造成影响的程度 |
2.架构评估重要方面
敏感点:是指为了实现某种特定的质量属性,一个或多个构件所具有的特性(能够给系统带来重大影响的质量属性)
权衡点:是影响多个质量属性的特性,权衡点属于敏感点
风险点:风险点与非风险点不是以标准专业术语形式出现的,只是一个常规概念,即可能引起风险的因素,可称为风险点。某个做法如果有隐患,有可能导致一些问题,则为风险点;而如果某件事是可行的可接受的,则为非风险点
软件架构评估在架构设计之后,系统设计之前,因此与设计、实现、测试都没有关系。评估的目的是为了评估所采用的架构是否能解决软件系统需求,但不是单纯的确定是否满足需求
解题技巧:
- 影响。。。一个质量属性,选敏感点
- 出现多个质量属性,选权衡点(一好一坏)
- 影响。。。,选风险点
3.架构评估的三种方式
- 基于调查问卷(检查表)的方式:类似于需求获取中的问卷调查方式,只不过是架构方面的问卷,要求评估人员对领域熟悉。
- 基于度量的方式:制定一些定量指标来度量架构,如代码行数等。要制定质量属性和度量结果之间的映射,要求评估人员对架构熟悉。
- 基于场景的方式:主要方法。首先要确定应用领域的功能和软件架构的结构之间的映射,然后要设计用于体现待评估质量属性的场景(即4+1视图中的场景),最后分析软件架构对场景的支持程度。要求评估人员即对领域熟悉,也对架构熟悉。从三个方面对场景进行设计:刺激(事件);环境(事件发生的环境);响应(架构响应刺激的过程)。
基于场景的评估方式,主要由3种方式:软件架构分析法(SAAM),架构权衡分析方法(ATAM),成本效益分析法(CBAM)
1.SAAM
SAAM是一种非功能质量属性的架构分析方法,是最早形成文档并得到广泛应用的软件架构分析方法。
- 特定目标:SAAM的目标是对描述应用程序属性的文档,验证基本的架构假设和原则。
- 质量属性:这一方法的基本特点是把任何形式的质量属性都具体化为场景,但可修改性是SAAM分析的主要质量属性。
- 架构描述:SAAM用于架构的最后版本,但早于详细设计。架构的描述形式应当被所有参与者理解。
- 功能、结构和分配被定义为描述架构的3个主要方面。
- 方法活动:SAAM的主要输入是问题描述、需求声明和架构描述。下图描绘了SAAM分析活动的相关输入及评估过程。包括5个步骤,即场景开发、架构描述、单个场景评估、场景交互和总体评估
2.ATAM
ATAM在SAAM基础上发展而来。主要针对性能、可用性、安全性、可修改性的性能指标。找出一个平衡点,对这些质量属性进行评价和折中
让架构师明确如何权衡多个质量目标,参与者有评估小组、项目决策者和其他项目相关人
ATAM被分为四个主要的活动领域,分别是场景和需求收集、体系结构视图和场景实现、属性模型构造和分析、折中。整个评估过程强调以属性作为架构评估的核心概念。
首先要进行场景和需求的收集,之后用架构设计过程中的架构描述来解决场景中的要求。第三阶段就是分析架构中的每个单一质量属性的支持程度;第四阶段进行分析哪些质量属性之间存在冲突,需要进行折中。通常情况会根据需求来定义场景的优先级,就是当发生冲突时分析哪一个场景的实现对系统更为重要。
软件复用
软件复用【重用】是多次不同的软件开发过程中重复使用相同或相似【软件元素】(需求分析文档、设计过程、设计文档、程序代码、测试用例、领域知识)的过程
复用的历史发展线路:
复用的维度:
- 水平复用:不分行业领域,通用
- 垂直复用:分行业领域,专用
构件
构件是一个独立可交付的功能单元,外界通过接口访问其提供的服务。
1.基本概念
发展历程:
构件由一组通常需要同时部署的原子构件组成。
一个原子构件是一个模块和一组资源。原子构件是部署、版本控制和替换的基本单位。原子构件通常成组地部署,但是它也能够被单独部署。
-
原子构件(Atomic Components):原子构件是构件的基本单元,这些基本单元不能再进一步拆分为更小的部署单元。原子构件本身可能是简单的功能模块,比如一个服务、一个库或者一个特定的数据处理单元。
-
通常需要同时部署:当一个构件由多个原子构件组成时,这些原子构件应当作为一个整体来部署。这意味着这些原子构件不能单独部署或独立运行,它们必须一起被部署才能形成完整的功能。
-
模块 (Module):
- 模块指的是执行某一特定任务的数据结构和程序代码。模块可以是一个类、一组函数或一个服务,它通常是逻辑上相关联的代码集合。
- 将模块的接口和功能定义为其外部特性
- 将模块的局部数据和实现该模块的程序代码称为内部特性
- 模块通常具有内部高内聚性,即模块内部的各个部分紧密相关;同时,模块之间保持低耦合度,即不同模块之间的依赖关系较少。
- 在模块设计时,最重要的原则就是实现信息隐蔽和模块独立
- 模块指的是执行某一特定任务的数据结构和程序代码。模块可以是一个类、一组函数或一个服务,它通常是逻辑上相关联的代码集合。
-
资源 (Resources):
- 资源是指模块运行所需要的非代码文件,例如配置文件、数据文件、图像文件等。
- 这些资源对于模块的正常运行至关重要,没有它们,模块可能无法正常工作或达到预期的效果。
# date_handler.py 模块
import datetime
from config import DATE_FORMAT
def get_current_date():
""" 获取当前日期 """
return datetime.datetime.now().date()
def format_date(date, format_str=DATE_FORMAT):
""" 格式化日期 """
return date.strftime(format_str)
# config.py 配置文件(资源)
DATE_FORMAT = "%Y-%m-%d"
# 日期处理模块(原子构件)
from date_handler import get_current_date, format_date
current_date = get_current_date()
formatted_date = format_date(current_date)
print("Today's date is:", formatted_date)
构件和原子构件之间的区别在于,大多数原子构件永远都不会被单独部署,尽管它们可以被单独部署。相反,大多数原子构件都属于一个构件家族,一次部署往往涉及整个家族。
一个模块是不带单独资源的原子构件,模块是构建原子构件的基础构件块。
一个单独的包被编译成多个单独的类文件——每个公共类都有一个。
模块是一组类和可能的非面向对象的结构体,比如过程或者函数。
一个构件可以包含多个类元素,但是一个类元素只能属于一个构件。
即:构件 > 原子构件 > 模块 > 类
构件的特性:
- 独立部署单元
- 作为第三方的组装单元(可以由不同的开发者创建,可以在不同的项目之间共享和复用)
- 没有(外部的)可见状态(构件不保留任何外部可以直接访问的状态信息,仅能依靠特定的接口)
对象的特性:
- 一个实例单元,具有唯一的标志
- 可能具有状态,此状态外部可见
- 封装自己的状态和行为
1.信息屏蔽
将每个程序的成分隐蔽或封装在一个单一的设计模块中,并且尽可能少地暴露其内部的处理过程
信息屏蔽可以提高软件的可修改性、可测试性和可移植性
2.模块独立
模块独立是指每个模块完成一个相对独立的特定子功能,并且与其他模块之间的联系最简单,通常用耦合和内聚两个标准来衡量
3.深度
表示软件结构中控制的层数,它往往能粗略地标志一个系统的大小和复杂程度。如果层数过多则应该考虑是否有许多管理模块过分简单,能否适当合并
4.宽度
是软件结构内同一层次上模块总数的最大值。宽度越大系统越复杂。对宽度影响最大的因素是模块的扇出
5.扇出
模块的扇出是指一个模块直接控制(调用)的下次模块数目。扇出过大意味着模块过分复杂,需要控制和协调过多的下级模块;扇出过小也不好。设计得好得系统平均扇出是3或4
6.扇入
是指由多少个上级模块调用它,扇入越大则共享该模块的上级模块数目越多
7.模块分解时需要注意
- 保持模块的大小适度
- 尽可能减少调用的深度
- 直接调用该模块的次数应该尽可能多,但调用其他模块的次数则不宜过多(扇入大,扇出小)。好的软件设计结构顶层高扇出,中间扇出少,底层高扇入
- 保证模块是单入口,单出口的
- 模块的作用域应该在模块之内
- 功能应该是可预测的
2.构件开发模型
Component Develop Model,也可翻译为组件开发模型。
软件构件是软件系统中具有一定意义的、相对独立的可重用单元。与对象相比,构件可以基于对象实现,也可以不作为对象实现。构件需要在容器中管理并获取容器提供的服务;客户程序可以在运行状态下利用接口动态确定构件所支持的功能并调用。
构件是软件部署过程中的最小单元,如jar包、dll文件、gem文件(Ruby)等,外界通过接口访问其提供的服务
软件构件是部署、版本控制和替换的基本单位。
构件是一组通常需要同时部署的原子构件。原子构件通常成组地部署,但是它也能够被单独部署。构件与原子构件的区别在于,大多数原子构件永远都不会被单独部署,尽管它们可以被单独部署。大多数原子构件都属于一个构件家族,一次部署往往涉及整个家族。一个模块是不带单独资源的原子构件。
构件技术就是利用某种编程手段,将一些人们所关心的,但又不便于让最终用户去直接操作的细节进行封装,同时对各种业务逻辑规则进行实现,用于处理用户的内部操作细节。构件是可复用的软件组成成份,可被用来构造其他软件。它可以是被封装的对象类、类树、一些功能软件工程中的构件模块、软件框架、软件构架(或体系结构)、文档、分析件、设计模式等。
系统构件组装分为三个不同的层次:定制(Customization)、集成(Integration)、扩展(Extension)。这三个层次对应于构件组装过程中的不同任务。
一个构件可以包含多个类元素,但一个类元素只能属于一个构件。构件的部署必须能跟它所在的环境及其他构件完全分离;构件作为一个部署单元是不可拆分的;在一个特定进程中只能存在一个特定构件的拷贝;对于不影响构件功能的某些属性可以对外部可见。
基于可重用构件的开发模型利用模块化方法将整个系统模块化,并在一定构件模型的支持下复用构件库中的一个或多个软件构件,通过组合手段髙效率、髙质量地构造应用软件系统的过程。基于构件的开发模型融合螺旋模型的许多特征,本质上是演化形的,开发过程是迭代的。
基于构件的开发模型由软件的需求分析定义、体系结构设计、构件库建立、应用软件构建、测试和发布5个阶段组成。
在基于构件的软件开发中:
- 逻辑构件模型:用功能包描述系统的抽象设计,用接口描述每个服务集合,以及功能之间如何交互以满足用户需求,它作为系统的设计蓝图以保证系统提供适当的功能
物理构件模型:用技术设施产品、硬件分布和拓扑结构,以及用于绑定的网络和通信协议描述系统的物理设计,这种架构用于了解系统的性能、吞吐率等许多非功能性属性
在仓库风格中,有两种不同的构件:
- 中央数据结构说明当前状态
- 独立构件在中央数据存储上执行
失配:指在软件复用的过程中,由于待复用构件对最终系统的体系结构和环境的假设(assumption)与实际状况不同而导致的冲突。在构件组装阶段失配问题主要包括:
由构件引起的失配,包括由于系统对构件基础设施、构件控制模型和构件数据模型的假设存在冲突引起的失配;
由连接子引起的失配,包括由于系统对构件交互协议、连接子数据模型的假设存在冲突引起的失配;
由于系统成分对全局体系结构的假设存在冲突引起的失配等。
要解决失配问题,首先需要检测出失配问题,并在此基础上通过适当的手段消除检测出的失配问题。
原子构件是部署、版本控制和替换的基本单位。原子构件通常成组地部署,但是它也能够被单独部署。
3.构件接口
接口标准化是对接口中消息的格式、模式和协议的标准化。它不是要将接口格式化为参数化操作的集合,而是关注输入输出的消息的标准化,它强调当机器在网络中互连时,标准的消息模式、格式、协议的重要性。
接口定义的作用主要有以下几点:
- 确保构件之间的正常通信:接口定义规定了构件之间的通信方式,确保了构件之间能够正确地发送和接收信息,保证系统的正常运行
- 提高系统的可维护性:通过明确定义接口,可以降低构件之间的耦合度,使得系统的各个构件可以独立开发和维护,提高了系统的可维护性
- 促进系统的扩展性:接口定义使得系统的各个构件之间解耦合,当需要对系统进行扩展时,只需要修改相应的接口定义,而不需要修改其他构件的代码,提高了系统的扩展性
接口定义的要素:
- 输入输出接口:输入接口定义了构件接收外部数据的方式和格式,输出接口定义了构件向外部输出数据的方式和格式。输入和输出接口的定义需要明确指定数据的类型、格式和传输方式,以确保构件之间能够正确地进行数据交换
- 数据接口:数据接口定义了构件之间传递数据的方式和格式。数据接口规定了数据的结构、字段和传递方式,确保构件之间能够正确地传递和解析数据
- 控制接口:控制接口定义了构件之间的控制流程和协调工作。控制接口规定了构件之间的调用关系、调用方式和调用参数,确保构件之间能够正确地进行协作和控制
- 界面接口:界面接口定义了构件与用户或其他系统之间的交互方式和规范。界面接口规定了用户输入的格式、界面的显示方式和交互方式,确保构件能够与用户或其他系统进行有效的交互。
4.面向构件的编程
Component-Oriented Programming, COP,关注于如何支持建立面向构件的解决方案。
基于一般OOP风格,面向构件的编程需要下列基本的支持:
- 多态性(可替代性)
- 模块封装性(高层次信息的隐藏)
- 后期的绑定和装载(部署独立性)
- 安全性(类型和模块安全性)
面向构件的编程仍然缺乏完善的方法学支持。现有的方法学只关注于单个构件本身,并没有充分考虑由于构件的复杂交互而带来的诸多困难,其中的一些问题可以在编程语言和编程方法的层次上进行解决。
构件技术就是利用某种编程手段,将一些人们所关心的,但又不便于让最终用户去直接操作的细节进行了封装,同时对各种业务逻辑规则进行了实现,用于处理用户的内部操作细节。
目前,国际上常用的构件标准主要有三大流派:(记住哪个标准是哪个公司就行)
◆ EJB(EnterpriseJava Bean)规范由Sun公司制定,有三种类型的EJB,分别是会话Bean(Session Bean)、实体Bean(Entity Bean)和消息驱动Bean(Message-driven Bean)。EJB实现应用中关键的业务逻辑,创建基于构件的企业级应用程序。(重要)
◆COM、DCOM、COM+:COM是微软公司的。DCOM是COM的进一步扩展,具有位置独立性和语言无关性。COM+并不是COM的新版本,是COM的新发展或是更高层次的应用。
◆CORBA标准主要分为三个层次:对象请求代理、公共 对象服务和公共设施。最底层是对象请求代理ORB,规定了分布对象的定义(接口)和语言映射,实现对象间的通讯和互操作,是分布对象系统中的“软总线”;在ORB之上定义了很多公共服务,可以提供诸如并发服务、名字服务、事务(交易)服务、安全服务等各种各样的服务;
最上层的公共设施则定义了组件框架,提供可直接为业务对象使用的服务,规
定业务对象有效协作所需的协定规则。
5.构件聚合原则
一般来说,一个软件项目的重心会从三角区域的右侧开始,前期主要牺牲的是复用性。然后,随着项目逐渐成熟,其他项目会逐渐开始对其产生依赖,项目重心就会逐渐向该三角区域的左侧滑动。
1.REP(复用/发布等同原则)
软件复用的最小粒度应等同于其发布的最小粒度
构件中的类与模块必须是彼此紧密相关的
2.CCP(共同闭包原则)
将由于相同原因而修改,并且需要同时修改的东西放在一起。将由于不同原因而修改,并且不同时修改的东西分开
3.CRP(共同复用原则)
不要依赖不需要用到的东西
ISP原则的一个普适版
6.构件耦合原则
1.无依赖环原则
组件依赖关系图中不应该出现环
2.稳定依赖原则
依赖关系必须要指向更稳定的方向
3.SAP(稳定抽象原则)
一个组件的抽象化程度应该与其稳定性保持一致
如果一个组件想要成为稳定组件,那么它就应该由接口和抽象类组成,以便将来做扩展
衡量抽象化程度:
- N C N_C NC:组件中类的数量
- N a N_a Na:组件中抽象类和抽象接口的数量
- A A A:抽象程度, A = N a / N c A=N_a/N_c A=Na/Nc
构件的复用
这块内容可以用在论文
- 检索与提取构件
- 基于关键字的检索:树形或有向无回路图
- 刻面检索法:
- 超文本检索法:按照人类的联想思维方式任意跳转到包含相关概念或构件的文档
- 理解与评价构件
- 要复用构件,准确地理解构件至关重要。特别是对构件修改使用时
- 为达到目的,必须要求构件的开发过程遵循公共标准
- 一般构件库的文档中全面而准确地说明内容:构件的功能与行为、相关的领域知识、可适应性约束条件与例外情形、可以预见的修改部分及修改方法
- 修改构件
- 理想状态是直接复用构件库中现成的构件,但大多数情况下,必须对构件进行或多或少的修改,以应对新需求
- 为了减少构件修改的工作量,要求开发人员尽量使构件的功能、行为和接口设计更为抽象化、通用化和参数化。这样,复用者即可通过对实参的选取来调整构件的功能或行为。如果这种调整仍不足以使构件适用于新系统,复用者就必须借助涉及信息和文档来修改构件
- 构件库若无可修改使用的构件,则按新需求开发构件,并存入构件库
- 组装构件
- 组装的方式
- 基于功能的组装:采用子程序调用和参数传递的方式将构件组装起来
- 基于数据的组装:仍然是传统的子程序调用与参数传递。但它所以来的软件设计方法不再是功能分解,而是面向数据的设计方法,例如,Jackson系统开发方法
- 面向对象的组装:如果从类库中检索出来的基类能够完全满足新系统的需求,则可以直接应用。否则,必须以基类为父类,生成相应的子类,以满足新系统的需求
- 构件组装失配问题
- 有构件引起的失配,包括由于系统对构件基础设施、构件控制模型和构件数据模型的假设存在冲突引起的失配
- 有连接子引起的失配,包括由于系统对构件交互协议、连接子数据模型的假设存在冲突引起的失配
- 由于系统成分对全局体系结构的假设存在冲突引起的失配等。要解决失配问题,首先需要检测
- 组装的方式
构件的三大标准
CORBA
伺服对象(Servant):CORBA对象的真正实现, 负责完成客户端请求
对象适配器(Obejct Adapter):用于屏蔽ORB内核的实现细节,为服务器对象的实现者提供抽象接口,一边他们使用ORBA内部的某些功能
对象请求代理(Object Request Broker):解释调用并负责查找实现该请求的对象,将参数传给找到的对象,并调用方法返回结果。客户方不需要了解服务对象的位置、通信方式、实现、激活或存储机制
J2EE
会话Bean:实现业务逻辑,负责完成服务端与客户端的交互
实体Bean:实现O/R映射,简化数据库开发工作
消息驱动Bean:处理并发与异常访问
构件组装模型
优点:易重用、易扩展、降低成本、安排任务更灵活
缺点:构件设计要求经验丰富的架构师、设计不好的构件难重用、强调重用可能牺牲其他指标(如性能)、第三方构件质量难控制
举例:
-
方舱医院
-
乐高积木
快速应用开发模型(RAD)
中间件
在一个分布式系统环境中处于操作系统和应用程序之间的软件,可以在不同的技术之间共享资源,将不同的操作系统、数据库、异构的网络环境以及若干应用结合成一个有机的协同工作整体
1.基本概念
中间件位于客户机/服务器的操作系统之上,管理计算机资源和网络通信,有如下特点
-
中间件是一类系统软件/一类构件,而非一种软件
-
中间件不仅仅实现互连,还要实现应用之间的互操作
- 实现互连:这指的是中间件的基本功能,即它使得不同的应用程序能够通过网络进行通信。即使这些应用程序可能运行在不同的操作系统上,使用不同的编程语言编写,或者驻留在不同的硬件平台上,中间件也提供了必要的桥梁,让它们可以互相“交谈”。
- 实现应用之间的互操作:这更进一步地指出了中间件的一个高级目标。不仅仅是连接不同的应用,中间件还应该允许这些应用在逻辑上协同工作。这意味着应用程序不仅需要能够发送和接收消息,还需要理解彼此的数据格式、业务逻辑以及可能的操作流程。例如,一个应用程序可能需要调用另一个应用程序的服务,或者两个应用程序可能需要同步其数据库记录。
-
中间件是基于分布式处理的软件,最突出的特点是其网络通信功能
中间件的任务是使应用程序开发变得更容易,通过提供统一的程序抽象,隐藏异构系统和分布式系统下低级别编程的复杂度
中间件可以处理一些常见的任务,如日志记录、请求路由、缓存、安全性、压缩、身份验证、API管理等
中间件技术的优点:
- 面向需求:设计师集中精力于业务逻辑本身
- 业务的分隔和包容性
- 设计与实现隔离
- 隔离复杂的系统资源
- 符合标准的交互模型:中间件实现了架构的模型,实现了标准的协议
- 软件复用
- 提供对应用构件的管理:基于中间件的软件可以方便地进行管理,因为构件总可以通过表示机制进行划分
2.中间件的分类
- 数据库访问中间件:通过一个抽象层访问数据库,从而允许使用相同或相似的代码访问不同的数据库资源。典型的技术如Windows平台的ODBC和Java平台的JDBC等。这些中间件可以帮助开发者在不需要详细了解数据库内部实现的情况下,进行数据库的查询、更新和管理操作,大大提高了开发效率
- 远程过程调用(RPC):是一种广泛使用的分布式应用程序处理方法。一个应用程序使用RPC来“远程”执行一个位于不同地址空间内的过程,从效果上看和执行本地调用相同
- 消息中间件(MOM):利用高效可靠的消息传递机制进行平台无关的数据交流,并可基于数据通信进行分布系统的集成。通过提供消息传递和消息排队模型,可在分布环境下扩展进程间的通信,并支持多种通信协议、语言、应用程序、硬件和软件平台。典型的产品如IBM的MQSeries
- 分布式对象中间件:随着对象技术与分布式计算技术的发展,两者相互结合形成了分布式对象技术,并发展成为当今软件技术的主流方向。典型的产品如OMG的CORBA、Sun的RMI/EJB、Microsoft的DCOM等
- 事务处理中间件:也称事务处理监控器(TPM)最早出现在大型机上。事务处理监控程序位于客户和服务器之间,完成事务管理与协调、负载平衡、失效恢复等任务,提高系统的整体性能
- Web服务器中间件:一种用于接收和处理HTTP请求的软件,通常用于提供Web服务。常见的Web服务器中间件:Apach、Nginx。
3.构件三大标准
CORBA:
- 伺服对象(Servant):CORBA对象的真正实现,负责完成客户端请求
- 对象适配器(Object Adapter):用于屏蔽ORB内核的实现细节,为服务器对象的实现者提供抽象接口,以便他们使用ORB内部的某些功能
- 对象请求代理(Object Request Broker):解释器调用并负责查找实现该请求的对象,将参数传给找到的对象,并调用方法返回结果。客户方不需要了解服务对象的位置、通信方式、实现、激活或存储机制
J2EE(EJB):
- 会话Bean:实现业务逻辑,负责完成服务端与客户端的交互
- 实体Bean:实现O/R映射,简化数据库开发工作
- 消息驱动Bean:处理并发与异常访问
大型网站系统架构演化
维度 | 涉及技术内容 |
---|---|
架构 | MVC、MVP、MVVM、REST、Web service、微服务 |
并发分流 | 集群(负载均衡)、CDN |
缓存 | MemCache、Redis、Squid |
数据 | 主从复制、内存数据库、反规范化技术、NoSQL、分区(分表)技术、视图与物化视图 |
持久化 | Hibernate、Mybatis |
分布存储 | Hadoop、FastDFS、区块链 |
数据编码 | XML、JSON |
Web应用服务器 | Apache、WebSphere、WebLogic、Tomcat、JBOSS、IIS |
安全性 | SQL注入攻击 |
其他 | 静态化、有状态与无状态、响应式Web设计、中台 |
缓存机制
1.MemCache
是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负载。Memcache通过在内存里维护一个统一的巨大的hash表,能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果
2.Redis
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
1.Redis分布式存储方案:
分布式存储方案 | 英文 | 核心特点 |
---|---|---|
主从模式 | Master/Slave | 一主多从,故障时手动切换 |
哨兵模式 | Sentinel | 有哨兵的一主多从,主节点故障自动选择新的主节点 |
集群模式 | Cluster | 分节点对等集群,分slots,不同slots的信息存储到不同节点 |
- 单机模式:
- 主从模式
- 哨兵模式
- 集群模式
2.集群切片方式
切片集群(分片集群),就是指启动多个Redis实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存
集群切片方式 | 核心特点 |
---|---|
客户端切片 | 在客户端通过key的hash值对应到不同的服务器 |
中间件实现分片 | 在应用软件和Redis中间,例如:Twemproxy、Codis等,由中间件实现服务到后台Redis节点的路由分派 |
客户端服务端协作分片 | Redis Cluster模式,客户端可采用一致性哈希,服务端提供错误节点的重定向服务slot,不同的slot对应到不同服务器 |
【趣话Redis:Redis集群是如何工作的?】https://www.bilibili.com/video/BV1ge411L7Sh?vd_source=d4e0984d2cdfaa74a54df60647de15e0
Redis的数据类型(考试会考应用场景):
关键字 | 类型 | 特点 | 示例 |
---|---|---|---|
String | 字符串 | 存储二进制,任何类型数据,最大512MB | 缓存,计数,共享Session |
Hash | 字典 | 无序字典,数组+链表,适合存对象Key对应一个HashMap。针对一组数据 | 存储、读取、修改用户属性 |
List | 列表 | Linked List:双向链表,有序,删增快,查询慢;Array List:数组方式,有序,删增慢,查询快 | 消息队列,文章列表,记录前N个最新登录的用户ID列表 |
Set | 集合 | 键值对无序,唯一,增删查复杂度均为O(1),支持交/并/差集操作 | 独立IP,共同爱好,标签 |
Sorted Set【Zset】 | 有序集合 | 键值对有序,唯一,自带按权重排序效果 | 排行榜 |
Redis数据淘汰算法(记住)
机制名 | 淘汰作用范围 | 策略 |
---|---|---|
noevication | 不淘汰 | 禁止驱逐数据,内存不足以容纳新入数据时,新写入操作就会报错。系统默认的一种淘汰策略 |
volatile-random | 设置了过期时间的键空间 | 随机移除某个key |
volatile-lru | 设置了过期时间的键空间 | 优先移除最近未使用的key |
volatile-ttl | 设置了过期时间的键空间 | ttl值最小的key优先移除 |
allkeys-random | 全键空间 | 随机移除某个key |
allkeys-lru | 全键空间 | 优先移除最近未使用的key |
Redis的持久化:
RDB:传统数据库中快照的思想,指定时间间隔将数据进行快照存储
AOF:传统数据库中日志的思想,把每条改变数据集的命令追加到AOF文件末尾,这样出问题了,可以重新执行AOF文件中的命令来重建数据集
对比维度 | RDB持久化 | AOF持久化 |
---|---|---|
备份量 | 重量级的全量备份,保存整个数据库 | 轻量级增量备份,一次只保存一个修改命令 |
保存间隔时间 | 保存间隔时间长 | 保存间隔时间短,默认1秒 |
还原速度 | 数据还原速度快 | 数据还原速度慢 |
阻塞情况 | save会阻塞,但bgsave或者自动不会阻塞 | 无论是平时还是AOF重写,都不会阻塞 |
数据体积 | 同等数据体积:小 | 同等数据体积:大 |
安全性 | 数据安全性:低,容易丢数据 | 数据安全性:高 |
Redis常见问题:
- 缓存雪崩
解决方案:
- 使用锁或队列:保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上
- 为key设置不同的缓存失效时间:在固定的一个缓存时间的基础上+随机一个事件作为缓存失效时间
- 二级缓存:设置一个有时间限制的缓存+一个无时间限制的缓存。避免大规模访问数据库
缓存传统问题:
查询无数据返回->直接查数据库
解决方案:
- 如果查询结果为空,直接设置一个默认值存放到缓存,这样第二次到缓存中获取就有值了
- 设置布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免对底层存储系统的查询压力
缓存预热(了解)
系统上线后,将相关需要缓存数据直接加到缓存系统中
解决方案:
- 直接写个缓存刷新页面,上线时手工操作
- 数据不大时,可以在项目启动的时候自动进行加载
- 定时刷新缓存
缓存更新
除Redis系统自带的缓存失效策略,常见采用以下两种:
- 定时清理过期的缓存
- 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存
缓存降级
降级的目的是保证核心服务可用,即使是有损的,而且有些服务是无法降级的(如电商的购物流程等);
在进行降级之前要对系统进行梳理,从而梳理出哪些必须保护,哪些可降级
缓存同步方案:
读取数据时,先读取 Redis 中的数据,如果 Redis 没有,则从原数据库中读取,并同步更新 Redis 中的数据。写回时,写入到原数据库中,并同步更新到 Redis 中。
3.两种缓存技术对比(重要)
工作 | MemCache | Redis |
---|---|---|
数据类型 | 简单key/value结构 | 丰富的数据结构 |
持久型 | 不支持 | 支持 |
分布式存储 | 客户端哈希分片/一致性哈希 | 多种方式,主从、Sentinel、Cluster等 |
多线程支持 | 支持 | 支持(Redis5.0及以前版本不支持) |
内存管理 | 私有内存池/内存池 | 无 |
事务支持 | 不支持 | 有限支持 |
数据容灾 | 不支持,不能做数据恢复 | 支持,可以在灾难发生时,恢复数据 |
- Memcache不支持数据持久化操作,所以掉电数据会全部丢失,而且无法直接恢复,存在可靠性问题。
- Memcache不支持事务,所以操作过程中可能产生数据的不一致性。