如果软件架构的定义是 SA={components, connectors, constraints},也就是说,软件架构包括组件、连接件和约束三大要素,这类软件架构演化主要关注的就是组件、连接件和约束的添加、修改与删除等。
演化类型 |
说明 |
对象演化 |
对象本身包含了众多的属性,如接口、类型、语义等,这些属性的演化对于对象之间的交互过程并无影响。因此,会对架构设计的动态行为产生影响的演化只包括AddObject和DeleteObject两种。 |
消息演化 |
消息演化是面向对象软件架构演化的核心。 我们将消息演化分为AddMessage、DeleteMessage、SwapMessageOrder(交换消息的时间顺序)、OverturnMessage(反转消息的发送对象与接收对象)、ChangeMessageModule(改变消息的发送或接收对象)五种。 |
复合片段演化 |
复合片段是对象交互关系的控制流描述,表示可能发生在不同场合的交互,与消息同属于连接件范畴。 复合片段的演化分为AddFragment、DeleteFragment、FragmentTypeChange(改变复合片段的类型)、FragmentConditionChange(改变复合片段内部执行的条件)。 |
约束演化 |
约束演化即直接对约束信息进行添加(AddConstraint)或删除(DeleteConstraint)。 |
1. 设计时演化:发生在体系结构模型和与之相关的代码编译之前的软件架构演化。
2. 运行前演化:发生在执行之前、编译之后的软件架构演化。
3. 有限制运行时演化:在某些特定约束满足时,进行的一些规定好的演化操作。
4. 运行时演化:系统的体系结构在运行时不能满足要求时发生的软件架构演化,此时的演化是最难实现的。
一次完整软件架构演化过程可以看作经过一系列原子演化操作组合而成。
1. 与可维护性相关的架构演化操作。
名称 |
说明 |
AMD(Add Module Dependence) |
增加模块间的依赖关系 |
RMD(Remove Module Dependence) |
删除模块间的依赖关系 |
AMI(Add Module Interface) |
增加模块间的接口 |
RMI(Remove Module Interface) |
删除模块间的接口 |
AM(Add Module) |
增加一个模块 |
RM(Remove Module) |
删除一个模块 |
SM(Split Module) |
拆分模块 |
AGM(Aggregate Modules) |
聚合模块 |
2. 与可靠性相关的架构演化操作。
架构演化的可靠性评估基于UML用例图、部署图和顺序图。
名称 |
说明 |
AMS(Add Message) |
在顺序图中增加模块交互消息 |
RMS(Remove Message) |
在顺序图中删除模块交互消息 |
AO(Add Object) |
在顺序图中增加交互对象 |
RO(Remove Object) |
在顺序图中删除交互对象 |
AF(Add Fragment) |
在顺序图中增加消息片段 |
RF(Remove Fragment) |
在顺序图中删除消息片段 |
CF(Change Fragment) |
在顺序图中修改消息片段 |
AU(Add Use Case) |
在用例图中为参与者增加一个可执行用例 |
RU(Remove Use Case) |
在用例图中为参与者删除某个可执行用例 |
AA(Add Actor) |
在用例图中增加参与者 |
RA(Remove Actor) |
在用例图中删除参与者 |
对于复杂的应用系统,通过对功能进行分层和线索化,可以形成正交软件结构(Orthogonal Software Architecture)。
正交体系结构中同一层级中的软件不允许相互调用,故每个变动仅影响一条线索。
目前,实现软件架构动态演化的技术主要有两种:
1. 采用动态软件架构(DSA)。
动态软件架构是指在系统执行期间可以修改自身的架构。
应用实例:PKUAS。
2. 进行动态重配置(DR)。
动态重配置是指在软件部署之后对配置信息进行修改。
应用实例:可重用、可配置的产品线架构。
DSA实施动态演化需要DSA描述语言的支持,按照描述视角可将这些语言分为三类:
1. 基于行为视角的π-ADL。
使用进程代数来描述具有动态性的行为。
进程代数将以序列化方式执行的一系列行为抽象为进程,行为的交互被简化为进程的合成。
2. 基于反射视角的Pilar。
利用反射理论显式地为元信息建立模型。
3. 基于协调视角的LIME。
注重计算和协调部分的分离,利用协调论的原理来解决动态性交互。
1. 演化成本控制(Evolution Cost Control,ECC)原则。
演化成本要控制在预期的范围之内,也就是演化成本要明显小于重新开发成本。
2. 进度可控(Schedule Control)原则。
架构演化要在预期时间内完成,也就是时间成本可控。
3. 风险可控(Risk Control)原则。
架构演化过程中的经济风险、时间风险、人力风险、技术风险和环境风险等必须在可控范围内。
4. 主体维持原则。
对称稳定增长(the Average Incremental Growth,AIG)原则所有其他因素必须与软件演化协调,开发人员、销售人员、用户必须熟悉软件演化的内容,从而达到令人满意的演化。因此,软件演化的平均增量的增长须保持平稳,保证软件系统主体行为稳定。
5. 系统总体结构优化(Optimization of Whole Structure)原则。
架构演化要遵循系统总体结构优化原则,使得演化之后的软件系统整体结构(布局)更加合理。
6. 平滑演化(Invariant Work Rate,IWR)原则。
在软件系统的生命周期里,软件的演化速率趋于稳定,如相邻版本的更新率相对固定。
7. 目标一致(Objective Conformance)原则。
架构演化的阶段目标和最终目标要一致。
8. 模块独立演化原则。
软件中各模块(相同制品的模块,如Java的某个类或包)自身的演化最好相互独立,或者至少保证对其他模块的影响比较小或影响范围比较小。
9. 影响可控(Impact Limitation)原则。
软件中一个模块如果发生变更,其给其他模块带来的影响要在可控范围内,也就是影响范围可预测。
10. 复杂性可控(Complexity Controllability)原则。
架构演化必须要控制架构的复杂性,从而进一步保障软件的复杂性在可控范围内。
11. 有利于重构(Useful for Refactoring)原则。
架构演化要遵循有利于重构原则,使得演化之后的软件架构更便于重构。
12. 有利于重用(Useful for Reuse)原则。
架构演化最好能维持,甚至提高整体架构的可重用性。
13. 设计原则遵从性(Design Principles Conformance)原则。
架构演化最好不能与架构设计原则冲突。
14. 适应新技术(Technology Independence,TI)原则。
软件要独立于特定的技术手段,这样才能够让软件运行于不同平台。
15. 环境适应性(Platform Adaptability)原则。
架构演化后的软件版本能够比较容易适应新的硬件环境与软件环境。
16. 标准依从性(Standard Conformance)原则。
架构演化不会违背相关质量标准(国际标准、国家标准、行业标准、企业标准等)。
17. 质量向好(Quality Improvement,QI)原则。
通过演化使得所关注的某个质量指标或某些质量指标的综合效果变得更好或者更满意,例如可靠性提高了。
18. 适应新需求(New Requirement Adaptability)原则。
架构演化要很容易适应新的需求变更;架构演化不能降低原有架构适应新需求的能力;架构演化最好可以提高适应新需求的能力。
量化分析,有需要再阅读。
大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量的数据,任何简单的需求一旦需要处理数以P计的数据和面对数以亿计的用户,问题就会变得棘手。通常大型网站架构主要解决这类问题。
小型网站最开始没有太多人访问,只需要一台服务器就绰绰有余,应用程序、数据库、文件等所有资源都在这台服务器上。
随着网站业务的发展,一台服务器逐渐不能满足需求,越来越多的用户访问导致性能越来越差,越来越多的数据导致存储空间不足,这时就需要将应用和数据分离。
应用和数据分离后整个网站使用3台服务器:应用服务器、文件服务器和数据库服务器。
这3台服务器对硬件资源的要求各不相同:
1. 应用服务器需要处理大量的业务逻辑,因此需要更快更强大的处理器速度。
2. 数据库服务器需要快速磁盘检索和数据缓存,因此需要更快的磁盘和更大的内存。
3. 文件服务器需要存储大量用户上传的文件,因此需要更大容量的硬盘。
随着用户逐渐增多,网站又一次面临挑战:数据库压力太大导致访问延迟,进而影响整个网站的性能,用户体验受到影响。这时可以使用缓存来进一步优化。
网站使用的缓存可以分为两种:
1. 缓存在应用服务器上的本地缓存。
本地缓存的访问速度更快一些,但是受应用服务器内存限制,其缓存数据量有限,而且会出现和应用程序争用内存的情况。
2. 缓存在专门的分布式缓存服务器上的远程缓存。
远程分布式缓存可以使用集群的方式,部署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存容量限制的缓存服务。
使用缓存后,数据访问压力得到有效缓解,但是单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器成为整个网站的瓶颈。
使用集群是网站解决高并发、海量数据问题的常用手段。
通过负载均衡调度服务器,可以将来自用户浏览器的访问请求分发到应用服务器的任何一台服务器上,如果有更多用户,就在集群中加入更多的应用服务器,使应用服务器的压力不再成为整个网站的瓶颈。
网站在使用缓存后,使对大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作(缓存访问不命中、缓存过期)和全部的写操作都需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈。目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器上。网站利用数据库的这一功能,实现数据库读写分离,从而改善数据库负载压力。
应用服务器在写数据的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离对应用透明。
随着网站业务不断发展,用户规模越来越大,由于区域的差别使得网络环境异常复杂,不同地区的用户访问网站时,速度差别也极大。有研究表明,网站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问速度。主要手段有使用CDN和反向代理。CDN和反向代理的基本原理都是缓存。
CDN部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据。
反向代理则部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器是反向代理服务器,如果反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。
使用CDN和反向代理的目的都是今早返回数据给用户,一方面加快用户访问速度,另一方面也减轻后端服务器的压力。
数据库经过读写分离后,从一台服务器拆分成两台服务器,但是随着网站业务的发展依然不能满足需求,这时需要使用分布式数据库。分布式数据库是网站数据拆分的最后手段,只有在单表数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同业务的数据部署在不同的物理服务器上。
文件系统也一样,必要时可以使用分布式文件系统。
随着网站业务越来越复杂,对数据存储和检索的需求也越来越复杂,网站需要采用一些非关系数据库技术如NoSQL和非数据库查询技术如搜索引擎。NoSQL和搜索引擎都是源自互联网的技术手段,对可伸缩的分布式特性具有更好的支持。应用服务器则通过一个统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。
大型网站为了应对日益复杂的业务场景,通过使用分而治之的手段将整个网站业务分成不同的产品线。如大型购物交易网站都会将首页、商铺、订单、买家、卖家等拆分成不同的产品线,分归不同的业务团队负责。
具体到技术上,也会根据产品线划分,将一个网站拆分成许多不同的应用,每个应用独立部署。应用之间可以通过一个超链接建立关系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系统。
随着业务拆分越来越小,存储系统越来越庞大,应用系统的整体复杂度呈指数级增加,部署维护越来越困难。由于所有应用要和所有数据库系统连接,在数万台服务器规模的网站中,这些连接的数目是服务器规模的平方,导致数据库连接资源不足,拒绝服务。
既然每一个应用系统都需要执行许多相同的业务操作,比如用户管理、商品管理等,那么可以将这些共用的业务提取出来,独立部署。由这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。
大型网站的架构演化到这里,基本上大多数的技术问题都得以解决,诸如跨数据中心的实时数据同步和具体网站业务相关的问题也都可以通过组合改进现有技术架构解决。
软件架构知识管理是对架构设计中所隐含的决策来源进行文档化表示,进而在架构维护过程中帮助维护人员对架构的修改进行完善的考虑,并能够为其他软件架构的相关活动提供参考。
架构知识 = 架构设计 + 架构设计决策,即需要说明在进行架构设计时采用此种架构的原因。
1. 圈复杂度(CCN)。
2. 扇入扇出度(FFC)。
扇入是指直接调用该模块的上级模块的个数,扇出指该模块直接调用的下级模块的个数。
3. 模块间耦合度(CBO)。
模块与其他模块交互的频繁程度。一般来说,组件与其他组件的依赖关系及接口越多,该组件的耦合度越大。
4. 模块的响应(RFC)。
组件执行所需的功能的数量,包括接口提供的功能、依赖的其他模块提供的功能以及子模块提供的功能。
5. 模块间内聚度TCC和LCC。