12.1 软件体系结构概念
软件的复杂性
处理复杂性
随着软件系统的规模和复杂性不断增加,对系统的全局结构设计和规划变得 比算法的选择和数据结构的设计明显重要得多。
软件体系结构概念
软件体系结构(Software Architecture)包括构成系统的设计元素的描述、设计元素 之间的交互、设计元素的组合模式以及在这些模式中的约束。
软件体系结构概念
软件体系结构 = 构件 + 连接件 + 约束
- 如何将复杂的软件系统划分成模块
- 如何规范模块的构成
- 如何将这些模块组织成为完整的系统
- 如何保证系统的质量要求
构件是具有某种功能的可复用的软件结构单元,表示系统中主要的计算元素和数据存储
连接是构件间建立和维护行为关联与信息传递的途径
- 机制:过程调用、中断、I/O、事件、进程、线程、共享、同步、并发、消息、远程调用、动态连接、API 等等
- 协议:对过程调用来说:参数的个数和类型、参数排列次序
对消息传送来说:消息的格式
连接件表示构件之间的交互并实现构件之间的连接
- 管道(pipe)
- 过程调用(procedure call)
- 事件广播(event broadcast)
- 客户机-服务器(client-server)
- 数据库连接(SQL)
- 一般构件是软件功能设计和实现的承载体
- 连接件是负责完成构件之间信息交换和行为联系的专用构件
软件体系结构的目标
可重用性:复用已实现证明的 体系结构,或提供可重用资产
可扩展性:易于增加新的功能 可改变性:易于适应需求变更
简单性:将复杂问题简单化, 使系统更加易于理解和实现
有效性:体现早期设计决策,展现系统满足需求的能力
软件体系结构的发展
风格、模式和框架
- 体系结构风格:用于描述某一特定应用领域中系统组织的惯用模式,反映 了领域中众多系统所共有的结构和语义特性。
- 设计模式:描述了软件系统设计过程中常见问题的一些解决方案,通常是 从大量的成功实践中总结出来的且被广泛公认的实践和知识。
- 软件框架:软件框架是由开发人员定制的应用系统的骨架,是整个或部分 系统的可重用设计,由一组抽象构件和构件实例间的交互方式组成。
框架和体系结构的关系:
- 体系结构的呈现形式是一个设计规约,而框架则是“半成品”的软件
- 体系结构的目的是指导软件系统的开发,而框架的目的是设计复用
框架和设计模式的关系:
- 框架给出的是整个应用的体系结构;而设计模式则给出了单一设计问 题的解决方案,且可以在不同的应用程序或者框架中进行应用。
- 举例:一个网络游戏可以基于网易的Pomelo框架开发,这是一个基于 Node.js的高性能、分布式游戏服务器框架;在实现某个动画功能时, 可能会使用观察者模式实现自动化的通知更新。
- 设计模式的目标是改善代码结构,提高程序的结构质量;框架强调的 是设计的重用性和系统的可扩展性,以缩短开发周期,提高开发质量。
12.2 软件设计原则
软件设计原则
设计原则是系统分解和模块设计的基本标准,应用这些原则可以使代码更加灵活、易于维护和扩展。
抽象是关注事物中与问题相关部分而忽略其他无关部分的一种思考方法。
封装和信息隐藏是指每个软件单元对其他所有单元都隐藏自己的设计决策, 各个单元的特性通过其外部可见的接口来描述。
要求:应将单元接口设计得尽可能简单,并将单元对于环境的假设和要求降至最低
模块化是在逻辑和物理上将整个系统分解成多个更小的部分,其实质是 “分而治之”,即将一个复杂问题分解成若干个简单问题,然后逐个解决。
系统分解原则
系统分解的目标:高内聚、低耦合
内聚性是一个模块或子系统内部的依赖程度.
- 如果一个模块或子系统含有许多彼此相关的元素,并且它们执行类似任务,那么其内 聚性比较高;如果一个模块或子系统含有许多彼此不相关的元素,其内聚性就比较低。
耦合性是两个模块或子系统之间依赖关系的强度.
- 如果两个模块或子系统是松散耦合的,二者相互独立,那么当其中一个发生变化时对 另一个产生的影响就很小;如果两个模块或子系统是紧密耦合的,其中一个发生变化 就可能对另一个产生较大影响.
改进:在子系统和数据库之间增加一个存储子系统Storage,从而屏蔽了 底层数据库的变化对上层子系统的影响
层次化
分层(Layering)
- 每一层可以访问下层,不能访问上层
- 封闭式结构:每一层只能访问与其相邻的下一层
- 开放式结构:每一层还可以访问下面更低的层次
- 层次数目不应超过 7±2 层
划分(Partitioning)
- 系统被分解成相互对等的若干模块单元
- 每个模块之间依赖较少,可以独立运行
注意:模块单元增加了处理开销,过度分层或划分会增加复杂性。
复用(Reuse)是利用某些已开发的、对建立新系统有用的软件元素来生成 新的软件系统,其好处在于提高生产效率,提高软件质量。
- 源代码复用:对构件库中的源代码构件进行复用
- 软件体系结构复用:对已有的软件体系结构进行复用
- 框架复用:对特定领域中存在的一个公共体系结构及其构件进行复用
- 设计模式:通过为对象协作提供思想和范例来强调方法的复用
12.3 软件体系结构风格(一)
软件体系结构风格
大部分的软件设计是例行设计,即有经验的软件开发人员经常会借鉴现有的 解决方案,将其改造成新的设计。
软件体系结构风格(Architectural Styles)是描述特定系统组织方式的惯用 范例,强调了软件系统中通用的组织结构。
常见的体系结构风格
主程序-子程序
主程序-子程序风格是结构化程序设计的一种典型风格,从功能的观点设计 系统,通过逐步分解和细化,形成整个系统的体系结构
构件:主程序、子程序
连接器:调用-返回机制
拓扑结构:层次化结构
面向对象风格
- 系统被看作是对象的集合,每个对象都有一个它自己的功能集合;
- 数据及作用在数据上的操作被封装成抽象数据类型;
- 只通过接口与外界交互,内部的设计决策则被封装起来。
构件:类和对象 连接器:对象之间通过函数调用和消息传递实现交互
管道-过滤器风格
管道-过滤器风格把系统任务分成若干连续的处理步骤,这些步骤由通过系统的数据流连接,一个步骤的输出是下一个步骤的输入。
以数据为中心的风格
剪贴板是一个用来进行短时间的数据存储,并在文档/应用之间进行 数据传递和交换的软件程序
仓库体系结构(Repository Architecture)是一种以数据为中心的体系结构, 适合于数据由一个模块产生而由其他模块使用的情形。
基于数据库的系统结构
12.8 数据库选择策略
什么是数据库?
- 数据库就是对数据的管理
- 业务中包括了对数据的增,删,改,查等操作
- 数据管理中包括用户访问权限,持久化,分布式等不同的方案选择
数据库的基本分类
针对面临的一些问题,现有的数据库往往有以下一些简单的性质分类。
数据怎么存?
• 持久化存储, 内存数据库
• 单机,分布式
数据怎么增删改查?
• 关系查找,key-value查找
操作是否安全?
• 事务
• 一致性,最终一致性
可用性
• 故障了怎么恢复?大部分数据库都具有故障恢复功能。
数据库选择策略
数据库选择案例分析
1. 考虑一个简单的社交网站案例,要求功能是用户登录,分享等功能。每秒同时 在线人数(并发请求)大约为100人。
- 用户数据需要持久化存储,不能用内存数据库。
- 用户数据安全性不高,不要求事务性,可以使用Mongo
- 查询并发量100,较小,mysql(以2000估计)与mongo(以5000估计)均可使用
- 业务较复杂,业务模型需要可扩展,mongo修改更方便。
- 综合以上几点,我们建议使用Mongo作为数据库
2. 考虑一个新闻网站,没有用户登录与交互,仅需要展示新闻内容,但是同时 的用户访问量大约为10000人(每秒万人访问)。
- 没有用户数据,也没有复杂的关系业务。
- 访问量极高,mongo,mysql均不适用。
- 内容单一,考虑内存数据库
- 安全性要求低,内存数据库可以使用
- 综合以上四点,我们建议使用redis等内存数据库。
3. 考虑一个抢票服务,功能为在某个时刻为大量用户进行抢票服务。要求能够 在10s内为5000个用户正确的返回抢票结果。
- 用户数据需要持久化存储,考虑 mysql 和 mongo。
- 事务性质,不能有一张票被两个人同时抢到,考虑mysql。
- 要求快速响应,需要在短时间内返回结果,考虑内存数据库
考虑到业务场景的复杂性,我们尝试能否对应用场景进行分离,即数据持久化存储 用mysql,抢票的时候则使用内存数据库进行响应。然而需要注意的是在进行业务 响应时,因为采用内存数据库,那么将不能保证事务性质,怎么办?
对于抢票业务,我们尝试分离业务场景: • 抢票业务可以分成 (1) 非抢票阶段用户登录注册 (2) 抢票阶段的用户抢票,这部分 又包括用户身份验证,用户发出抢票请求,系统验证是否还有余票,系统返回结果
针对以上业务场景的分离,我们对(2)部分的业务请求速度有着极大的需求,因此希望 全都在内存之中进行。而(1)因为需要持久化存储,则使用Mysql进行。而在抢票时对 用户身份的验证,可以在抢票前将所有用户信息读入到内存数据库之中,来保证访问 速度。最后在系统验证余票时,应该有一个事务机制保证不会出现一张票被两个人同 时抢到,该机制完全可以通过我们自己的业务代码来进行效率的保证,从而能够将(2) 部分的任务全都在内存中进行,保证了反馈的速度。