Xiaowu/20230221 (#777)
* update * update * update * update * update * update * Update 12.2 业务场景架构.md * update * update * Update 12.2 业务场景架构.md * up * update * update * Update 13.1 与架构相关的概念.md * update * Update 13.8 架构设计的最佳实践.md * updata * update * Update 12.0 技术架构.md * Update 13.2 业务场景架构.md * update * update * Update 12.7 康威定律.md * Update 12.2 技术架构模式.md * Update 13.1 与架构相关的概念.md * update * update * update * update * update * update * update * Update 13.4 架构设计最佳实践.md * update * Update 13.4 架构设计最佳实践.md * update * Update 13.4 架构设计最佳实践.md * update * update * update * update * update * update * updaate * update * update * update * update * update * Update 13.7 手机Edge浏览器概要设计.md * update * update * Delete Slide36.SVG * update * update * Update 13.7 手机Edge浏览器概要设计.md * Update 13.5 架构设计最佳实践.md * update * update * update * Update 13.8 三高.md * update * Update 13.8 三高.md * update * update * update * update * update
|
@ -322,6 +322,8 @@ OK,如果你能够从以上几个方面分析好自己当前的境遇,判断
|
|||
|
||||
### 3.3.6 环形系统与思维形式
|
||||
|
||||
环形系统,在生活中貌似不常见,但是其实人类本身就生活在一个环形的大自然环境中,虽然封闭但是可以自我修复,以达到最平衡的状态。古人总结出来的太极和五行理论就是环形系统。
|
||||
|
||||
<img src="img/Slide20.SVG"/>
|
||||
|
||||
图 3.3.7 环状系统的负反馈
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
对于大中型系统,需要架构设计,因为要考虑框架的灵活性、可维护性等。在架构设计文档中要包括 4+1 视图:
|
||||
- 场景视图
|
||||
- 逻辑视图
|
||||
- 进程视图
|
||||
- 运行视图
|
||||
- 开发视图
|
||||
- 物理视图
|
||||
|
||||
|
|
|
@ -281,4 +281,6 @@
|
|||
|
||||
也可以叫做 postmortem(事后检讨)。
|
||||
|
||||
2、4、5三个会议有点儿罗嗦,可以合并成一个或两个会议,只要大家能分清楚开会时在讨论什么主题即可。
|
||||
【最佳实践】2、4、5三个会议有点儿罗嗦,可以合并成一个或两个会议,只要大家能分清楚开会时在讨论什么主题即可。
|
||||
|
||||
【最佳实践】敏捷开发流程是一把双刃剑,已经有过严格的软件工程规范训练的团队可以考虑使用,而新手尽量避免使用,避免“萝卜快了不洗泥,最终用户嘴啃泥”的情况发生。
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
## 5.7 开发流程与验证工具的选择
|
||||
|
||||
(这部分可以考虑挪到 11 章)
|
||||
|
||||
### 5.7.1 采用合理的开发流程
|
||||
|
||||
#### 顺序开发与敏捷开发的对比
|
||||
|
@ -227,3 +229,45 @@ MVP 最小可行产品与 4.6 节中讲的“垂直切片”的概念不完全
|
|||
而图 5.7.4 的右子图展示了最终的完美产品,它应该在软件质量的各个特性上都是平衡的,而非金字塔形。
|
||||
|
||||
从一定程度上来说,最小可行产品的概念更像是 4.4 节中的小型软件开发流程,所以,从中也可以看到,中大型软件和小型软件的开发流程其实是互通的,可以互相借鉴,或者是最终都可以通过系统分解而归结到小型软件上。
|
||||
|
||||
|
||||
### 5.7.3 CI/CD
|
||||
|
||||
解决上述问题的手段就是集成,从一开始就集成,并且不断的集成,反复的将拆分的子系统、模块、组件重新组合,看看是否能够顺利组合起来,并且保证功能的不变。
|
||||
|
||||
实现上述不断地集成以及成果物交付的过程就是持续集成和持续交付:
|
||||
|
||||
1.持续集成:指对代码的提交,构建,测试的过程,这是一个持续、反复的过程。
|
||||
|
||||
2.持续交付:指将集成好的交付物,例如war、jar或者容器镜像,部署在联调环境,或者预发环境的过程。
|
||||
|
||||
以下是本项目采用的一个持续集成、持续交付的过程,研发团队在项目实施过程中要严格遵守:
|
||||
|
||||
|
||||
<img src="img/Slide24.JPG"/>
|
||||
|
||||
新加了一张图
|
||||
|
||||
|
||||
持续集成、持续交付的基本流程如下:
|
||||
|
||||
1. 代码开发,完成分配的任务。
|
||||
|
||||
2. 每天提交代码,降低代码集成的风险。采用SVN的提交方式,后提交者有责任去merge,保证代码的编译通过和测试通过。
|
||||
3. 专人定期审核提交的代码,把控代码质量。
|
||||
|
||||
4. 代码审核完毕之后,触发编译过程,完成代码编译。
|
||||
|
||||
5. 编译完成,进行单元测试。要求每个类都要有单元测试,并且单元测试覆盖率要达到一定的指标。单元测试要有带Mock的模块内的集成测试。如果单元测试不通过,则统计后发邮件,抄送所有的人。
|
||||
|
||||
6. 单元测试通过以后,上传成果物(war、jar或其它)至Nexus私服。
|
||||
|
||||
7. 如果采用私有云,并且使用docker容器,则需要编译Dockerfile,使用Docker镜像作为交付,能够实现更好的环境一致性,保证原子的升级和回滚。
|
||||
|
||||
8. 每天下班前,当天的代码需要提交到库中去,晚上会做一次统一的环境部署和集成测试。这个集成测试或者叫回归测试每天晚上都做,都是在一个全新的环境中。如果某一天测试不通过,则会发邮件通知。
|
||||
|
||||
9. 一个周期完毕,进行UAT测试。如果测试不通过,则会发邮件通知,开发人员要及时更正。
|
||||
|
||||
10.UAT测试通过以后,准备上线到生产环境。建议采用灰度发布或蓝绿发布机制,分批次发布、切换流量。一般情况下,具有权限的管理人员通过自动化脚本进行部署。
|
||||
|
||||
通过持续集成、持续交付这套完整的流程,层层保证质量,保证项目可以按时按质的完成,减少项目的实施风险。
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
- Workspace 工作区
|
||||
你电脑本地看到的文件和目录,在 Git 的版本控制下,构成了工作区。
|
||||
|
||||
<img src="img/Slide24.JPG"/>
|
||||
<img src="img/Slide25.JPG"/>
|
||||
|
||||
图 5.8.1 Git 工作流
|
||||
|
||||
|
@ -58,7 +58,7 @@ Git 的使用流程如下:
|
|||
|
||||
先登录 Azure DevOps 网站,建立团队和项目。
|
||||
|
||||
<img src="img/Slide25.JPG"/>
|
||||
<img src="img/Slide26.JPG"/>
|
||||
|
||||
图 5.8.2 项目流程类型与主菜单
|
||||
|
||||
|
@ -66,7 +66,7 @@ Git 的使用流程如下:
|
|||
|
||||
#### 2. Backlogs - 创建产品计划
|
||||
|
||||
<img src="img/Slide26.JPG"/>
|
||||
<img src="img/Slide27.JPG"/>
|
||||
|
||||
图 5.8.3 待办事项 Backlogs
|
||||
|
||||
|
@ -87,7 +87,7 @@ Git 的使用流程如下:
|
|||
|
||||
#### 3. Boards - 添加具体工作内容并管理
|
||||
|
||||
<img src="img/Slide27.JPG"/>
|
||||
<img src="img/Slide28.JPG"/>
|
||||
|
||||
图 5.8.4 工作版内容管理 Boards
|
||||
|
||||
|
@ -99,7 +99,7 @@ Board 中有四列分栏,分别是 New、Active、Resolved、Closed,状态
|
|||
|
||||
#### 4. Sprints - 制定冲刺计划
|
||||
|
||||
<img src="img/Slide28.JPG"/>
|
||||
<img src="img/Slide29.JPG"/>
|
||||
|
||||
图 5.8.5 冲刺计划 Sprints
|
||||
|
||||
|
@ -117,7 +117,7 @@ Board 中有四列分栏,分别是 New、Active、Resolved、Closed,状态
|
|||
- Dashboards - 仪表板
|
||||
在主菜单的 Overview 下面。
|
||||
|
||||
<img src="img/Slide29.JPG"/>
|
||||
<img src="img/Slide30.JPG"/>
|
||||
|
||||
图 5.8.6 仪表板 Dashboards
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
- 毛毛:“电子邮件是无纸办公的重要手段,倒是可以考虑增加这个功能。”
|
||||
- 木头:“用户实际上是想通过网络的方式把自己的意见快速准确地转达给其它人,那不如我们后期考虑在 Azure 上搭建一个服务器吧,这样大家都可以分享自己的见解。”
|
||||
|
||||
【最佳实践】就如同亨利福特遇到的情况,用户希望有一匹快马,但其实用户需要的只是“快”,而不是“马”,“快”是真正的需求,“马”只是载体,所以福特开始造新的载体——汽车。理解用户的真实意图,用自己的专业知识提供解决方案。
|
||||
【最佳实践】就如同亨利·福特遇到的情况:福特问用户在交通方面有什么需求,用户说“希望有一匹快马”。但其实用户需要的只是“快”,而不是“马”,“快”是真正的需求,“马”只是载体,所以福特开始造新的载体——汽车。理解用户的真实意图,用自己的专业知识提供解决方案。
|
||||
|
||||
### 7.2.2 卡片分类
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
### 8.10.2 需求规格说明书的模板
|
||||
|
||||
#### Assumption/Background 假定和背景
|
||||
#### 1. Assumption/Background 假定和背景
|
||||
|
||||
在需求规格说明书中,首先要简单讲解一下项目或产品的大概背景。可以分成两种情况:
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
|||
|
||||
*我们发现大部分研究员都是用 Edge 浏览器和 OneNote 进行阅读和做笔记,非常不方便,所以想在 Edge 浏览器上开发一个扩展插件,来解决做笔记的需要。*
|
||||
|
||||
#### Goal/Non-Goal 目标和非目标
|
||||
#### 2. Goal/Non-Goal 目标和非目标
|
||||
|
||||
仍然沿用读论文工具的例子:
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
|||
|
||||
*Non-Goal:不准备支持 Word 文档格式,不支持触摸输入。*
|
||||
|
||||
#### Persona/Scenario 典型用户与场景
|
||||
#### 3. Persona/Scenario 典型用户与场景
|
||||
|
||||
有了典型用户后,就要把该用户“放进”具体的应用场景中。Scenario 的意思,就是要把用户放进一个真实的故事片段中,来检验其可行性。
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
|||
|
||||
反过来看,就是Surface Hub、Azure、OpenPAI 等设备、服务、软件,都应该提供什么功能,才能满足大学电教课程的需要。设计人员坐在办公室冥想是想不出来的,必须到教学现场观察一下上课的情况,才能够设计出相应的产品,然后再泛化。用故事场景(即Scenario)的形式来描述,就是还原现场的一种有效方法。
|
||||
|
||||
#### Feature/Function list 特性与功能列表
|
||||
#### 4. Feature/Function list 特性与功能列表
|
||||
|
||||
Function 和 Feature 不是同一层意思:
|
||||
|
||||
|
@ -65,7 +65,7 @@ Function 和 Feature 不是同一层意思:
|
|||
|
||||
所以,Spec 既要有功能(Function)列表,又要有特性(Feature)列表。功能列表通常由用户指定,而特性列表则由 PM 在需求分析的基础上给出,要具体到软件界面元素,比如是选择菜单还是点击按钮,出现的是一个数据列表框还是条形图,等等。
|
||||
|
||||
#### Condition/Performance 条件和性能
|
||||
#### 5. Condition/Performance 条件和性能
|
||||
|
||||
一般指运行环境的要求或者限制,以及在这一运行环境下的系统性能指标。比如:
|
||||
|
||||
|
@ -75,7 +75,7 @@ Function 和 Feature 不是同一层意思:
|
|||
|
||||
*网站允许200个用户并发,每个用户的响应时间为50毫秒。*
|
||||
|
||||
#### UI/UX 用户界面与交互
|
||||
#### 6. UI/UX 用户界面与交互
|
||||
|
||||
复杂软件的用户界面和交互,是需要 Designer 参与的,会提供全套的界面元素设计和交互设计。如果是简单的功能,PM 可以根据以有的界面元素直接给定。
|
||||
|
||||
|
@ -83,7 +83,7 @@ Function 和 Feature 不是同一层意思:
|
|||
|
||||
在需求阶段,可以给用户提供一个界面草稿,比如:这个 APP 的界面打算设计成左右分屏的,就像 Outlook 那种三级窗口的形式,功能按钮都在左边栏,阅读区在左侧主屏,辅助区在右侧小屏。一些细节的设置用对话框的形式展现。
|
||||
|
||||
#### Schedule/Plan 计划和日期
|
||||
#### 7. Schedule/Plan 计划和日期
|
||||
|
||||
给出项目的几个关键点,如:
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
|
||||
<img src="img/Slide1.SVG"/>
|
||||
|
||||
技术架构是一个很模糊的概念。
|
||||
|
||||
<img src="img/Slide2.SVG"/>
|
||||
|
||||
|
||||
https://blog.csdn.net/lfsf802/article/details/8487990
|
||||
|
||||
https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013
|
||||
|
||||
https://zhuanlan.zhihu.com/p/41395345
|
||||
|
||||
https://blog.csdn.net/Jayphone17/article/details/103651076
|
||||
|
||||
https://zhuanlan.zhihu.com/p/41395345
|
||||
|
||||
https://www.ou.nl/documents/40554/791670/IM0203_03.pdf/30dae517-691e-b3c7-22ed-a55ad27726d6
|
||||
|
||||
https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013
|
||||
|
||||
https://max.book118.com/html/2016/1115/63079375.shtm
|
||||
|
||||
https://blog.csdn.net/hguisu/article/details/78259898
|
||||
|
||||
|
||||
https://help.aliyun.com/document_detail/207135.html
|
|
@ -1,4 +1,4 @@
|
|||
## 12.1 架构模式演化的故事
|
||||
## 12.1 技术架构演化的故事
|
||||
|
||||
还记得第一章中关于“木头与软件工程的故事”吧?这里有另外一个版本,是同样的应用场景,但是想说明的是软件**技术架构**演化的故事。**架构**这个词包含好几个子概念,本小节中只讲其中的**技术架构**。
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
|
||||
## 12.2 技术架构模式
|
||||
|
||||
### 12.2.1 概念
|
||||
|
||||
#### **1. 什么是架构?**
|
||||
|
||||
架构,源于中国古代建筑术语。架 = 加 + 木,构 = 木 + 勾,即把一堆木头加起来并互相勾住,以形成一个完整的、牢固的建筑。引申到软件产品概念上,就是:
|
||||
|
||||
1. 结构切分:把一个软件项目整体切分成不同的部分;
|
||||
2. 功能定义:定义这些部分各自完成的局部功能;
|
||||
3. 关系建立:建立这些部分相互沟通的机制,使之结合为一个整体,并完成这个整体所需要的所有功能。
|
||||
|
||||
|
||||
在软件系统设计中,有四种常用的架构方法:
|
||||
|
||||
- RUP(Rational Unified Process)统一开发过程
|
||||
- UML(Unified Model Language)统一建模语言
|
||||
- TOGAF(The Open Group Architecture Framework)开发组织定义的架构框架
|
||||
- 其它流派
|
||||
|
||||
我们在第十三章中还要重点讨论。
|
||||
|
||||
#### 2. 什么是技术架构?
|
||||
|
||||
在众多的架构组成中,技术架构定义为:
|
||||
|
||||
$$
|
||||
技术架构 = 逻辑功能 + 数据存储 + 运行过程 + 软件开发 + 物理部署
|
||||
$$
|
||||
|
||||
这些概念还是很复杂,所以读者可以先简单地认为,技术架构包括设计、实现、部署的一系列方案的混合体,就是面向实现的架构设计,主要是给开发人员内部看的,用于统一认识。
|
||||
|
||||
#### **3. 什么是技术架构模式?**
|
||||
|
||||
**模式**是主体行为的一般方式,包括科学实验模式(包括软件开发)、经济发展模式、企业盈利模式等,是理论和实践之间的中介环节,具有一般性、简单性、重复性、结构性、稳定性、可操作性的特征。
|
||||
|
||||
我们经常说“从理论到实践,再从实践到理论”,其实就是为了总结出一个解决问题的通用思路来,这个思路就是模式。所以,模式解决某一类问题的方法论。
|
||||
|
||||
**技术架构模式**是在给定上下文中对软件构建中常见问题的通用的、可重用的解决方案。
|
||||
|
||||
读者都知道 GOF 的 23 种设计模式,技术架构模式类似于软件设计模式,但前者层次更高、范围更广,所解决的问题更大。
|
||||
|
||||
|
||||
### 12.2.2 技术架构模式
|
||||
|
||||
一些基本的技术架构模式如下:
|
||||
|
||||
#### 星形模式
|
||||
|
||||
- 客户端-服务器模式(Client-server pattern)
|
||||
- 浏览器-服务器模式
|
||||
- 对等模式(Peer-to-peer pattern)
|
||||
|
||||
#### 串行模式
|
||||
|
||||
- 分层模式(Layered pattern)
|
||||
- 管道-过滤器模式(Pipe-filter pattern)
|
||||
- 主-从模式(Master-slave pattern)
|
||||
|
||||
#### 树形模式
|
||||
|
||||
- 代理模式(Broker pattern)
|
||||
- 微服务模式(Microservices pattern)
|
||||
- 插件模式(Plug-in pattern)
|
||||
|
||||
#### 环形模式
|
||||
|
||||
- 模型-视图-控制器模式(Model-view-controller pattern)
|
||||
- 反馈模式
|
||||
- 事件驱动模式(Event-driven pattern)
|
||||
|
||||
一个复杂系统的技术架构是利用上面这些基本模式的一个或几个有机组合而成。当然,上面这些并不是全部,只是笔者认为比较重要的、在实际应用中经常被用到的模式。
|
||||
|
||||
### 12.2.3 技术架构模式一览
|
||||
|
||||
表 12.2.1 技术架构模式一览表
|
||||
|
||||
|名称|简单描述|质量评价|
|
||||
|-|-|-|
|
||||
|**星形模式**||
|
||||
|客户端-服务器模式|一个服务器为多个客户端提供定制化服务。<br>主要优点:安全性好,开发方便,可测试性好。<br>主要缺点:客户端数量多时升级维护麻烦。|开发期:较高。<br>运行期:一般。|
|
||||
|浏览器-服务器模式|一组服务器为很多客户端的浏览器提供网页服务。<br>主要优点:开发、维护非常简便,对客户端要求很低。<br>主要缺点:浏览器端开发能力和计算能力受限制。|开发期:一般。<br>运行期:较高。|
|
||||
|对等模式|很多节点之间相互提供服务。<br>主要优点:支持分布式计算,可扩展性高。<br>主要缺点:服务质量、安全性、性能没有保证。|开发期:一般。<br>运行期:一般。|
|
||||
|**串行模式**||
|
||||
|分层模式|把完成相似功能的节点组合成层,形成多层。<br>主要优点:级别与接口清晰,层内改动不影响其他层。<br>主要缺点:性能不高,层间通信有开销。|开发期:较高。<br>运行期:一般。|
|
||||
|管道-过滤器模式|多个节点前后串连接力完成任务。<br>主要优点:不同的节点可以自由组合完成不同的任务。<br>主要缺点:串行计算效率低,过滤器之间交换数据较慢。|开发期:较高。<br>运行期:较低。|
|
||||
|主-从模式|一个主节点把任务分发给多个从节点。<br>主要优点:负载分担,提供更准确的计算结果。<br>主要缺点:从节点孤立,主从通信有延迟而造成不一致。|开发期:较高。<br>运行期:较高。|
|
||||
|**树形模式**||
|
||||
|代理模式|一个节点接受请求并代替其它节点完成任务。<br>主要优点:被代理的对象可以轻松上线离线,修改方便。<br>主要缺点:需要事先知道代理的能力,有可能成为瓶颈。|开发期:较高。<br>运行期:一般。|
|
||||
|微服务模式|很多个小节点临时被创建并组合完成任务。<br>主要优点:强隔离,耦合度低,开发速度快。<br>主要缺点:数量多时带来管理、性能问题。|开发期:较高。<br>运行期:较低。|
|
||||
|插件模式|主节点完成基本任务,附着的小节点完成额外的任务。<br>主要优点:功能插拔灵活,可扩展性高。<br>主要缺点:核心系统需要鲁棒性强,接口设计精细。|开发期:一般。<br>运行期:较高。|
|
||||
|**环形模式**||
|
||||
|MVC 模式|模型、视图、控制器三者协同完成数据展示、修改工作。<br>主要优点:一个模型可以对应多个视图。<br>主要缺点:实现复杂,一般需要框架支持。|开发期:一般。<br>运行期:较高。|
|
||||
|反馈模式|多节点串行工作,末节点的输出作为首节点的部分输入。<br>主要优点:算法先进,内部迭代来学习数据特征。<br>主要缺点:性能低,反馈信号不容易调节。|开发期:较低。<br>运行期:较低。|
|
||||
|事件驱动模式|多个节点从一个队列中获得任务来完成各自不同的任务。<br>主要优点:轻松添加新的发布者和订阅者。<br>主要缺点:事件总线有可能影响计算性能。|开发期:较高。<br>运行期:一般。|
|
||||
|
||||
### 12.2.4 其它模式
|
||||
|
||||
除了上面列出的模式以外,还有一些不常用的模式如下:
|
||||
|
||||
- 黑板模式(Black-Board pattern)
|
||||
|
||||
黑板模式是一种常用的架构模式,应用中的多种不同数据处理逻辑相互影响和协同来完成数据分析处理。黑板模式允许多个消息读写者同时存在,消息的生产者和消费者完全分开。这就像一个黑板,任何一个教授(消息的生产者)都可以在其上书写消息,任何一个学生(消息的消费者)都可以从黑板上读取消息,两者在空间和时间上可以解耦,并且互不干扰。这种模式对于没有确定解决方案策略的问题是有用的。
|
||||
|
||||
这种模式性能较低,一般不适合于实时系统。
|
||||
|
||||
- 解析器模式(Interpreter pattern)
|
||||
|
||||
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
|
||||
|
||||
这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。
|
||||
|
||||
这种模式适用范围较窄。
|
||||
|
||||
- 空间模式(Space pattern)
|
||||
|
||||
基于空间的架构模式(有时也称为云架构模式)将限制应用扩展的因素最小化了。它的名称来源于元组空间(tuple space)的概念,也就是分布式共享内存的概念。通过冗余的内存中的数据网格代替数据库来实现高伸缩性。应用数据保存在内存中,并在所有活动的处理单元中保存一份副本。处理单元可以根据负载大小动态地添加或关闭。这样数据库的瓶颈就不存在了,提供了几乎可以无限扩展的能力。
|
||||
|
||||
该架构模式中有两个主要组件:处理单元(processing unit)和虚拟化中间件(virtualized middleware)。如图1所示。处理单元组件包含应用组件(或部分应用组件),它包含Web组件和后端的业务逻辑。处理单元通常包含应用程序模块、内存中的数据网格和一个可选的异步持久化转移模块。还包含一个复制引擎供虚拟中间件调用,来同步各单元之间的数据变化。
|
||||
|
||||
这种模式设计开发阶段非常复杂。
|
||||
|
||||
- 六边形模式(Hexagonal pattern)
|
||||
|
||||
六边形架构又称为端口-适配器,这个名字更容器理解。六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。内部通过端口和外部系统通信,端口代表了一定协议,以API呈现。一个端口可能对应多个外部系统,不同的外部系统需要使用不同的适配器,适配器负责对协议进行转换。这样就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动,并且,可以在与实际运行的设备和数据库相隔离的情况下开发和测试。
|
||||
|
||||
这种模式在实现业务逻辑时,由于都在六边形内部,有可能因为过于复杂而导致不易维护。
|
|
@ -138,42 +138,3 @@
|
|||
- 升级软件时需要有计划地逐个节点升级,而且备份、恢复资源困难。
|
||||
- 网络安全性差,资源分散,容易有薄弱环节
|
||||
- 当用户所在的节点做为服务器时,会影响用户本地的计算性能。
|
||||
|
||||
### 12.3.4 主从模式(Master-Slave/Primary-Secondary)
|
||||
|
||||
因为 Slave 这个词比较敏感,因此英文中更多地使用 Primary-Secondary,我们只要知道 Slave 不是“奴隶”的意思就好。见图 12.3.4。
|
||||
|
||||
<img src='img/Slide11.svg'>
|
||||
|
||||
图 12.3.4 主从模式
|
||||
|
||||
#### 架构模式
|
||||
|
||||
这种模式由两部分组成:主节点和从节点。主节点将工作分配给不同的从节点,并根据从节点返回的结果计算最终结果。图 12.3.4 右侧显示了主从节点之间分配工作的顺序图,其中有一个硬性的要求:初始任务必须是可以**分割**的。
|
||||
|
||||
主从模式是分治(divide-and-conquer)原则的一个例子。每个从节点都是独立的,没有任何共享状态,因为它们是并行运行,或者是在同一个主机的不同进程中,或者是在不同的主机上,所以主从通信的延迟可能是一个问题。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
那么什么叫做可以分割?注意“分割”这个词比较生硬,它并非智能地“分解”,也非理性地“分析”。有几种应用:
|
||||
|
||||
- 并行计算:把一批数据或任务整齐地拆分成 N 份儿,然后分配给 N 个从节点去处理。比如大规模的并行计算时,可以把多个矩阵的运算分配给多个从节点并行完成,再在主节点中合并。
|
||||
- 容错:主节点把同一个计算任务分配个多个从节点,然后从最快结束运算的从节点那里获得返回结果,并返回给调用者;或者是比较所有从节点的返回结果,取相似性最高的结果(三个结果是1.512,一个结果是1.511,则取1.512),返回给调用者。
|
||||
- 提供准确度:不同的从节点执行同一个任务,但是它们各自的实现方式不同,比如,一个是用神经网络推理,一个用线性回归做预测,还有一个用表格匹配法取近似值,最终在主节点上用一种策略来决定最佳结果,如平均值或最多相似值。
|
||||
- 数据库复制:主数据库被视为权威源数据库,从数据库与之同步。在有读取请求时,主数据库把请求转给从数据库,以提高并非读取的速度;在有写入请求时,只发生在主数据库上,然后再同步给从数据库。
|
||||
|
||||
|
||||
#### 优缺点
|
||||
|
||||
优点:
|
||||
|
||||
- 读写分离。
|
||||
- 适合读多写少的场景,大并发读取时,多个从节点可以提供负载均衡。
|
||||
- 从节点数量扩展灵活。
|
||||
- 不怕数据丢失,有很多个备份。
|
||||
|
||||
缺点:
|
||||
|
||||
- 同步延迟问题,主从节点上的数据在某一时刻有可能不一致,不适合于要求一致性高的场合。
|
||||
- 如果有大量写操作,会集中在主节点上。
|
||||
- 主节点发生故障会比较麻烦,自动切换的复杂度高。
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
见图 12.4.1。
|
||||
|
||||
<img src='img/Slide12.svg'>
|
||||
<img src='img/Slide11.svg'>
|
||||
|
||||
图 12.4.1 管道-过滤器模式
|
||||
|
||||
|
@ -20,7 +20,8 @@
|
|||
|
||||
#### 扩展模式
|
||||
|
||||
允许一个过滤器把数据分可后分别推送到不同的管道中,以便后续的不同逻辑的过滤器处理,甚至有可能进入不同的数据汇点。或者是在一开始,就有不同的数据源进入,但是共享某些过滤器(这种情况一般是分当作两个 pipeline 处理)。
|
||||
- 允许一个过滤器把数据分可后分别推送到不同的管道中,以便后续的不同逻辑的过滤器处理,甚至有可能进入不同的数据汇点。
|
||||
- 或者是在一开始,就有不同的数据源进入,但是共享某些过滤器,这种情况一般是分当作两个 pipeline 处理,图 12.4.1 中的过滤器 3 只是个可以重用的组件而已。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
|
@ -54,7 +55,7 @@ cat a.txt | grep 'food' | sort | uniq > out
|
|||
|
||||
见图 12.4.2。
|
||||
|
||||
<img src='img/Slide13.svg'>
|
||||
<img src='img/Slide12.svg'>
|
||||
|
||||
图 12.4.2 分层模式
|
||||
|
||||
|
@ -112,3 +113,42 @@ cat a.txt | grep 'food' | sort | uniq > out
|
|||
- 代码量变多,肯定比都聚合在一起的代码量大。
|
||||
|
||||
分层后代码量自然要比不分层多
|
||||
|
||||
### 12.4.3 主从模式(Master-Slave/Primary-Secondary)
|
||||
|
||||
因为 Slave 这个词比较敏感,因此英文中更多地使用 Primary-Secondary,我们只要知道 Slave 不是“奴隶”的意思就好。见图 12.4.3。
|
||||
|
||||
<img src='img/Slide13.svg'>
|
||||
|
||||
图 12.4.3 主从模式
|
||||
|
||||
#### 架构模式
|
||||
|
||||
这种模式由两部分组成:主节点和从节点。主节点将工作分配给不同的从节点,并根据从节点返回的结果计算最终结果。图 12.4.3 右侧显示了主从节点之间分配工作的顺序图,其中有一个硬性的要求:初始任务必须是可以**分割**的。
|
||||
|
||||
主从模式是分治(divide-and-conquer)原则的一个例子。每个从节点都是独立的,没有任何共享状态,因为它们是并行运行,或者是在同一个主机的不同进程中,或者是在不同的主机上,所以主从通信的延迟可能是一个问题。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
那么什么叫做可以分割?注意“分割”这个词比较生硬,它并非智能地“分解”,也非理性地“分析”。有几种应用:
|
||||
|
||||
- 并行计算:把一批数据或任务整齐地拆分成 N 份儿,然后分配给 N 个从节点去处理。比如大规模的并行计算时,可以把多个矩阵的运算分配给多个从节点并行完成,再在主节点中合并。
|
||||
- 容错:主节点把同一个计算任务分配个多个从节点,然后从最快结束运算的从节点那里获得返回结果,并返回给调用者;或者是比较所有从节点的返回结果,取相似性最高的结果(三个结果是1.512,一个结果是1.511,则取1.512),返回给调用者。
|
||||
- 提供准确度:不同的从节点执行同一个任务,但是它们各自的实现方式不同,比如,一个是用神经网络推理,一个用线性回归做预测,还有一个用表格匹配法取近似值,最终在主节点上用一种策略来决定最佳结果,如平均值或最多相似值。
|
||||
- 数据库复制:主数据库被视为权威源数据库,从数据库与之同步。在有读取请求时,主数据库把请求转给从数据库,以提高并非读取的速度;在有写入请求时,只发生在主数据库上,然后再同步给从数据库。
|
||||
|
||||
|
||||
#### 优缺点
|
||||
|
||||
优点:
|
||||
|
||||
- 读写分离。
|
||||
- 适合读多写少的场景,大并发读取时,多个从节点可以提供负载均衡。
|
||||
- 从节点数量扩展灵活。
|
||||
- 不怕数据丢失,有很多个备份。
|
||||
|
||||
缺点:
|
||||
|
||||
- 同步延迟问题,主从节点上的数据在某一时刻有可能不一致,不适合于要求一致性高的场合。
|
||||
- 如果有大量写操作,会集中在主节点上。
|
||||
- 主节点发生故障会比较麻烦,自动切换的复杂度高。
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
## 12.5 树形系统架构模式
|
||||
|
||||
树形系统是星形系统的一种扩展,在星形系统中,子节点只有一级,而且所有的子节点都是同类的。而树形系统中的下级节点可以是多级,并且各自都不同。树形系统可以理解为是具有分布式架构的系统。
|
||||
|
||||
### 12.5.1 代理模式 (Broker)
|
||||
|
||||
#### 几种“代理”的区别
|
||||
|
@ -82,90 +84,39 @@ DNS 就属于第一种方式,但是并不是典型的代理模式。
|
|||
- 可能会造成请求的处理速度变慢,甚至带来通信瓶颈。
|
||||
- 实现代理模式需要额外的或重复的工作,不易开发。
|
||||
|
||||
|
||||
### 12.5.2 事件驱动模式 (Event-Driven)
|
||||
### 12.5.2 微服务模式(Microservices)
|
||||
|
||||
#### 架构说明
|
||||
|
||||
事件驱动模式应用于事件处理,主要由四个组件构成:事件源(event source),事件侦听者(event listener),通道(Channel)以及事件总线(event bus)。 事件源将消息发布到总线的特定通道,侦听者订阅相应的通道,事件源所发布的消息经通道通告给订阅通道的侦听者。
|
||||
在多层架构模式中,一个应用程序的所有功能都运行在一个进程中,称作单体结构(Monolithic Architecture)。当业务增长使得应用逻辑变得复杂时,代码量骤增,其增加新功能、修改、维护的成本非常高,测试、部署所花的时间也很长。因此,人们想到了面向服务(Service Oriented)这个概念。**面向对象可以封装类,而面向服务可以封装业务逻辑。**
|
||||
|
||||
见图 12.5.2 拓扑结构。
|
||||
面向服务的架构中又可以分为两类,第一类是基于事件驱动模式的(基于数据协议的松耦合合作关系),第二个是基于 REST API 调用的,微服务模式属于第二种。
|
||||
|
||||
当将应用程序作为一组微服务编写时,实际上就是在编写可以协同工作的多个应用程序。其中每个微服务都有自己的职责,每个开发团队都可以独立于其他微服务进行开发。这些微服务之间唯一的依赖就是通信。当微服务彼此通信时,必须确保它们之间发送的消息能够向后兼容(因为微服务可以很快地升级)。
|
||||
|
||||
<img src='img/Slide15.svg'>
|
||||
|
||||
图 12.5.2 事件驱动模式
|
||||
图 12.5.2 微服务模式
|
||||
|
||||
一个事件源一般只发送消息到一个通道中,该通道一般由消息队列实现。而监听者根据业务需求可以监听一个或多个通道内的消息,比如图 12.5.2 中的监听者 1 就同时监听通道 1 和通道 2 的消息,此时,事件源 2 要发送两个消息拷贝到通道 2 中。
|
||||
|
||||
发布-订阅模式是事件驱动模式的一个子集。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
这里用一个网络购物的应用常见来说明事件驱动的应用方式,见图 12.5.2 右侧订单服务实力。
|
||||
|
||||
1. 一个网络购物的用户下了订单,应用程序会发送“订单事件”到事件总线;
|
||||
2. “订单处理”模块会得到“订单处理通知”,处理订单,比如核对存货量、配送地、付款信息等;
|
||||
3. 处理完毕后发送“订单确认事件”到事件总线;
|
||||
4. “订单确认”模块会得到“订单确认通知”,给用户发送确认短信或使用其他通信手段;
|
||||
5. 发送消息给用户;
|
||||
6. 发送完毕后发送“通知成功事件”到事件总线;
|
||||
7. “订单执行”模块会得到“订单配送通知”;
|
||||
8. 商家配送,送货上门,用户签收;
|
||||
9. 配送完成后发送“订单完成事件”到事件总线。
|
||||
10. (未画出)系统发送“用户评价通知”然后关闭该订单。
|
||||
|
||||
这种应用场景相当复杂,如果用串行系统架构种的管道-过滤器模式的话,所有操作都要做成同步的,对于网络购物这种大访问量的应用几乎是不可能的。所以,这种“重要不紧急”的场景很适合于使用事件驱动模式。
|
||||
|
||||
|
||||
#### 优缺点
|
||||
|
||||
优点:
|
||||
|
||||
- 分布式的异步架构,事件处理器之间高度解耦,软件的扩展性好。
|
||||
- 适用性广,各种类型的项目都可以用。
|
||||
- 性能较好,因为事件的异步本质,软件不易产生堵塞。
|
||||
- 事件处理器可以独立地加载和卸载,容易部署。
|
||||
|
||||
缺点:
|
||||
|
||||
- 涉及异步编程(要考虑远程通信、失去响应等情况),开发相对复杂。
|
||||
- 难以支持原子性操作,因为事件通过会涉及多个处理器,很难回滚。
|
||||
- 分布式和异步特性导致这个架构较难测试。
|
||||
|
||||
|
||||
### 12.5.3 微服务模式(Microservices)
|
||||
|
||||
#### 架构说明
|
||||
|
||||
在多层架构模式中,一个应用程序的所有功能都运行在一个进程中,称作单体结构(Monolithic Architecture)。当业务增长使得应用逻辑变得复杂时,代码量骤增,其增加新功能、修改、维护的成本非常高,测试、部署所花的时间也很长。因此,人们想到了面向服务(Service Oriented)这个概念。面向对象可以封装类,而面向服务可以封装业务逻辑。
|
||||
|
||||
面向服务的架构中又可以分为两类,第一类是基于事件总线模式的,第二个是基于 REST API 调用的,微服务模式属于第二种。
|
||||
|
||||
当将应用程序作为一组微服务编写时,实际上就是在编写可以协同工作的多个应用程序。其中每个微服务都有自己的职责,团队可以独立于其他微服务进行开发。这些微服务之间唯一的依赖就是通信。当微服务彼此通信时,必须确保它们之间发送的消息能够向后兼容。
|
||||
|
||||
<img src='img/Slide16.svg'>
|
||||
|
||||
图 12.5.3 微服务模式
|
||||
|
||||
此架构拓扑中有三个部分,见图 12.5.3:
|
||||
此架构拓扑中有三个部分,见图 12.5.2:
|
||||
|
||||
- 网关服务:负责鉴别服务请求,然后通过 REST API 调用对应的微服务。
|
||||
- 微服务:具有很多种类的微服务,每个种类完成一个任务。当同时有很多请求到达时,可以临时创建微服务实例来应对。
|
||||
- 数据库/存储:每个服务种类都有单独的数据库/存储。所以,在微服务和数据库外面画了一个虚线框,表示它们是紧密集成的。当然,有时候可以在两类微服务间共享数据库。
|
||||
|
||||
微服务之间可能存在调用关系,如图 12.5.3 拓扑结构中的黄色虚线所示,但是需要事先注册,以便可以互相发现。
|
||||
微服务之间可能存在调用关系,如图 12.5.2 拓扑结构中的黄色虚线所示,但是需要事先注册,以便可以互相发现。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
电子商务类的互联网应用非常适合用微服务架构模式,见图 12.5.3 右侧,是一个外卖网站的实例。
|
||||
电子商务类的互联网应用非常适合用微服务架构模式,见图 12.5.2 右侧,是一个外卖网站的实例。
|
||||
|
||||
- 处于上方的派送管理、参观管理、客户管理,都是基于静态信息的管理服务。
|
||||
- 处于中间的订单服务、餐馆服务、派送服务,都是以订单为中心的动态信息的业务服务。
|
||||
- 处于上方(橙色)的派送管理、餐馆管理、客户管理,都是基于静态信息的管理服务。
|
||||
- 处于中间(绿色)的订单服务、餐馆服务、派送服务,都是以订单为中心的动态信息的业务服务。
|
||||
- 最下方的计费服务是大家共用的服务。
|
||||
|
||||
另外,客户端软件也可以使用微服务模式,比如 Microsoft OneNotes 应用。该应用中集成了非常多的 Microsoft Office 办公套件的功能,可以自由展示各类文档,还能允许用户在上面随手写写画画。总之就是一个文档的仓库。
|
||||
另外,客户端软件也可以使用微服务模式,比如 Microsoft OneNotes 应用。该应用中集成了非常多的 Microsoft Office 办公套件的功能,可以自由展示各类文档,还能允许用户在上面随手写写画画。
|
||||
|
||||
各种文档格式之间没有多大关系,所以完全可以封装成一个个微服务,各自处理好文档的展示、编辑、保存等功能。
|
||||
各种文档格式之间没有多大关系,所以完全可以封装成一个个微服务,各自处理好文档的展示、编辑、保存等功能。总之就是一个文档的仓库。
|
||||
|
||||
#### 微服务模式的最佳实践
|
||||
|
||||
|
@ -175,7 +126,7 @@ DNS 就属于第一种方式,但是并不是典型的代理模式。
|
|||
|
||||
1. 响应需求变慢,需求开发时间变长。
|
||||
2. 交付的效率变差,bug 数越来越多。
|
||||
3. 业务复杂度变高,应用达到 3 个或 3 个以上,或者模块达到 5 个或以上。
|
||||
3. 业务复杂度变高,子系统达到 3 个或 3 个以上,或者模块达到 5 个或以上。
|
||||
4. 团队人数变多,开发至少有5人以上,运维至少2人。
|
||||
5. 项目需要长期迭代维护,至少一年以上。
|
||||
|
||||
|
@ -226,7 +177,7 @@ DNS 就属于第一种方式,但是并不是典型的代理模式。
|
|||
- 整个系统的设计变得更加困难。
|
||||
- 引入了分布式系统的复杂性。
|
||||
|
||||
### 12.5.4 插件模式(Plug-in)
|
||||
### 12.5.3 插件模式(Plug-in)
|
||||
|
||||
#### 架构说明
|
||||
|
||||
|
@ -240,16 +191,16 @@ DNS 就属于第一种方式,但是并不是典型的代理模式。
|
|||
|
||||
更严格地说,硬盘、鼠标、键盘都不是核心系统,也算作插件。
|
||||
|
||||
见图 12.5.4。
|
||||
见图 12.5.3。
|
||||
|
||||
<img src='img/Slide17.svg'>
|
||||
<img src='img/Slide16.svg'>
|
||||
|
||||
|
||||
#### 应用场景
|
||||
|
||||
很多允许第三方进行扩展的软件都设计成插件模式。这种插件,可以按功能设计,也可以都是同一个功能但是服务于不同的地区(因为各个地区的应用场景不同)。
|
||||
很多允许第三方进行扩展的软件都设计成插件模式。这种插件,可以按功能设计,也可以都是同一个功能但是服务于不同类型的用户(因为应用场景不同)。
|
||||
|
||||
我们一起看一下 VSCode 组成结构,就是一个典型的插件模式,见图 12.5.4 右侧。
|
||||
我们一起看一下 VSCode 组成结构,就是一个典型的插件模式,见图 12.5.3 右侧。
|
||||
|
||||
VSCode 是基于 Electron 构建的,主要由三部分构成:
|
||||
|
||||
|
@ -292,7 +243,7 @@ while(True):
|
|||
process(event) # 处理事件
|
||||
```
|
||||
|
||||
这是用一个 while(True) 写的“死循环”,它不断地检测每个设备上有没有新的事件到来,如果有,就进行相应的处理。为了不让 CPU 持续运转,在每次循环开始都要休眠 10 毫秒,否则 CPU 会被 100% 占用。
|
||||
这是用一个 ```while(True)``` 写的“死循环”,它不断地检测每个设备上有没有新的事件到来,如果有,就进行相应的处理。为了不让 CPU 持续运转,在每次循环开始都要休眠 10 毫秒,否则 CPU 会被 100% 占用。
|
||||
|
||||
这种模式常用于通信场景中,因为通信板卡都是以事件的形式把各种通信事件发送给应用程序,比如响铃、摘机、按键、挂机等。而应用程序处理这些事件也是非常地迅速,向设备发送一个命令后,就可以异步地立刻返回而无需等待,基本可以在毫秒级返回。
|
||||
|
|
@ -26,7 +26,7 @@ MVC 模式通过将内部信息表示、用户信息呈现以及用户操作接
|
|||
|
||||
见图 12.6.1。
|
||||
|
||||
<img src='img/Slide18.svg'>
|
||||
<img src='img/Slide17.svg'>
|
||||
|
||||
图 12.6.1 MVC 架构
|
||||
|
||||
|
@ -34,7 +34,7 @@ MVC 模式通过将内部信息表示、用户信息呈现以及用户操作接
|
|||
|
||||
- 游戏主机是控制器 C。注意这里的“控制”不是指用户的操作,而是对各个零部件的控制。它接收手柄的操作信号,把模型中指定的视图播放到显示器上。
|
||||
- 游戏卡是模型 M,封装了游戏的数据和逻辑,和其它两个部件完全无关,可以任意更换游戏卡,只要接口一致即可。它决定显示器上的图像。
|
||||
- 游戏手柄和显示器是视图 V。注意,手柄是用户交互设备,所以属于视图,而不是属于控制器。
|
||||
- 游戏手柄和显示器是视图 V。注意,手柄是用户交互设备,相当于鼠标键盘,所以属于视图,而不是属于控制器。如果屏幕是可以触摸的话,也可以直接用手操作屏幕。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
|
@ -88,7 +88,7 @@ MVC使开发和维护用户接口的技术含量降低。使用MVC模式使开
|
|||
|
||||
见图 12.6.2。
|
||||
|
||||
<img src='img/Slide19.svg'>
|
||||
<img src='img/Slide18.svg'>
|
||||
|
||||
图 12.6.2 反馈模式
|
||||
|
||||
|
@ -142,3 +142,51 @@ MVC使开发和维护用户接口的技术含量降低。使用MVC模式使开
|
|||
- 反馈信号需要妥善处理,反馈力度太强会引起系统崩溃,太弱则不起作用。
|
||||
- 因为要正向、反向反复运行,所以运行性能较低。
|
||||
|
||||
### 12.6.3 事件驱动模式 (Event-Driven)
|
||||
|
||||
#### 架构说明
|
||||
|
||||
事件驱动模式应用于事件处理,主要由四个组件构成:事件源(event source),事件侦听者(event listener),通道(Channel)以及事件总线(event bus)。 事件源将消息发布到总线的特定通道,侦听者订阅相应的通道,事件源所发布的消息经通道通告给订阅通道的侦听者。
|
||||
|
||||
见图 12.6.3 拓扑结构。
|
||||
|
||||
<img src='img/Slide19.svg'>
|
||||
|
||||
图 12.6.3 事件驱动模式
|
||||
|
||||
一个事件源一般只发送消息到一个通道中,该通道一般由消息队列实现。而监听者根据业务需求可以监听一个或多个通道内的消息,比如图 12.5.2 中的监听者 1 就同时监听通道 1 和通道 2 的消息,此时,事件源 2 要发送两个消息拷贝到通道 2 中。
|
||||
|
||||
发布-订阅模式是事件驱动模式的一个子集。单独此模式的拓扑结构,感觉它是一个树形,但是在实际的应用中,参与通信的角色既是事件源,又是监听者。作为监听者接收事件后进行处理,然后发送新的事件到消息队列中,由下一级的监听者处理,最后绕了一圈儿又回来了,形成一个闭环。
|
||||
|
||||
#### 应用场景
|
||||
|
||||
这里用一个网络购物的应用常见来说明事件驱动的应用方式,见图 12.5.2 右侧订单服务实力。
|
||||
|
||||
1. 一个网络购物的用户下了订单,应用程序会发送“订单事件”到事件总线;
|
||||
2. “订单处理”模块会得到“订单处理通知”,处理订单,比如核对存货量、配送地、付款信息等;
|
||||
3. 处理完毕后发送“订单确认事件”到事件总线;
|
||||
4. “订单确认”模块会得到“订单确认通知”,给用户发送确认短信或使用其他通信手段;
|
||||
5. 发送消息给用户;
|
||||
6. 发送完毕后发送“通知成功事件”到事件总线;
|
||||
7. “订单执行”模块会得到“订单配送通知”;
|
||||
8. 商家配送,送货上门,用户签收;
|
||||
9. 配送完成后发送“订单完成事件”到事件总线。
|
||||
10. 系统发送“用户评价通知”然后关闭该订单(未画出)。
|
||||
|
||||
这种应用场景相当复杂,如果用串行系统架构种的管道-过滤器模式的话,所有操作都要做成同步的,对于网络购物这种大访问量的应用几乎是不可能的。所以,这种“重要不紧急”的场景很适合于使用事件驱动模式。另外,这个场景中的所有参与者基本上都既是事件源又是监听者。
|
||||
|
||||
#### 优缺点
|
||||
|
||||
优点:
|
||||
|
||||
- 分布式的异步架构,事件处理器之间高度解耦,软件的扩展性好。
|
||||
- 适用性广,各种类型的项目都可以用。
|
||||
- 性能较好,因为事件的异步本质,软件不易产生堵塞。
|
||||
- 事件处理器可以独立地加载和卸载,容易部署。
|
||||
|
||||
缺点:
|
||||
|
||||
- 涉及异步编程(要考虑远程通信、失去响应等情况),开发相对复杂。
|
||||
- 难以支持原子性操作,因为事件通过会涉及多个处理器,很难回滚。
|
||||
- 分布式和异步特性导致这个架构较难测试。
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
|
||||
## 13.7 康威定律
|
||||
|
||||
康威定律是一句格言,指出“**一个团队设计的系统来通常反映了他们自己的沟通(组织)结构**”。它以计算机程序员梅尔文·康威的名字命名,他于1967年提出了这个想法。原文是:
|
||||
康威定律是一句格言,指出“**一个团队设计的系统来通常反映了他们自己的组织沟通结构**”。它以计算机程序员梅尔文·康威的名字命名,他于1967年提出了这个想法。原文是:
|
||||
|
||||
**Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.**
|
||||
|
||||
笔者最初也是不明白康先生在讲什么,直到在写作的时候分析了微软 Cortana 的产品战略和系统架构之后,才恍然大悟。
|
||||
笔者最初也是不明白康先生在讲什么,直到在写作的时候分析了微软 Cortana 的产品战略和系统架构之后,才恍然大悟。意思是说:假设一个组织内部有三部分人来共同完成一个项目(不一定是软件系统),那么系统设计中起码会有三个组成部分,因为每个组成部分是由三部分人之中的一部分来完成的。
|
||||
|
||||
|
||||
见图 13.7.1。
|
||||
|
||||
|
@ -58,7 +59,13 @@
|
|||
|
||||
其中,第一点的代码位置限制了 Cortana 的技能的快速扩展,因为第三方根本不可能加入到这个生态系统中来。
|
||||
|
||||
当年笔者看到了 Cortana 的架构后,不禁心里发紧,深知这是 Windows Team 的一贯作风 —— 这就是**康威定律**的具体体现:Windows Team 本身就是一个封闭的组织,很多人在里面工作了十几年,所以设计出来的产品也是封闭的。就算投入几百人做 Cortana 技能,但是如果这些人不是服务领域的专家,也不会做出什么好的服务体验的,这就根本不能和亚马逊的“全民皆兵”的模式媲美。
|
||||
当年笔者看到了 Cortana 的架构后,不禁心里发紧,深知这是 Windows Team 的一贯作风 —— 这就是**康威定律**的具体体现:
|
||||
|
||||
1. Windows Team 本身就是一个封闭的组织,很多人在里面工作了十几年,所以设计出来的产品也是封闭的。
|
||||
2. Windows 向来不依赖外部,但是这次需要有语音处理分析这些事情,Windows 自己搞不定,只能求助于 Bing。
|
||||
3. 就算投入几百人做 Cortana 技能,但是如果这些人不是服务领域的专家,也不会做出什么好的服务体验的,这就根本不能和亚马逊的“全民皆兵”的模式媲美。
|
||||
|
||||
除此之外,还有一个天然的缺陷,就是 Cortana 被设定在“办公助理”的位置,那么谁会在办公室里对着 Cortana 大喊要做什么做什么呢?岂不是暴露了自己正在上班摸鱼的秘密?
|
||||
|
||||
#### 13.7.3 假设 Cortana 有一个开放架构...
|
||||
|
После Ширина: | Высота: | Размер: 55 KiB |
До Ширина: | Высота: | Размер: 61 KiB После Ширина: | Высота: | Размер: 61 KiB |
До Ширина: | Высота: | Размер: 75 KiB После Ширина: | Высота: | Размер: 75 KiB |
До Ширина: | Высота: | Размер: 65 KiB После Ширина: | Высота: | Размер: 65 KiB |
После Ширина: | Высота: | Размер: 52 KiB |
До Ширина: | Высота: | Размер: 60 KiB После Ширина: | Высота: | Размер: 60 KiB |
До Ширина: | Высота: | Размер: 80 KiB После Ширина: | Высота: | Размер: 78 KiB |
До Ширина: | Высота: | Размер: 49 KiB После Ширина: | Высота: | Размер: 49 KiB |
До Ширина: | Высота: | Размер: 79 KiB После Ширина: | Высота: | Размер: 79 KiB |
До Ширина: | Высота: | Размер: 55 KiB После Ширина: | Высота: | Размер: 55 KiB |
До Ширина: | Высота: | Размер: 63 KiB После Ширина: | Высота: | Размер: 71 KiB |
До Ширина: | Высота: | Размер: 43 KiB После Ширина: | Высота: | Размер: 43 KiB |
До Ширина: | Высота: | Размер: 72 KiB После Ширина: | Высота: | Размер: 72 KiB |
После Ширина: | Высота: | Размер: 75 KiB |
До Ширина: | Высота: | Размер: 71 KiB После Ширина: | Высота: | Размер: 83 KiB |
После Ширина: | Высота: | Размер: 86 KiB |
После Ширина: | Высота: | Размер: 89 KiB |
После Ширина: | Высота: | Размер: 108 KiB |
До Ширина: | Высота: | Размер: 78 KiB После Ширина: | Высота: | Размер: 78 KiB |
До Ширина: | Высота: | Размер: 94 KiB После Ширина: | Высота: | Размер: 94 KiB |
|
@ -1,6 +0,0 @@
|
|||
|
||||
|
||||
## 12.2 业务架构
|
||||
|
||||
数字化学校系统
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
|
||||
业务架构
|
||||
|
||||
逻辑架构
|
||||
|
||||
运行架构
|
||||
|
||||
开发架构
|
||||
|
||||
物理架构
|
||||
|
||||
数据架构
|
||||
|
||||
|
||||
||RUP 4+1|UML|TOGAF|Other|$\rightarrow$|统一名称|
|
||||
|-|-|-|-|-|-|-|
|
||||
|1|场景视图|用户模型|业务架构|逻辑架构|$\rightarrow$|业务场景架构|
|
||||
|2|逻辑视图|结构模型|应用架构|逻辑架构|$\rightarrow$|逻辑结构架构|
|
||||
|3|过程视图|行为模型|应用架构|运行架构|$\rightarrow$|行为过程架构|
|
||||
|4|开发视图|实现模型|技术架构|开发架构|$\rightarrow$|开发架构|
|
||||
|5|物理视图|环境模型|技术架构|物理架构|$\rightarrow$|物理环境架构|
|
||||
|6|N/A|N/A|数据架构|数据架构|$\rightarrow$|数据存储架构|
|
|
@ -1,254 +0,0 @@
|
|||
## 12.2 与架构相关的概念
|
||||
|
||||
### 12.2.1 基本概念
|
||||
|
||||
先澄清一些容易混淆的基本概念,在网络上这些概念鱼龙混杂,让读者不明所以。
|
||||
|
||||
#### 1. 架构、架构师、构建、框架、结构
|
||||
|
||||
**架构师**(architect)使用**架构**模式**构建**(construct)了一个**框架**(framework),用于约束那些开发者们只能使用规定的**结构**(structure)来进行二次开发。
|
||||
|
||||
#### 2. 架构设计、总体设计、系统设计、概要设计、详细设计
|
||||
|
||||
- 在大型系统中,需求分析结束后,首先要做的是**架构设计**。由架构师来负责。**架构设计**比**概要设计**更高层、更抽象。
|
||||
- 对于中小型项目,架构设计可以不做,而是直接做**概要设计**,在概要设计里用**总体设计**这一名词来代表**架构设计**。由技术专家来负责。
|
||||
- **系统设计**如果是应用于软件领域,则其含义 = 架构设计 + 概要设计 + 详细设计。
|
||||
- 在小型项目中,可以用概要设计代替架构设计和详细设计,只不过要求概要设计写得稍微细一些,上可以涵盖架构设计的内容,下可以涵盖详细设计的内容。
|
||||
|
||||
所以,总的来说软件的系统设计顺序为:架构设计$\rightarrow$概要设计$\rightarrow$详细设计,总结为表 12.2.1。
|
||||
|
||||
表 12.2.1 几种“设计”概念的比较
|
||||
|
||||
||架构设计|概要设计|详细设计|
|
||||
|-|-|-|-|
|
||||
|负责人|架构师|技术专家|开发人员|
|
||||
|内容|描述系统层次结构、各<br>层包含的重要的模块、<br>各层之间的接口和通讯<br>方式、大模块的物理部<br>署等。|描述出系统分为哪些功<br>能模块,每个模块又可<br>以分为哪些子模块每个<br>模块的主要功能是什么。|针对各个模块进行的,<br>包括算法、流程、状<br>态、数据等等,必须符<br>合概要设计的约束,如<br>功能约定、接口约定。|
|
||||
|用途|确定轮廓,合理规划|理清脉络,层次分解|确定细节,编写代码|
|
||||
|输出文档|架构设计说明书|概要设计说明书|详细设计说明书|
|
||||
|
||||
无论那种级别的设计,都应该包含:结构、功能、数据三个要素。
|
||||
|
||||
#### 3. 架构、框架、设计模式
|
||||
|
||||
见图 12.2.1。
|
||||
|
||||
<img src="img/Slide7.SVG"/>
|
||||
|
||||
图 12.2.1 架构、框架、设计模式之间的关系
|
||||
|
||||
**(1)架构**
|
||||
|
||||
软件架构指软件系统的顶层结构。简单的说架构就是一个蓝图,是一种设计方案,将客户的需求抽象成为组件,并且能够描述这些抽象组件之间的通信和调用。
|
||||
|
||||
比如,三层架构、事件总线架构等,它定义了系统的总体结构。
|
||||
|
||||
**(2)框架**
|
||||
|
||||
软件框架是项目软件开发过程中提取特定领域软件的共性部分形成的体系结构,不同领域的软件项目有着不同的框架类型。框架不是现成可用的应用系统,而是一个半成品,提供了诸多服务,开发人员在框架内部进行二次开发,实现具体功能的应用系统。框架是代码,是工具,不是知识。
|
||||
|
||||
比如 .Net Framework 是开发框架,PyTorch 是深度学习框架。一旦使用了某个框架,就会有一定的粘性,想迁移到其它框架上会有困难。比如使用 PyTorch 搭建好神经网络模型后,如果想迁移到 Tensor Flow(另外一个深度学习框架)上,需要重新学习语法、重新搭建、重新训练。
|
||||
|
||||
【最佳实践】
|
||||
|
||||
不要轻易地决定是否使用框架,一旦投入,就和框架绑定了,需要学习理解框架的底层逻辑,代码结构要遵守框架的约定,框架的 bug 也会让项目陷入泥潭。
|
||||
|
||||
笔者曾接手 Windows Phone 10 上的新浪微博的开发,上一个开发者采用了一个框架,用于处理用户交互事件,第一印象是:本来很直接的一件事,非得要通过框架绕一个弯儿。而笔者是第一批在 Windows Phone 10 上开发的先驱,对整个开发体系非常清楚,自然看不上这么一个奇奇怪怪的不知名的框架,于是不费什么力气,就摘掉了这个框架。意想不到的是,发布出去以后,用户反映整个应用的响应速度比以前快了,软件运行流畅了很多。
|
||||
|
||||
【最佳实践】
|
||||
|
||||
不要为了一个项目轻易决定自己写框架,原因如下:
|
||||
|
||||
- 与业务逻辑耦合
|
||||
|
||||
如果能够直接使用现有框架是最好的。但是如果项目有特殊需求,也不要轻易决定自己写框架,因为很难把具体的业务与框架分开,也就是说无法从一个特殊需求抽象出来一个框架。
|
||||
|
||||
- 没人用
|
||||
|
||||
写框架的目的是想以后重用,或者自己重用,或者让别人重用。但是大概率是没有人用的,除非这个框架可以解决通用问题。
|
||||
|
||||
在 5.3 节中讲到的强化学习资源优化平台(MARO),基本上没有可重用的可能,因为应用场景都有很大的差异;量化交易平台(Qlib)的应用场景相对比较明确,所以会有不少用户来尝试使用。
|
||||
|
||||
**(3)设计模式**
|
||||
|
||||
设计模式是一套被反复使用、广为人知的、经过分类的代码设计经验的总结,它强调的是一个设计问题的解决方法。设计模式是知识,是概念,不是代码。
|
||||
|
||||
稍微有些底层软件开发经验的读者,都知道有约 23 种软件设计模式,比如工厂模式、适配器模式、策略模式等等,目的是保证代码的可重用性、可读性、可靠性。而且还有 7 种设计原则,是上述的 23 种 设计模式要遵守的基本规则。
|
||||
|
||||
在上层的架构设计上,同样有设计原则和设计模式,一个合格的架构师就是根据这些原则和模式来工作的。比如在设计模式中有观察者(订阅-发布)模式,在架构模式中有事件总线模式,其工作原理是相同的。
|
||||
|
||||
|
||||
**(4)三者的比较**
|
||||
|
||||
所以,从概念大小和开发顺序上看,架构 $\gt$ 框架 $\gt$ 设计模式。
|
||||
|
||||
- 首先架构应该是一个最大的概念,是最高层次的设计。所以在做一个项目的时候首先出来的应该是架构,是对整个问题的一个总体上的设计。可以根据已有架构模式衍生出具体的架构,或者自己设计出新的架构。一个架构设计中可能会用到多个框架和多个设计模式。
|
||||
|
||||
- 其次会考虑采用什么现有的框架或者自己实现框架来解决架构设计中的子系统、子任务。一个架构中,在不同的局部可能要采用不同框架,比如在靠近应用层的部分应该采用 MVC 框架,而靠近后台处理的部分应该采用事件总线框架。系统小的话,只使用一种框架也可以。框架是针对共性抽象出来的半成品,所以,如果开发者自己实现框架的话,基本上会和实例(实际的应用)代码混在一起,分不出哪个是框架,哪个是应用。
|
||||
|
||||
- 最后,在具体的功能模块实现时就需要用到设计模式,所以设计模式就是解决单一问题的设计思路和解决方法。
|
||||
|
||||
这三者的共同点都是解决软件开发中的问题而出现的,而且都会表现出来的就是“高内聚,低耦合”的理念,就是让我们的设计更面向对象化。所以我们要想做好一个项目,那么架构设计、框架选型、设计模式的使用,三者都是非常重要的。
|
||||
|
||||
简而言之:架构是大智慧,用来对软件设计进行分工;框架是半成品,加入代码来实现自己想要的功能;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
|
||||
|
||||
|
||||
### 12.2.2 架构设计方法
|
||||
|
||||
目前软件领域有几种主流的架构设计方法,见图 12.2.2。
|
||||
|
||||
<img src="img/Slide8.SVG"/>
|
||||
|
||||
图 12.2.2 主流的架构设计方法
|
||||
|
||||
#### 1. RUP 4+1
|
||||
|
||||
RUP,Rational Unified Process,即统一开发过程。
|
||||
|
||||
<img src="img/Slide9.SVG"/>
|
||||
|
||||
图 12.2.3 RUP 4+1 视图
|
||||
|
||||
0. 场景视图(Scenario View)
|
||||
|
||||
通过数量一些重要场景(更常见的是用例)进行无缝协同工作,我们为场景描述相应的脚本(对象之间和过程之间的交互序列)。
|
||||
|
||||
1. 逻辑视图(Logical View)
|
||||
|
||||
主要支持功能性需求,即系统应该为用户提供哪些服务。
|
||||
|
||||
2. 过程视图(Process View)
|
||||
|
||||
一些非功能性的需求,如性能和可用性。它解决并发性、分布性、系统完整性、容错性的问题,以及逻辑视图如何与过程结构配合在一起,在哪个控制线程上,对象的操作被实际执行。
|
||||
|
||||
3. 开发视图(Development View)
|
||||
|
||||
关注软件开发环境下实际模块的组织。软件打包成小的程序块(程序库或子系统),它们可以由一位或几位开发人员来开发。子系统可以组织成分层结构,每个层为上一层提供良好定义的接口。
|
||||
|
||||
4. 物理视图(Physical View)
|
||||
|
||||
主要描述硬件配置。在UML中通常被称为部署视图,它主要考虑如何把软件映射到硬件上。通常需要考虑到解决系统拓扑结构、系统安装和通信等问题。
|
||||
|
||||
#### 2. UML
|
||||
|
||||
UML,即Unified Model Language,统一建模语言。
|
||||
|
||||
<img src="img/Slide10.SVG"/>
|
||||
|
||||
图 12.2.4 UML 分析模型
|
||||
|
||||
1. 用户模型(Use Case View)
|
||||
|
||||
强调从用户的角度看到的或需要的系统功能,是被称为参与者的外部用户所能观察到的系统功能的模型图。用用例图描述。
|
||||
|
||||
2. 结构模型(Logic View)
|
||||
|
||||
展现系统的静态或结构组成及特征,也称为结构模型视图(Structural Model View)或静态视图(Static View)。用类图和对象描述。
|
||||
|
||||
3. 行为模型(Concurrent View)
|
||||
|
||||
体现了系统的动态或行为特征,也称为行为模型视图(Behavioral Model View)或动态视图(Dynamic View)。用状态图、时序图、协作图、活动图描述。
|
||||
|
||||
4. 实现模型(Component View)
|
||||
|
||||
体现了系统实现的结构和行为特征,也称为实现模型视图(Implementation Model View)。用组件图描述。
|
||||
|
||||
5. 环境模型(Deployment View)
|
||||
|
||||
体现了系统实现环境的结构和行为特征,也称为环境模型视图(Environment Model View)或物理视图(Physical View)。用配置图描述。
|
||||
|
||||
#### 3. TOGAF
|
||||
|
||||
TOGAF,即 The Open Group Architecture Framework,开放组织架构框架,是由 The Open Group 这个开放组织指定的企业发展架构的方法和工具(即框架)。
|
||||
|
||||
0. 组织架构
|
||||
|
||||
在 TOGAF 中不存在(所以图中为灰色),但却是客观存在的,由人或部门组成的公司管理层,不属于架构设计范畴。
|
||||
|
||||
1. 业务架构(Business Architecture)
|
||||
|
||||
业务战略、管理、组织和关键业务流程,也叫做俯视架构。
|
||||
|
||||
2. 应用架构(Application Architecture)
|
||||
|
||||
也叫做剖面架构、逻辑架构。
|
||||
|
||||
3. 数据架构(Data Architecture)
|
||||
|
||||
各类逻辑和物理数据资产以及数据管理资源的结构。
|
||||
|
||||
4. 技术架构(Technology Architecture)
|
||||
|
||||
支持上述架构的必要软硬件,包括基础设施、中间件、网络、部署。
|
||||
|
||||
如图 12.2.5 左侧所示。
|
||||
|
||||
<img src="img/Slide11.SVG"/>
|
||||
|
||||
图 12.2.5 TOGAF架构模型和其它流派
|
||||
|
||||
#### 4. 其它流派
|
||||
|
||||
如图 12.2.5 右侧所示。
|
||||
|
||||
1. 开发架构
|
||||
|
||||
开发架构则更关注程序包,不仅仅是我们自己写的程序,还包括应用程序依赖的SDK、第三方类库、中间价等。尤其是像目前主流的Java、.NET等依靠虚拟机的语言和平台,以及主流的基于数据库的应用,都会比较关注。和逻辑架构有紧密的关联。
|
||||
|
||||
2. 逻辑架构
|
||||
|
||||
逻辑架构关注的是功能,包含用户直接可见的功能,还有系统中隐含的功能。或者更加通俗来描述,逻辑架构更偏向我们日常所理解的“分层”,把一个项目分为“表示层、业务逻辑层、数据访问层”这样经典的“三层架构”。
|
||||
|
||||
3. 运行架构
|
||||
|
||||
顾名思义,更关注的是应用程序运行中可能出现的一些问题。例如并发带来的问题,比较常见的“线程同步”问题、死锁问题、对象创建和销毁(生命周期管理)问题等等。开发架构,更关注的是飞机起飞之前的一些准备工作,在静止状态下就能规划好做好的,而运行架构,更多考虑的是飞机起飞之后可能发生的一些问题。
|
||||
|
||||
4. 数据架构
|
||||
|
||||
数据架构,更关注的是数据持久化和存储层面的问题,也可能会包括数据的分布、复制、同步等问题。更贴切来讲,如何选择需要的关系型数据库、流行的 NoSql,如何保障数据存储层面的性能、高可用性、灾备等等。很多时候,和物理架构是有紧密联系的,但它更关注数据存储层面的,物理架构更关注整个基础设施部署层面。
|
||||
|
||||
5. 物理架构
|
||||
|
||||
物理架构,更关注的系统、网络、服务器等基础设施。例如:如何通过服务器部署和配置网络环境,来实现应用程序的“可伸缩性、高可用性”。或者举一个实际的例子,如何通过设计基础设施的架构,来保障网站能支持同时 10 万人在线、7*24 小时提供服务,当超过 10 万人或者低于 10 万人在线时,可以很方便的调整部署架构来支撑。
|
||||
|
||||
|
||||
表 四种架构体系的统一
|
||||
|
||||
||RUP 4+1|UML|TOGAF|Other|$\rightarrow$|统一名称|
|
||||
|-|-|-|-|-|-|-|
|
||||
|1|场景视图|用户模型|业务架构|逻辑架构|$\rightarrow$|业务架构|
|
||||
|2|逻辑视图|结构模型|应用架构|逻辑架构|$\rightarrow$|逻辑架构|
|
||||
|3|过程视图|行为模型|应用架构|运行架构|$\rightarrow$|运行架构|
|
||||
|4|开发视图|实现模型|技术架构|开发架构|$\rightarrow$|开发架构|
|
||||
|5|物理视图|环境模型|技术架构|物理架构|$\rightarrow$|物理架构|
|
||||
|6|N/A|N/A|数据架构|数据架构|$\rightarrow$|数据架构|
|
||||
|
||||
|
||||
### 架构设计方法
|
||||
|
||||
最开始只有逻辑架构和物理架构
|
||||
|
||||
|
||||
逻辑架构 = 模块划分 + 接口定义 + 领域模型
|
||||
|
||||
运行架构 = 技术选型 + 控制流划分 + 同步关系
|
||||
|
||||
开发架构 = 技术选型 + 文件划分 + 编译关系
|
||||
|
||||
物理架构 = 硬件分布 + 软件部署 + 方案优化
|
||||
|
||||
数据架构 = 技术选型 + 存储格式 + 数据分布
|
||||
|
||||
|
||||
|
||||
大型系统
|
||||
|
||||
架构设计
|
||||
子系统设计
|
||||
|
||||
中型系统
|
||||
|
||||
系统设计
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
https://blog.csdn.net/hguisu/article/details/78259898
|
||||
|
||||
|
||||
具体的设计实例:
|
||||
- 高并发的设计
|
||||
- 异地灾备的设计
|
||||
- 消息队列的设计
|
||||
- 微信系统的设计(点对点通信)
|
||||
- 微博系统的设计(消息订阅,发布)
|
||||
- 淘宝系统的设计
|
||||
- 异地访问延迟
|
||||
- 高性能
|
||||
- 微服务
|
||||
- 语音系统的设计
|
||||
- 地图系统的设计
|
||||
|
||||
https://blog.csdn.net/danpu0978/article/details/107274524
|
||||
|
||||
|
||||
|
||||
Which architectural pattern is best for a given situation depends on
|
||||
which requirements have the highest priority, such as:
|
||||
– Maintainability: how easy or difficult is it to add an additional pro59
|
||||
Open Universiteit Software Architecture
|
||||
cessing component, for instance to filter certain words? How easy
|
||||
or difficult is it to change the input format, by adding line numbers,
|
||||
for example? In the Pipe-Filter pattern for instance, adding a filter is
|
||||
very easy, but changing the input format might be hard.
|
||||
– Reusability: can individual components be reused in other systems?
|
||||
In this case, the Pipe-and-Filter pattern enhances reusability because
|
||||
of the uniform data format that is used.
|
||||
– Performance: is the response time small enough? Is overall resource
|
||||
behaviour (memory usage in this example) acceptable? Patterns that
|
||||
make use of parallelism, such as the Pipe-Filter pattern and the EventBus pattern, will have better performance. On the other hand, starting a complex system like the Event Bus system, or transforming
|
||||
data in every filter using a different data structure, may lower performance.
|
||||
– Explicitness: is it possible to provide feedback to the user? Per stage?
|
||||
This is not possible in the Pipe-Filter pattern, for instance.
|
||||
– Fault tolerance: for the KWIC example, there is no difference between
|
||||
the different solutions, but fault-tolerance would have been enhanced
|
||||
if a Master-slave pattern been applied.
|
||||
The list of requirements and their priorities will vary for every system.
|
||||
No rigid guidelines are available to tell you which pattern will be the
|
||||
best in every case. Much also depends on the implementation of the
|
||||
pattern. Independent processes, for example, may be implemented using threads or using processes on different machines. The balance between communication and computation, the capacity of the processors
|
||||
involved and the speed of communication between machines, among
|
||||
other things, will decide which implementation will have the best performance
|
|
@ -1,45 +0,0 @@
|
|||
设计三大原则
|
||||
|
||||
https://cloud.tencent.com/developer/article/1372372
|
||||
|
||||
|
||||
### 架构设计的三个原则
|
||||
分别是合适原则、简单原则、演化原则
|
||||
|
||||
4.1 合适原则
|
||||
“合适优于业界领先”
|
||||
|
||||
根据团队人数、团队人员的技术水平选用难度合适的技术
|
||||
|
||||
根据业务场景选择合适的技术
|
||||
|
||||
4.2 简单原则
|
||||
“简单优于复杂”
|
||||
|
||||
软件领域的复杂性体现在两个方面:
|
||||
|
||||
结构的复杂性
|
||||
结构复杂的系统的特点:组成复杂系统的组件数量更多,同时这些组件之间的关系也更加复杂。
|
||||
|
||||
2.逻辑的复杂性
|
||||
|
||||
如果想降低结构复杂性,可以降低组件数量,将功能和逻辑聚在一个组件上,但是这带来了新问题:某个组件的逻辑太复杂,一样会带来各种问题。
|
||||
|
||||
复杂的电路意味更强大的功能,而复杂的架构却有很多问题的根本原因在于电路一旦设计好后进入生产,就不会再变,复杂性只是在设计时带来影响;
|
||||
而一个软件系统在投入使用后,后续还有源源不断的需求要实现,因此要不断地修改系统,复杂性在整个系统生命周期中都有很大影响。
|
||||
|
||||
4.3 演化原则
|
||||
“演化优于一步到位”
|
||||
|
||||
建筑一旦完成(甚至一旦开建)就不可再变,而软件却需要根据业务的发展不断地变化,因此需要进行演化,一开始业务没那么复杂,性能要求没那么高,就没必要做得太复杂,建议如下:
|
||||
|
||||
首先,设计出来的架构要满足当时的业务需要。
|
||||
其次,架构要不断地在实际应用过程中迭代,保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计,使得架构逐渐完善。
|
||||
第三,当业务发生变化时,架构要扩展、重构,甚至重写;代码也许会重写,但有价值的经验、教训、逻辑、设计等(类似生物体内的基因)却可以在新架构中延续。
|
||||
4.4 总结
|
||||
三个原则其实是一体的,核心就是因为软件系统的复杂就意味着问题,变化才是主题,因此”适当“才是最好地应对变化的方式,而不是“过度”
|
||||
|
||||
合适原则第一考虑,优先满足业务需求;
|
||||
简单原则第二考虑,挑选简单方案快速落地验证;
|
||||
演进原则第三考虑,适当预测业务发展,感觉预测不准就不预测,等真的出现问题的时候演进即可
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
|
||||
<img src="img/Slide1.SVG"/>
|
||||
|
||||
架构,源于中国古代建筑术语。架 = 加 + 木,构 = 木 + 勾,把一堆木头加起来并互相勾住,以形成一个完整的、牢固的建筑。引申到软件产品概念上,就是:
|
||||
|
||||
1. 结构切分:把一个软件项目整体切分成不同的部分;
|
||||
2. 功能定义:定义这些部分各自完成的局部功能;
|
||||
3. 关系建立:建立这些部分相互沟通的机制,使之结合为一个整体,并完成这个整体所需要的所有功能。
|
||||
|
||||
有四种常用的架构方法:
|
||||
|
||||
- RUP(Rational Unified Process)统一开发过程
|
||||
- UML(Unified Model Language)统一建模语言
|
||||
- TOGAF(The Open Group Architecture Framework)开发组织定义的架构框架
|
||||
- 其它流派
|
||||
|
||||
在本章中,主要讨论技术架构。
|
||||
|
||||
|
||||
<img src="img/Slide2.SVG"/>
|
||||
|
||||
|
||||
https://blog.csdn.net/lfsf802/article/details/8487990
|
||||
|
||||
https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013
|
||||
|
||||
https://zhuanlan.zhihu.com/p/41395345
|
||||
|
||||
https://blog.csdn.net/Jayphone17/article/details/103651076
|
||||
|
||||
https://zhuanlan.zhihu.com/p/41395345
|
||||
|
||||
https://www.ou.nl/documents/40554/791670/IM0203_03.pdf/30dae517-691e-b3c7-22ed-a55ad27726d6
|
||||
|
||||
https://towardsdatascience.com/10-common-software-architectural-patterns-in-a-nutshell-a0b47a1e9013
|
||||
|
||||
https://max.book118.com/html/2016/1115/63079375.shtm
|
||||
|
||||
https://blog.csdn.net/hguisu/article/details/78259898
|
||||
|
||||
|
||||
https://help.aliyun.com/document_detail/207135.html
|
|
@ -1,71 +0,0 @@
|
|||
|
||||
|
||||
### 12.2 技术架构模式
|
||||
|
||||
**架构模式**是在给定上下文中对软件构建中常见问题的通用、可重用解决方案。架构模式类似于软件设计模式,但层次更高、范围更广。架构对应着架构模式;框架本身就是个模式,只不过包含基础功能代码;而设计对应着设计模式。
|
||||
|
||||
所谓技术架构模式,就是面向实现的架构设计,用于指导后面的概要设计。在本章中主要讨论这种架构模式,其它的如逻辑、过程、数据等等的架构,在后面的章节讨论。
|
||||
|
||||
一些基本的技术架构模式如下:
|
||||
|
||||
一个复杂系统的架构是由以上这些架构模式有机组合而成。
|
||||
|
||||
|
||||
|
||||
|
||||
#### 星形模式
|
||||
|
||||
- 客户端-服务器模式(Client-server pattern)
|
||||
- 浏览器-服务器模式
|
||||
- 对等模式(Peer-to-peer pattern)
|
||||
- 主-从模式(Master-slave pattern)
|
||||
|
||||
#### 串行模式
|
||||
|
||||
- 分层模式(Layered pattern)
|
||||
- 管道-过滤器模式(Pipe-filter pattern)
|
||||
|
||||
#### 树形模式
|
||||
|
||||
- 代理模式(Broker pattern)
|
||||
- 事件总线模式(Event-bus pattern)
|
||||
- 微服务模式
|
||||
- 插件模式
|
||||
|
||||
#### 环形模式
|
||||
|
||||
- 模型-视图-控制器模式(Model-view-controller pattern)
|
||||
- 反馈模式
|
||||
|
||||
|
||||
|
||||
#### 一览
|
||||
|
||||
表 12.2.1 技术架构模式一览表
|
||||
|
||||
|名称|描述|优点|缺点|综合|
|
||||
|-|-|-|-|-|
|
||||
|客户端-服务器模式||||灵活性:低<br>发布易用性:低<br>可测试性:高<br>性能:低<br>可扩展性:低<br>开发容易度:高|
|
||||
|浏览器-服务器模式|||||
|
||||
|对等模式|
|
||||
|主-从模式|
|
||||
|分层模式|
|
||||
|管道-过滤器模式|
|
||||
|代理模式|
|
||||
|事件驱动模式||||灵活性:高<br>发布易用性:高<br>可测试性:低<br>性能:高<br>可扩展性:高<br>开发容易度:低|
|
||||
|微服务模式||||灵活性:高<br>发布易用性:高<br>可测试性:高<br>性能:低<br>可扩展性:高<br>开发容易度:高|
|
||||
|插件模式||||灵活性:高<br>发布易用性:高<br>可测试性:高<br>性能:高<br>可扩展性:低<br>开发容易度:低|
|
||||
|MVC 模式|
|
||||
|反馈模式|
|
||||
|
||||
|
||||
#### 其它模式
|
||||
|
||||
|
||||
- 黑板模式(Black Board)
|
||||
|
||||
黑板模式是一种常用的架构模式,应用中的多种不同数据处理逻辑相互影响和协同来完成数据分析处理。黑板模式允许多个消息读写者同时存在,消息的生产者和消费者完全分开。这就像一个黑板,任何一个教授(消息的生产者)都可以在其上书写消息,任何一个学生(消息的消费者)都可以从黑板上读取消息,两者在空间和时间上可以解耦,并且互不干扰。这种模式对于没有确定解决方案策略的问题是有用的。
|
||||
|
||||
- 解析器模式(Interpreter)
|
||||
- 空间模式
|
||||
- 六边形模式
|
До Ширина: | Высота: | Размер: 57 KiB |
До Ширина: | Высота: | Размер: 50 KiB |
До Ширина: | Высота: | Размер: 56 KiB |
До Ширина: | Высота: | Размер: 65 KiB |
До Ширина: | Высота: | Размер: 69 KiB |
До Ширина: | Высота: | Размер: 108 KiB |
|
@ -1,2 +0,0 @@
|
|||
|
||||
https://www.cnblogs.com/gaojingsong/p/13184296.html
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
|
||||
<img src="img/Slide1.SVG"/>
|
||||
|
||||
本章的标题是概要设计,但是在前面几个小节中讲述了大量的“架构设计”的知识。那么**概要设计**和**架构设计**之间的区别和联系是什么呢?
|
||||
|
||||
在本章中,首先会澄清一些关于“设计”的概念;然后木头会给读者讲一个故事,引入架构设计的概念;接下来介绍几个主流的架构设计方法供大家参考;当然笔者也会从中总结出自己认为较为合理的架构设计的任务、关注点以及最佳实践。接下来会把架构设计降维到概要设计,成为一系列的基本的设计任务,并形成概要设计说明书。最后会讲到如何实现设计的核心目标,如性能、可用性、可扩展性等等。
|
||||
|
||||
<img src="img/Slide2.SVG"/>
|
||||
|
||||
|
||||
#### 参考资料
|
||||
|
||||
- Architectural Blueprints—The “4+1” View Model of Software Architecture,Philippe Kruchten
|
||||
https://www.cs.ubc.ca/~gregor/teaching/papers/4+1view-architecture.pdf
|
||||
|
||||
- 京东应用架构设计,吴博
|
||||
https://www.slidestalk.com/u3813/Design_and_Governance_of_Jingdong_Application_Architecture
|
||||
|
||||
- 建筑设计理念与构想,文镜
|
||||
https://zhuanlan.zhihu.com/p/92154271
|
||||
|
||||
- The C4 Model for Software Architecture,Simon Brown
|
||||
https://www.infoq.com/articles/C4-architecture-model/
|
||||
https://c4model.com/
|
||||
|
||||
- The Art of Crafting Architectural Diagrams,Ionut Balosin
|
||||
https://www.infoq.com/articles/crafting-architectural-diagrams/
|
||||
|
||||
- The TOGAF Standard,Version 9.2
|
||||
https://pubs.opengroup.org/architecture/togaf9-doc/m/mobile-index.html
|
||||
|
||||
- 《软件架构设计》,温昱,电子工业出版社
|
||||
|
||||
- 《从零开始学架构》,李运华,电子工业出版社
|
||||
|
||||
- 《大型网址技术架构》,李智慧,电子工业出版社
|
|
@ -0,0 +1,205 @@
|
|||
## 13.1 设计的概念
|
||||
|
||||
本小节主要是澄清一些容易混淆的关于“设计”的基本概念,在网络上这些概念鱼龙混杂,让读者不明所以;而公司里的老板和同事们也是经常混用这些概念,让新手们一头雾水。
|
||||
|
||||
### 13.1.1 建筑系统的分解与设计
|
||||
|
||||
我们先用一个居民小区的设计与建设来做个比喻,见表 13.1.1。
|
||||
|
||||
表 13.1.1 居住小区的设计与建设
|
||||
|
||||
|设计任务|居民小区|每栋楼宇|每户人家|
|
||||
|:-:|-|-|-|
|
||||
|**总体规划**|周边配套设施|楼宇周边设计|户门之间的相互关系|
|
||||
|**静态设计**<br>(功能区与间隔)|院门设置,内部功能区划分,外部边界圈定|结构,外观,长宽高,位置,朝向,单元门划分|室内布局,空间形状,墙体,门窗采光通风|
|
||||
|**动态设计**<br>(交通与水电气)|主干道,支道,地下总管线|电梯楼梯,楼宇内主管线|室内管线,开关,行走动线,干湿分离|
|
||||
|**开发设计**<br>(运输与施工)|从北向南推进,先铺管线再铺路|打地基,浇筑,从下到上逐层搭建|砌墙,铺管线,墙顶地面找平,安装门窗,室内装修|
|
||||
|
||||
#### 1. 居民小区
|
||||
|
||||
这是一个大系统,需要总体规划设计,主要完成周边配套设施的规划,商场、学校、幼儿园、公园、体育设施等等。
|
||||
|
||||
在建筑行业**没有**静态设计、动态设计、开发设计的说法,笔者为了能和后面要讲的软件工程具有可比性,才引入了这个说法。对于小区来说,所谓静态设计,主要是功能区的划分;所谓动态设计,主要是人员车辆的流动,水电气的运行,生活垃圾清理等等;所谓开发设计,即施工方法,一般是从小区深处向小区门口推进,方便清理运输。
|
||||
|
||||
#### 2. 每栋楼宇
|
||||
|
||||
楼宇就可以定义为中型系统了。
|
||||
|
||||
对于每栋楼宇来说,它的总体规划会降级,关注楼宇与其它楼宇之间的距离是否影响采光,周边道路,停车场安排。
|
||||
|
||||
静态设计主要是建筑整体结构上的一些考虑,动态设计是主要是人员通行的考虑以及楼宇内总管线的设计,开发设计当然是先打地基、浇筑主框架,然后从下到上逐层搭建。
|
||||
|
||||
在施工的时候有个技巧,整栋楼是按层施工的,在每个单元之间并没有物理隔离,以便工人和建筑材料可以在整栋楼内来回流动,在最后阶段才会隔离单元。
|
||||
|
||||
#### 3. 每户人家
|
||||
|
||||
门户就是小型系统了。
|
||||
|
||||
它的总体规划会进一步降级,关注一梯三户之间户门的相互关系,比如户门位置是不是会影响开启,与电梯、楼梯之间的位置是否合适等等。
|
||||
|
||||
在室内设计上需要比较精细,主要是考虑人类日常生活中的一些习惯,布局合理,浪费面积小。
|
||||
|
||||
当然,在建筑学上还有很深的学问,笔者只是班门弄斧而已,只是想说明在不同的层次上面对不同规模的问题,设计的任务不同。
|
||||
|
||||
### 13.1.2 软件系统的分解与设计
|
||||
|
||||
#### 1. 软件系统规模定义
|
||||
|
||||
设计这个词本来是一个非常宽泛的概念,但是在软件工程领域,有其严格的定义,否则大家就无法基于同一概念来沟通了。如同 13.1.1 所说的建筑设计,**针对不同规模的系统,会有不同级别的设计方法**。所以,我们先考察一下软件系统的规模是如何界定的。
|
||||
|
||||
<img src="img/Slide3.SVG"/>
|
||||
|
||||
图 13.1.1 软件系统规模与设计的关系
|
||||
|
||||
在图 13.1.1 左侧,有一个粗略评价系统规模的表格,主要依据是投入的人力、花费的时间、最终的产出等三项。为什么说是粗略的呢?因为:
|
||||
|
||||
1. 代码复杂度:
|
||||
|
||||
- 以前的代码都是 C/C++ 代码,比较容易比较代码数量。但是现在有很多种编程语言,比如一段 Python 代码只用两行就调用了一个计算包,而用 C/C++ 要实现底层计算包。
|
||||
- Web 项目中的各种辅助数据,如 xml/html/css/JSON 等等,不容易界定是否属于代码。但笔者认为只要是程序员写出来的东西都算代码,包括注释行。
|
||||
- 有些是自动生成的代码,比如在使用 JAVA/.Net 框架时,都会产生这类代码,而且是和程序员写的代码位于同一个文件中,无法拆分。
|
||||
|
||||
2. 业务复杂度:面向企业的系统会比面向公众的系统复杂很多,因为需要实现一些特殊的工作流程。一个公众系统可以从简单到复杂慢慢进化,而企业系统必须一次性交付才能满足合同要求。
|
||||
|
||||
3. 人员复杂度:程序员的素质不同,团队合作的成熟度也有差异,对于团队规模和完成时间有很大的影响。
|
||||
|
||||
4. 技术复杂度:项目难度也需要考虑。比如一个项目中包含了若干高难度的算法或模型的研发和实现任务,所需要的时间也就相对较长。
|
||||
|
||||
5. 环境复杂度:软件运行环境如果比较复杂,有很多外部系统需要连接,那么接口设计与实现、开发过程中的调试、集成测试等环节都会比较耗时。
|
||||
|
||||
当然,除了大中小三种规模外,还会有超大型和微型。
|
||||
|
||||
#### 2. 分解与设计
|
||||
|
||||
如图 13.1.1 右侧所示:
|
||||
|
||||
1. 对于大型系统,需求分析结束后,需要**架构师**把它分解成若干个中型系统,此时需要**架构设计**来完成分解任务。
|
||||
|
||||
2. 对于中型系统,首先还是要进行需求分析,然后需要**技术专家**把它分解成若干个小型系统。此时,我们需要**概要设计**来完成分解任务,因为在概要设计中也包含有**架构设计**的工作内容,但是一般用**总体设计**这一名词来代表架构设计,以避免和大型系统中的架构设计混淆。
|
||||
|
||||
3. 对于小型系统,一般就不需要做需求分析了,我们需要**概要设计**和**详细设计**,最终达到能用代码实现的目的,这项工作由具体的**开发人员**来完成。
|
||||
|
||||
对于超大型系统,把它分解为中型系统即可;对于微型系统,只做详细设计即可。
|
||||
|
||||
所以,**在软件工程中,只有架构设计(Architecture Design)、概要设计(Preliminary Design)、详细设计(Detailed Design)三个设计概念**。
|
||||
|
||||
细数当今世界上的大型系统,国外有微软、谷歌、亚马逊的搜索、购物平台,国内有微信、微博、抖音、淘宝、京东、12306 等的电商、娱乐平台,总数不超过 50 个。而且这些大平台也都是一步步从小到大逐步扩展的。所以一般人很少有机会从事、参与大型系统的设计工作。既然大型系统很少,那么就很少有人立说著书来讲解架构设计,因为即使是经历过大型系统设计工作的人,也不能保证他/她的经验就是普遍适用的。最终的结果是:关于**架构设计的知识不成体系**。
|
||||
|
||||
|
||||
那么读者可能会有疑问:既然大型系统很少,我们为什么还要学习架构设计?原因如下:
|
||||
|
||||
1. 架构设计是一种抽象思维活动,学习相关知识可以让读者打开眼界,学会自上而下的思考过程,而不是只会自下而上。
|
||||
2. 架构设计方法论,是软件工程在大系统的架构层次上的具体实践,**这种方法论,在我们经常遇到的中小型系统中同样适用**。
|
||||
3. 从另外一个角度看,理论上概要设计是可以包括架构设计的(因为里面有总体设计部分的内容需要填写),所以在坊间一般只要求开发人员写概要设计和详细设计,非必要不写架构设计,除非系统特别复杂或者规模特别大。
|
||||
|
||||
总的来说**软件系统的设计顺序为:架构设计$\rightarrow$概要设计$\rightarrow$详细设计**,总结为表 13.1.2,但这不算完整,在 13.5 节中,我们还会继续扩展此表。
|
||||
|
||||
表 13.1.2 几种“设计”概念的比较
|
||||
|
||||
|系统设计|架构设计|概要设计|详细设计|
|
||||
|-|-|-|-|
|
||||
|系统规模|只应用于大型系统|主要应用于中型系统|主要应用于小型系统|
|
||||
|设计负责人|架构师|技术专家|开发人员|
|
||||
|设计对象|系统|子系统|模块|
|
||||
|设计内容|把系统分解为子系统,<br>说明子系统之间的关系,<br>找出共同依赖的基础,<br>并考虑运行时的质量。|把子系统按照一定的原则<br>分解为模块,分配每个模<br>块具体功能,并确定模块<br>间调用关系和接口。|确定模块的算法、流程、<br>状态转换、数据结构等详<br>细信息,便于后续开发。|
|
||||
|设计参考|架构模式|框架|设计模式|
|
||||
|用途|确定轮廓,层次分解|分解模块,理清关系|确定细节,编写代码|
|
||||
|输出文档|架构设计说明书|概要设计说明书|详细设计说明书|
|
||||
|
||||
【最佳实践】
|
||||
|
||||
对于小型系统,为了体现敏捷开发的原则,我们通常只写一个稍微详细些的概要设计,通过评审后就可以开始编码开发工作,而不比写详细设计。有些技术细节可以在开发阶段再解决,以 Scrum meeting 的方式向团队其它成员汇报,并补充到最开始写的概要设计中。
|
||||
#### 3. 其它几个概念
|
||||
|
||||
1. 系统
|
||||
|
||||
一堆**组件**依靠相互之间的**关系**组成一个有机的整体以实现完整的**功能**。所以,可以说:
|
||||
|
||||
$$
|
||||
系统 = 组成元素 + 结构关系 + 实现功能
|
||||
$$
|
||||
|
||||
在谈论一个系统的时候,我们通常用以上这三个要素来描述,缺一不可。比如:
|
||||
- 缺乏第一个要素:那么后两者也将不存在,比如一个真空世界。
|
||||
- 缺乏第二个要素:一堆零件零散地摆在那里只能成为废品,不能实现任何功能。
|
||||
- 缺乏第三个要素:汽车模型非常漂亮,有前两个元素,但是没有任何功能,仅供观赏,不能成为系统。
|
||||
|
||||
2. 系统与子系统
|
||||
|
||||
子系统是一种相对概念,对于大型系统来说,子系统是中型系统;对于中型系统来说,子系统是小型系统。以此类推。
|
||||
|
||||
3. 模块与组件
|
||||
|
||||
- 模块是从源代码层面对系统的拆分,它包含了完整的代码逻辑和数据结构,并提供对外的调用接口。它的主要作用是从业务功能角度进行职责分离。
|
||||
|
||||
- 组件是从可执行程序层面对系统的拆分,它可大可小,有用户界面(如 WinForm 中的窗体控件)或调用接口,并通常是可以编程的。使用者并不知道其实现的源代码是什么语言,它的主要作用从技术角度被跨系统的单元复用。
|
||||
|
||||
4. 系统设计(System Design)
|
||||
|
||||
针对大型、中型、小型软件系统的设计行为。但是具体的设计内容根据系统的规模不同而不同,可以理解为“系统设计 = 架构设计 + 概要设计 + 详细设计”。比如,老板对你说:“你把 xx 项目做一个系统设计,我们后面好展开。” 那就需要先评价 xx 项目的系统规模,然后再决定使用架构设计、概要设计还是详细设计。
|
||||
|
||||
5. 总体设计(Overal Design / General Design / High-Level Design / Outline Design)
|
||||
|
||||
在概要设计中,用于完成针对当前系统规模的架构设计工作的代名词。除了微型系统系统之外,其它规模的系统都可以有总体设计,也就是说“架构设计”这个词汇可以下沉到小型系统设计中,但是设计内容有所不同。
|
||||
|
||||
### 13.1.3 架构模式、框架、设计模式
|
||||
|
||||
在表 13.1.2 中列出了设计参考一项,我们现在来做个解释。
|
||||
|
||||
架构设计对应着架构模式;框架本身就是个模式,只不过包含基础功能代码,通常在概要设计中指定;而详细设计对应着设计模式。
|
||||
|
||||
从概念大小和开发顺序上看,架构模式 $\gt$ 框架 $\gt$ 设计模式。
|
||||
|
||||
这三者的共同点都是解决软件开发中的问题而出现的,而且都会表现出来的就是“高内聚,低耦合”的理念,就是让我们的设计更面向对象化。所以我们要想做好一个项目,那么架构设计、框架选型、设计模式的使用,三者都是非常重要的。
|
||||
|
||||
简而言之:架构是大智慧,用来对软件设计进行分工;框架是半成品,加入代码来实现自己想要的功能;设计模式是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度。
|
||||
|
||||
见图 13.1.2。
|
||||
|
||||
<img src="img/Slide4.SVG"/>
|
||||
|
||||
图 13.1.2 架构模式、框架、设计模式之间的关系
|
||||
|
||||
#### 1. 架构模式
|
||||
|
||||
首先架构应该是一个最大的概念,是最高层次的设计。所以在做一个项目的时候首先出来的应该是架构,是对整个问题的一个总体上的设计。可以根据已有架构模式衍生出具体的架构,或者自己设计出新的架构。一个架构设计中可能会用到多个框架。
|
||||
|
||||
在第十二章中给出了一些主流的技术架构模式,比如,三层架构、事件总线架构等,它定义了系统的总体结构。
|
||||
|
||||
#### 2. 框架
|
||||
|
||||
框架也是一种模式,但包含代码。
|
||||
|
||||
其次会考虑采用什么现有的框架或者自己实现框架来解决架构设计中的子系统、子任务。一个架构中,在不同的局部可能要采用不同框架,比如在靠近应用层的部分应该采用 MVC 框架,而靠近后台处理的部分应该采用事件总线框架。系统小的话,只使用一种框架也可以。框架是针对共性抽象出来的半成品,所以,如果开发者自己实现框架的话,基本上会和实例(实际的应用)代码混在一起,分不出哪个是框架,哪个是应用。
|
||||
|
||||
软件框架是项目软件开发过程中提取特定领域软件的共性部分形成的体系结构,不同领域的软件项目有着不同的框架类型。框架不是现成可用的应用系统,而是一个半成品,提供了诸多服务,开发人员在框架内部进行二次开发,实现具体功能的应用系统。框架是代码,是工具,不是知识。
|
||||
|
||||
比如 .Net Framework 是开发框架,PyTorch 是深度学习框架。一旦使用了某个框架,就会有一定的粘性,想迁移到其它框架上会有困难。比如使用 PyTorch 搭建好神经网络模型后,如果想迁移到 Tensor Flow(另外一个深度学习框架)上,需要重新学习语法、重新搭建、重新训练。
|
||||
|
||||
【最佳实践】
|
||||
|
||||
不要轻易地决定是否使用框架,一旦投入,就和框架绑定了,需要学习理解框架的底层逻辑,代码结构要遵守框架的约定,框架的 bug 也会让项目陷入泥潭。
|
||||
|
||||
笔者曾接手 Windows Phone 10 上的新浪微博的开发。上一个开发者采用了一个框架,用于处理用户交互事件,给笔者的第一印象是:本来很直接的一件事,非得要通过框架绕一个弯儿。而笔者是第一批在 Windows Phone 10 上开发的先驱,对整个开发体系非常清楚,自然看不上这么一个奇奇怪怪的不知名的框架,于是不费什么力气,就摘掉了这个框架。意想不到的是,发布出去以后,用户反映整个应用的响应速度比以前快了,软件运行流畅了很多。
|
||||
|
||||
【最佳实践】
|
||||
|
||||
不要为了一个项目轻易决定自己写框架,原因如下:
|
||||
|
||||
- 与业务逻辑耦合
|
||||
|
||||
如果能够直接使用现有框架是最好的。但是如果项目有特殊需求,也不要轻易决定自己写框架,因为很难把具体的业务与框架分开,也就是说无法从一个特殊需求抽象出来一个泛化的框架。
|
||||
|
||||
- 没人用,一次性劳动
|
||||
|
||||
写框架的目的是想以后重用,或者自己重用,或者让别人重用。但是大概率是没有人用的,除非这个框架可以解决通用问题。
|
||||
|
||||
在 5.3 节中讲到的强化学习资源优化平台(MARO),基本上没有可重用的可能,因为应用场景都有很大的差异;量化交易平台(Qlib)的应用场景相对比较明确,所以会有不少用户来尝试使用。
|
||||
|
||||
#### 3. 设计模式
|
||||
|
||||
最后,在具体的功能模块实现时就需要用到设计模式,所以设计模式就是解决单一问题的设计思路和解决方法,是一套被反复使用、广为人知的、经过分类的代码设计经验的总结,它强调的是一个设计问题的解决方法。设计模式是知识,是概念,不是代码。
|
||||
|
||||
稍微有些底层软件开发经验的读者,都知道有约 23 种软件设计模式,比如工厂模式、适配器模式、策略模式等等,目的是保证代码的可重用性、可读性、可靠性。而且还有 7 种设计原则,是上述的 23 种 设计模式要遵守的基本规则。
|
||||
|
||||
一般说到设计模式时,都是指这 23 种,但也并不是绝对的。在上层的架构设计上,同样有设计原则和设计模式,一个合格的架构师就是根据这些原则和模式来工作的。比如在设计模式中有观察者(订阅-发布)模式,在架构模式中有事件总线模式,其工作原理是相同的。
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
## 13.2 业务场景架构设计的故事
|
||||
|
||||
目前各个高校的数字化建设早已经非常完备了,而且大多数读者想必在上大学的时候都亲身体验过数字化系统带来的便利。所以,在本节中,木头带着大家回到十几年前的校园,来经历一遍建立数字化校园的过程。
|
||||
|
||||
数字化校园是一个大系统,所以必须要经历架构设计过程。
|
||||
|
||||
### 13.2.1 业务需求调研
|
||||
|
||||
业务需求调用的策略有两种:
|
||||
|
||||
#### 1. 自顶向下
|
||||
|
||||
给特定客户开发的产品,比如企业管理系统、智能校园系统等等,需要以客户的真实需求为导向,不能自己胡编乱造。这种情况下,按照客户的组织层级由上而下地进行需求调研是一个省时省力的方法。
|
||||
|
||||
在数字化校园系统中,先和校长及其它校领导谈顶层的业务需求(参见 7.4 节),然后再深入到中层获得用户需求,最后到基层获得功能需求。
|
||||
|
||||
#### 2. 自底向上
|
||||
|
||||
对于自主式的产品(即产品的 owner 不是客户,而是产品开发团队本身,自己开发并运营该产品,如微信、微博),他们需要直接从“民间”获得需求,因为他们的“客户”就是最终用户。
|
||||
|
||||
这种方式相对来说过程比较复杂,但是一般的面向公众的软件产品(如微软的各种产品)都是使用这种方式做需求调研。所以,我们假设下面的过程使用这种**自底向上**的方式完成的,以帮助读者顺利学习理解。
|
||||
|
||||
在第六章中,我们学习了软件工程的第一步:需求调研,其中讲了很多种方法,有观察、体验、访谈、问卷调查等到。于是,木头和需求调研小组首先在校长那里拿到了“尚方宝剑”,也就是校长的介绍信,然后拿着纸笔跑遍了学校的每个角落,大概花了两周的时间,粗略地记录了一些零散的需求点,如图 13.2.1 所示。
|
||||
|
||||
<img src="img/Slide5.SVG"/>
|
||||
|
||||
图 13.2.1 数字化校园系统业务需求调研
|
||||
|
||||
在这个阶段,调研者先不需要动什么脑筋,只需要带着耳朵去听就行,把所有的不知道有用没用的需求都收集到一起,用贴纸(或电子贴纸)方式把它们贴在白板上。
|
||||
|
||||
但是,需求调研小组的任务还没有结束,有两个原因:
|
||||
|
||||
1. 用户第一次描述需求,其实他们可能根本不知道什么是需求,什么该说,什么不该说;
|
||||
2. 需求调研小组的人也是第一次接触业务,如果用户说得太详细的话,需求小组的人也不一定能完全领会。
|
||||
|
||||
所以第一轮需求调研只是了解大概情况,后续还有更细的一轮功能调研。
|
||||
|
||||
### 13.2.2 业务需求整理
|
||||
|
||||
需求调研小组面对着贴满小纸条的白板,开始了分类行动。
|
||||
|
||||
有些需求点很容易分类,比如,从图书馆管理员那里听到的需求就属于图书馆的,与食堂宿舍无关。但是,有些需求没有明确的责任人,比如课程问题,它是属于教学过程,既有老师参与,也有学生参与,还有教务处安排,所以这类需求要独立分类。
|
||||
|
||||
最后,大家把粗略的分类结果在白板上展示出来,如图 13.2.2 所示。
|
||||
|
||||
<img src="img/Slide6.SVG"/>
|
||||
|
||||
图 13.2.2 数字化校园系统业务需求整理
|
||||
|
||||
在图 13.2.2 中,大家用笔在同类的需求贴纸周围画了一个圈子,然后再看看有没有什么明显的分类错误。没有的话,则进入下一阶段。
|
||||
|
||||
### 13.2.3 业务需求分析
|
||||
|
||||
下面要根据分类结果给每一个类别都起一个名字来标识此类,比如,图 13.2.2 中的一个圈子内的所有需求都与学生有关,则叫做“学生”;都与吃喝拉撒睡有关,就叫做“生活”,等等。最后得到图 13.2.3 的分析图。
|
||||
|
||||
<img src="img/Slide7.SVG"/>
|
||||
|
||||
图 13.2.3 数字化校园系统业务需求分析
|
||||
|
||||
在图 13.2.3 中,大家把所有的**需求点**分成了 6 个**需求块**,这与图 13.2.2 中的五个圈子不完全吻合,是进一步分析归纳的结果。
|
||||
|
||||
6 个块的名字分别是:教师、学生、教学、生活、图书、行政。每个名字下面包含了数量不等的模块,并且其名字和原始的需求点名字不尽相同。比如,食堂$\rightarrow$食堂管理,宿舍$\rightarrow$宿舍管理,校内办公$\rightarrow$办公系统,等等。并且,根据一些归纳和推理,还删除了一些重复的需求,增加了一些用户没有提起的但又必须有的模块。
|
||||
|
||||
有的读者可能对 6 个需求块的划分有不同意见,没关系,笔者在这里只是举例说明,读者完全可以给出自己的业务划分结果。
|
||||
|
||||
如果我们用**自顶向下**的方法来做本场景的需求调研,可以比较容易地得到与图 13.2.3 近似的结果,需要再整理一下。要注意的是“教学”这个**需求块**不能直接得到,它是从教务处、任课教师、学生等多方得到信息的综合。
|
||||
|
||||
### 13.2.4 业务场景架构
|
||||
|
||||
有了图 13.2.3 后,大家心里对这个数字化校园的基本功能块都有了初步的理解,但是如果把图 13.2.3 拿给校长看的话,还是有些简单,没什么技术含量。接下来要做进一步的规划。
|
||||
|
||||
首先,图 13.2.3 中的六个**需求块**,可以基本上确定为**子系统**,在这些子系统中的各个**需求点**将会成为**模块**,可以简单地理解为:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
需求块 &= \sum 需求点
|
||||
\\
|
||||
需求块 \rightarrow 子系统 &,需求点 \rightarrow 模块
|
||||
\\
|
||||
子系统 &= \sum 模块
|
||||
\\
|
||||
系统 &= \sum 子系统
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
如果在后期的调研中发现有些模块过于复杂,也可以上升到子系统的级别,比如行政子系统中的财务管理,完全可以升级为财务子系统。
|
||||
|
||||
其次,需要把六个子系统的重要程度排序,区分出主要次要,因为建设数字化校园是一个长期而费钱的工程,校长也不可能一次性拿出一大笔资金来支持。而且基于规避风险的需要,校长也肯定要决定先做什么后做什么,视前期的效果而制定后期的计划。
|
||||
|
||||
于是,需求分析小组和**架构设计师**坐在一起讨论,把六个子系统分成了三个层次,如图 13.2.4 所示:
|
||||
|
||||
1. 重点场景:教学管理子系统;
|
||||
2. 基本场景:教师管理子系统、学生管理子系统;
|
||||
3. 服务场景:行政子系统、生活子系统、图书子系统。
|
||||
|
||||
<img src="img/Slide8.SVG"/>
|
||||
|
||||
图 13.2.4 数字化校园系统业务框架
|
||||
|
||||
这些子系统之间没有强烈的依赖关系,都可以单独开发、运行。但是,还有基础组件需要先期完成:
|
||||
|
||||
1. 系统管理,包含了用户登录验证、权限管理、管理员管理等基本的服务。
|
||||
2. 基础设施建设,比如 IT 网络、数据库以及机房的建设。
|
||||
3. 处于上方的用户接入部分,如果只提供浏览器方式接入,相对来说解决方案比较成熟,这部分可以同时做。
|
||||
4. 处于右侧的外部系统部分,比如与互联网的物理连接方式,与教育部的上级系统的软件接口等等,也是非常重要的组成部分,需要事先考虑清楚。
|
||||
|
||||
有了图 13.2.4 后,木头认为可以和校长以及其它校领导进行初步的汇报了,因为就这一部分的内容来说,没有复杂的技术问题,校长和校领导们可以完全理解的,因为这是他们的业务职责范围,所以被称为**业务框架**。
|
||||
|
||||
但是,为了体现出架构设计师的素养,木头又把图 13.2.4 进一步细化,绘制成了 图 13.2.5 的样子。
|
||||
|
||||
<img src="img/Slide9.SVG"/>
|
||||
|
||||
图 13.2.5 数字化校园系统业务场景架构图
|
||||
|
||||
【最佳实践】
|
||||
|
||||
在画业务场景架构图时,除了内容上要完全和需求分析结果匹配,还需要注意的技巧是:
|
||||
|
||||
1. 颜色搭配要合理,最好是用绘图工具(如 Power Point)中建议的色系。比如图 13.2.5 中的色系就是 深黄、浅黄、绿色、浅绿、浅蓝、深蓝,不会出现红色、紫色等。
|
||||
2. 用层次关系表达设计思想。
|
||||
3. 列出一些必要的子模块,篇幅不够的画可以少列一些。
|
||||
4. 同一类功能使用同一种颜色,用方框包围,并给出小标题,如“教学应用”。
|
||||
5. 方框内部不需要绘制箭头线,一是显得乱,二是还不能确定逻辑关系。在方框外部可以画一些箭头线,笼统地表示有交互。
|
||||
6. 虚线框实线框结合,表示层级关系,先虚后实或先实后虚都可以,内外框线的颜色要不同。
|
||||
|
||||
### 13.2.5 其它业务场景架构举例
|
||||
|
||||
在上面讲述的“数字化校园”系统的业务场景只是真实的系统的一个缩影而已,实际的系统复杂度是图 13.2.5 的三到五倍,但图 13.2.5 也已经可以称作是一个大系统了。
|
||||
|
||||
下面我们可以再举一些更流行的例子,来说明业务场景架构的概念。如图 13.2.6 所示。
|
||||
|
||||
<img src="img/Slide10.SVG"/>
|
||||
|
||||
图 13.2.6 电商业务场景架构图
|
||||
|
||||
图 13.2.6 这张业务场景架构图基本覆盖了电商业务的全貌,其中有几个需要解释的地方:
|
||||
|
||||
- 每个小的矩形是一个模块,比如实物平台、商家工具等等;
|
||||
- 小矩形中的名称比较多样化,后缀有“平台”、“系统”、“服务”、“管理”等等,其实都是一个功能模块;
|
||||
- 每个圆角矩形框是一个子系统,比如“用户”、“支付”等等;
|
||||
- 每个子系统中都有三个模块,只是为了绘图好看而已,实际上模块的数量要更多一些;
|
||||
- 中间的绿色区域是核心业务,包含网站、交易、订单、履约;
|
||||
- 箭头表示业务交互关系,有双向的箭头,也有单向的。
|
||||
|
||||
从层次来看:
|
||||
|
||||
- 最上层是与用户或者商家的接口;
|
||||
- 中层是核心业务;
|
||||
- 下层是外围服务部分。
|
||||
|
||||
这中层次划分与图 13.2.4 的“数字化校园”系统类似。
|
||||
|
||||
到此为止,第一张架构图“业务场景架构”已经诞生了,它属于非技术的业务需求,是所有人(主要是用户)能够看懂的。但是,这只是架构设计的工作开始。
|
|
@ -0,0 +1,221 @@
|
|||
|
||||
## 13.3 主流的架构设计方法
|
||||
|
||||
在上一节中讲述了一个数字化校园系统的业务场景架构设计的故事,最后的输出是给校长和领导们看的**业务场景架构图**。为什么叫做这个名字呢?还有其它类型的架构图吗?我们在本节中一起来解决这个问题。
|
||||
|
||||
首先我们先看看业界当前流行的五种架构设计方法,它们分别是:
|
||||
|
||||
- RUP 4+1 架构视图
|
||||
- UML 统一建模视图
|
||||
- C4 模型
|
||||
- TOGAF 企业架构设计图
|
||||
- 其它
|
||||
|
||||
### 13.3.1 RUP 4+1 视图
|
||||
|
||||
RUP,Rational Unified Process,即统一开发过程。
|
||||
|
||||
这是 Philippe Kruchten 在 1995 年 11 月发表的一篇 IEEE 的论文,使用多个并发视图来描述一种软件架构模型,多个视图可以分别面向各种“利益相关者”:最终用户、开发人员、系统工程师、项目经理等,分别处理功能和非功能需求,针对以架构为中心、场景驱动的迭代开发过程设计的。见图 13.3.1。
|
||||
|
||||
<img src="img/Slide11.SVG"/>
|
||||
|
||||
图 13.3.1 RUP 4+1 视图
|
||||
|
||||
#### 1. 逻辑视图(Logical View)
|
||||
|
||||
- 面向对象的分解。
|
||||
- 描述最终用户关心的软件功能。
|
||||
|
||||
逻辑架构主要支持功能需求,即系统应该为用户提供哪些服务。根据问题领域(即应用场景)把系统分解为一组抽象的对象或对象类的形式。这种分解不仅是为了功能分析,还用于识别分布在系统各个部分中的可以共享的底层模块。
|
||||
|
||||
#### 2. 过程视图(Process View)
|
||||
|
||||
- 过程的分解。
|
||||
- 描述系统集者成关心的的性能和可扩展性。
|
||||
|
||||
过程架构主要支持非功能需求,如性能和可用性。它解决并发性、分布性、系统完整性、容错性的问题,以及逻辑视图如何与过程结构配合在一起,使得针对对象的操作如何被实际执行。
|
||||
|
||||
#### 3. 开发视图(Development View)
|
||||
|
||||
- 子系统分解。
|
||||
- 描述开发人员所关系的软件代码结构管理问题。
|
||||
|
||||
开发架构关注软件开发环境下实际模块的组织。软件打包成小的程序块(程序库或子系统),它们可以由一位或几位开发人员来开发。子系统可以组织成分层结构,每个层为上一层提供的接口。
|
||||
|
||||
#### 4. 物理视图(Physical View)
|
||||
|
||||
- 软件在硬件上的部署。
|
||||
- 描述系统工程师关心的拓扑结构和网络通信问题。
|
||||
|
||||
物理架构主要考虑系统的非功能需求,如可用性、可靠性(容错)、性能(吞吐量)和可扩展性,描述软件系统中的不同部分如何部署在网络中的多台计算机上运行。
|
||||
|
||||
#### 5. 场景视图(Scenarios)
|
||||
|
||||
- 场景视图是 4+1 中的 1。
|
||||
|
||||
把上面的所有内容都串起来,通过一些重要的需求场景(常见用例)进行无缝协同工作,验证对象之间和过程之间的交互。
|
||||
|
||||
#### 小结
|
||||
|
||||
表 13.3.1 RUP 4+1 视图小结
|
||||
|
||||
|视图|组件|使用者|关注点|
|
||||
|-|-|-|-|
|
||||
|逻辑|类|用户|功能|
|
||||
|过程|任务|设计者,集成者|性能,可用性|
|
||||
|开发|模块,子系统|开发者,管理者|代码结构,可重用性|
|
||||
|物理|计算节点|设计者|可扩展性,性能,可用性|
|
||||
|场景|步骤|用户,开发者|可理解性|
|
||||
|
||||
### 13.3.2 UML 建模视图
|
||||
|
||||
UML,Unified Model Language,即统一建模语言。UML 还总结了一套建模视图,见图 13.3.2。
|
||||
|
||||
<img src="img/Slide12.SVG"/>
|
||||
|
||||
图 13.3.2 UML 建模视图
|
||||
|
||||
#### 1. 用户模型(Use Case View)
|
||||
|
||||
强调从用户的角度看到的或需要的系统功能,是被称为参与者的外部用户所能观察到的系统功能的模型图。
|
||||
|
||||
#### 2. 结构模型(Logic View)
|
||||
|
||||
展现系统的静态或结构组成及特征,也称为结构模型视图(Structural Model View)或静态视图(Static View)。
|
||||
|
||||
#### 3. 行为模型(Concurrent View)
|
||||
|
||||
体现了系统的动态或行为特征,也称为行为模型视图(Behavioral Model View)或动态视图(Dynamic View)。
|
||||
|
||||
#### 4. 实现模型(Component View)
|
||||
|
||||
体现了系统实现的结构和行为特征,也称为实现模型视图(Implementation Model View)。
|
||||
|
||||
#### 5. 环境模型(Deployment View)
|
||||
|
||||
体现了系统实现环境的结构和行为特征,也称为环境模型视图(Environment Model View)或物理视图(Physical View)。
|
||||
|
||||
#### 小结
|
||||
|
||||
表 13.3.2 UML 建模视图小结
|
||||
|
||||
|模型|使用者|使用图形|
|
||||
|-|-|-|
|
||||
|用户模型|用户,设计者,开发者,测试者|实例图,活动图|
|
||||
|结构模型|设计者,开发者|类和对象图,状态图,时序图,协同图,活动图|
|
||||
|行为模型|开发者,集成者|状态图,时序图,协同图,活动图,构件图,配置图|
|
||||
|实现模型|开发者|构件图|
|
||||
|环境模型|开发者,集成者,测试者|配置图|
|
||||
|
||||
### 13.3.3 C4 模型
|
||||
|
||||
C4 模型是一位知名的专门从事软件架构的独立顾问 Simon Brown 在 2018 年国际软件开发会议上提出的方法论,是一种在不同抽象层次上交流软件架构的简单方法,可以向不同的受众讲述不同的故事。这也是向软件开发团队介绍(通常是重新引入)严谨和轻量级建模的一种方式,避免过度强调敏捷开发而带来的弊端。见图 13.3.3。
|
||||
|
||||
<img src="img/Slide13.SVG"/>
|
||||
|
||||
图 13.3.3 C4 模型
|
||||
|
||||
#### 1. 系统上下文图(System Context)
|
||||
|
||||
这一层不会关注细节,提供的是系统级别的总览关系图,这层的关注点应该是用户和系统的交互,而不是协议,技术点等一些具体的体现。所以它的使用人群是非技术人员,这里面描述的是系统级别的交互,以及谁使用。
|
||||
|
||||
#### 2. 容器图(Container)
|
||||
|
||||
这里的 Container 也可以简单地理解为容器,前提是这个 Container 只有一个服务,当 Container 具备多个服务组成时,那么这个 Container 就包含一系列的服务了,不能简单的对等于容器。
|
||||
|
||||
这层是给具有技术背景的人员看,这里面描述的是进程级别的应用,这是可直接部署和运行的,通过这层,我们将可以看清软件整体形态以及职责描述。
|
||||
|
||||
#### 3. 组件图(Component)
|
||||
|
||||
这一层是给开发人员看,这里描述的是系统组成部件、部件之间的层级关系和依赖。
|
||||
|
||||
这层的 Component,可以理解为 Java 里面的包或者 C# 里面的程序集,这里描述的是内部的包/程序集相互引用(调用)关系以及包对外部系统的程序的依赖。
|
||||
|
||||
#### 4. 代码(Code)
|
||||
|
||||
这一层的使用对象是开发人员,这里描述的是基于 UML 等图形来描述实现的技术细节,直接反映了代码层面。
|
||||
|
||||
#### 小结
|
||||
|
||||
C4 模型是一种轻量级的设计方法,其实就是像看电子地图一样一步步放大,直到能看到代码细节。适合于小型系统做概要设计。
|
||||
|
||||
表 13.3.3 C4 模型小结
|
||||
|
||||
|视图|使用者|绘图元素|使用图形|建议|
|
||||
|-|-|-|-|-|
|
||||
|上下文|用户,开发团队|软件系统和使用者|系统上下文图|必须有|
|
||||
|容器|设计者,开发者,集成者|系统中的容器和使用者|普通框图|必须有|
|
||||
|组件|设计者,开发者,测试者|容器中的组件和组件的使用者|普通框图|可以有|
|
||||
|代码|设计者,开发者|组件中的类,接口,功能,数据库|UML 类和对象图|不必有|
|
||||
|
||||
### 13.3.4 TOGAF 企业架构设计图
|
||||
|
||||
TOGAF,即 The Open Group Architecture Framework,开放组织架构框架,是由 The Open Group 这个开放组织指定的企业发展架构的方法和工具(即框架)。见图 13.3.4。
|
||||
|
||||
<img src="img/Slide14.SVG"/>
|
||||
|
||||
图 13.3.4 TOGAF 企业架构
|
||||
|
||||
#### 1. 业务架构(BA - Business Architecture)
|
||||
|
||||
定义了企业战略,管理,组织和主要的业务流程。开发基于原则、业务目标和策略驱动力的目标业务架构,描述产品、服务策略,以及业务环境在组织、功能、过程、信息和地理这些方面的内容。
|
||||
|
||||
#### 2. 数据架构(DA - Data Architecture)
|
||||
|
||||
描述一个组织的物理和逻辑数据资产,以及数据资源的结构。通过一种干系人容易理解的方法数据的类型与来源进行定义。数据架构并不是针对存储系统在逻辑或物理方面的设计,而是对企业相关的数据实体的定义。包括数据管理、数据迁移、数据治理。
|
||||
|
||||
与应用架构并称为信息架构。
|
||||
|
||||
#### 3. 应用架构(AA - Application Architecture)
|
||||
|
||||
描述各个应用程序逻辑,它们之间的相互作用,以及它们的关系,是该企业的核心业务流程。作为信息系统架构的另外一个组成部分,应用架构描述了各种用于支持业务架构并对数据架构所定义的各种数据进行处理的应用系统。
|
||||
|
||||
与数据架构并称为信息架构。
|
||||
|
||||
#### 4. 技术架构(TA - Technology Architecture)
|
||||
|
||||
描述了需要支持的业务、数据和应用服务部署的逻辑软件和硬件的能力,包括 IT 基础设施、中间件、网络、通信、处理、标准等。将应用架构中定义的各种应用组件映射为相应的技术组件,这些技术组件代表了各种可以从市场或组织内部获得的软件和硬件组件。
|
||||
|
||||
应用架构本身只关心需要哪些应用系统,哪些平台来满足业务目标的需求,而不会关心在整个构建过程中你需要使用哪些技术。技术架构是根据应用架构的技术需求,并根据识别的技术需求,进行技术选型,把各个关键技术和技术之间的关系描述清楚。技术架构解决的问题包括:如何进行纯技术层面的分层、开发框架的选择、开发语言的选择、涉及非功能性需求的技术选择
|
||||
|
||||
#### 小结
|
||||
|
||||
【最佳实践】
|
||||
|
||||
严格地说,**TOGAF 并非软件开发的架构指南**,而是建立、管理企业的一种架构指南。在软件业界有很多人用它来做企业管理软件的架构设计,也未尝不可,因为 TOGAF 本身就是企业的一个很好的抽象。但是在使用过程中有以下几点需要注意:
|
||||
|
||||
- 它强调了数据架构,因为企业中有很多数据需要管理,但是在普通的应用程序中,数据是依附在功能和流程上的,不要本末倒置。所以,它只适合于数据业务较重的软件系统。
|
||||
|
||||
- 它的正确顺序是:业务架构 $\rightarrow$ 信息架构(数据架构 + 应用架构)$\rightarrow$ 技术架构。而有些人把它解释成:业务架构 $\rightarrow$ 应用架构 $\rightarrow$ 数据架构 $\rightarrow$ 技术架构,但是又画不出实际的数据架构。此时,可以把应用架构看作是“静态+动态”架构,而把技术架构看作是“开发+部署”架构,忽略数据架构环节。
|
||||
|
||||
- 不要把这四种架构和产品架构、代码架构/开发架构、部署架构/物理架构等名词混在一起用,因为它们互相之间有重叠,不利于画出关注点分离的架构图。
|
||||
|
||||
- 如果不是严格按照 TOGAF 的定义使用架构名词时,就不要宣称自己是按照标准来设计的,完全可以说自成体系,按自己的理解来解释上述四个架构名词。
|
||||
|
||||
### 13.3.4 五视图流派
|
||||
|
||||
这个流派的来源不明,可能是一些从业者在总结了上面所述的一些架构设计思路后,整理出的最佳实践,叫做架构设计五视图。见图 13.3.5。
|
||||
|
||||
<img src="img/Slide15.SVG"/>
|
||||
|
||||
图 13.3.5 五视图流派
|
||||
|
||||
#### 1. 逻辑架构
|
||||
|
||||
逻辑架构关注的是功能,包含用户直接可见的功能,还有系统中隐含的功能。或者更加通俗来描述,逻辑架构更偏向我们日常所理解的“分层”,把一个项目分为“表示层、业务逻辑层、数据访问层”这样经典的“三层架构”。
|
||||
|
||||
#### 2. 开发架构
|
||||
|
||||
开发架构则更关注程序包,不仅仅是我们自己写的程序,还包括应用程序依赖的SDK、第三方类库、中间件等。尤其是像目前主流的Java、.NET等依靠虚拟机的语言和平台,以及主流的基于数据库的应用,都会比较关注。和逻辑架构有紧密的关联。
|
||||
|
||||
#### 3. 运行架构
|
||||
|
||||
顾名思义,更关注的是应用程序运行中可能出现的一些问题。例如并发带来的问题,比较常见的“线程同步”问题、死锁问题、对象创建和销毁(生命周期管理)问题等等。开发架构,更关注的是飞机起飞之前的一些准备工作,在静止状态下就能规划好做好的,而运行架构,更多考虑的是飞机起飞之后可能发生的一些问题。
|
||||
|
||||
#### 4. 物理架构
|
||||
|
||||
物理架构,更关注的系统、网络、服务器等基础设施。例如:如何通过服务器部署和配置网络环境,来实现应用程序的“可伸缩性、高可用性”。或者举一个实际的例子,如何通过设计基础设施的架构,来保障网站能支持同时 10 万人在线、7*24 小时提供服务,当超过 10 万人或者低于 10 万人在线时,可以很方便的调整部署架构来支撑。
|
||||
|
||||
#### 5. 数据架构
|
||||
|
||||
数据架构,更关注的是数据持久化和存储层面的问题,也可能会包括数据的分布、复制、同步等问题。更贴切来讲,如何选择需要的关系型数据库、流行的 NoSql,如何保障数据存储层面的性能、高可用性、灾备等等。很多时候,和物理架构是有紧密联系的,但它更关注数据存储层面的,物理架构更关注整个基础设施部署层面。
|
|
@ -0,0 +1,306 @@
|
|||
|
||||
## 13.4 架构设计的任务与关注点
|
||||
|
||||
### 13.4.1 各种设计方法的比较
|
||||
|
||||
总结一下上个小节中的内容,目前软件工程领域有几种主流的架构设计方法,见图 13.4.1。为了下文中描述方便,我们分别用这几种方法的首字母作为该方法的缩写:R,U,C,T,O。
|
||||
|
||||
<img src="img/Slide16.SVG"/>
|
||||
|
||||
图 13.4.1 主流的架构设计方法比较
|
||||
|
||||
从各方法的步骤名称上看,可以引发一些联想,比如:
|
||||
|
||||
- 场景、用户、业务、上下文,这些词都代表了用户业务需求;
|
||||
- 逻辑、结构、功能,这些是描述静态结构和逻辑功能的词汇;
|
||||
- 过程、行为、运行,这些是描述动态变化和行为模式的词汇;
|
||||
- 开发、实现、代码,这些是描述编写代码和技术实现的词汇;
|
||||
- 物理、环境、技术,这些是描述网络节点和硬件环境的词汇。
|
||||
|
||||
所以,接下来我们通过横向对比这几种主流方法,来发掘它们的共同点,以便得到一个统一的方法论。这种方法就好比 12.3.4 小节中描述的主从模式的工作方式:既然大家的意见有细微差别,那么就由“从节点”给出各自的答案,由“主节点”(笔者和各位读者)总结出来一个更准确的最终答案。
|
||||
|
||||
见表 13.4.1。注意该表中各方法的顺序与图 13.4.1 有所不同,主要是为了横向比较,不会影响架构设计工作的进行。比如:
|
||||
|
||||
- 图 13.4.1 中的 RUP 4+1,本来场景视图在最下方,原文的含义是用场景视图做架构设计验证,检测是否可以把前四步的设计结果串联成一个完整的业务场景;如果在表 13.4.1 中放在最上方,意味着需要先定义一个业务场景,这种软件工程中属于需求分析,是合理的。
|
||||
|
||||
- 图 13.4.1 中的 TOGAF 的数据架构本来在第二位,但其实在原文中它和第三位的应用架构是并列的,二者相辅相成,没有前后关系,所以在表 13.4.1 中把二者对调了一下位置。
|
||||
|
||||
- 图 13.4.1 中的 Other 的开发架构放在第二位,由于找不到出处,不知道这个顺序的理由是什么。从笔者的观点看,运行架构是设计的一部分,必须先有运行架构,才会决定开发架构,所以二者应该对调。另外,数据架构为什么放在最后一位?没有数据架构的话,开发架构是有缺失项的。
|
||||
|
||||
表 13.4.1 架构设计方法的统一
|
||||
|
||||
|顺序|统一名称|(R)RUP 4+1|(U)UML|(C)C4|(T)TOGAF|(O)Other|
|
||||
|-|-|-|-|-|-|-|-|
|
||||
|1|**业务场景架构**|场景视图|用户模型|上下文图|业务架构|N/A|
|
||||
|2|**逻辑功能架构**|逻辑视图|结构模型|容器图|应用架构|逻辑架构|
|
||||
|3|**运行过程架构**|过程视图|行为模型|组件图|应用架构|运行架构|
|
||||
|4|**数据存储架构**|N/A|N/A|N/A|数据架构|数据架构|
|
||||
|5|**软件开发架构**|开发视图|实现模型|代码图|技术架构|开发架构|
|
||||
|6|**物理部署架构**|物理视图|环境模型|N/A|技术架构|物理架构|
|
||||
|
||||
从表 13.4.1 中可看到:
|
||||
|
||||
- 最左侧的序号表明了架构任务的顺序,按照这个顺序进行是最合理的。
|
||||
- 第二列中使用 6 个汉字的统一名称,这样命名更加准确,以避免一些模糊的含义产生错误的理解。比如,4 个字的“运行架构”用 6 个字的“运行过程架构”来表示。
|
||||
- TOGAF 的“应用架构”和“技术架构”都出现了两次,正如 13.3.4 节中所说,因为这个方法不是标准的软件工程方法,所以它的定义比较模糊。目前在业界有很多人使用这个方法(因为与其它方法比较,这种方法设计量小,关注点模糊,可以随意解释),在网上查资料看到的画出来的图关注点混乱不堪,误导读者,慎用。
|
||||
- “数据架构”被上移到了第四位,符合设计工作的正常顺序。
|
||||
|
||||
### 13.4.2 业务场景架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
**场景、用户、业务、上下文**,这些词都代表了用户业务需求。
|
||||
|
||||
前四种方法中都有反映业务需求的非技术架构,如 (R)场景视图、(U)用户模型、(C)上下文图、(T)业务架构。在软件工程中,如果从需求出发的话,第一张图应该是给用户看的,不妨叫做**业务场景架构**。该图中没有逻辑,只有一些大的功能区,用来区分系统/子系统的边界。这张图在 13.2 节中已经绘制好了,我们不妨拷贝过来到图 13.4.2 以方便阅读。
|
||||
|
||||
<img src="img/Slide9.SVG"/>
|
||||
|
||||
图 13.4.2 业务场景架构图
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
业务场景架构,就好比一个车企的产品发布会,需要把一系列的产品展现给观众,包括家庭轿车、家庭 SUV、商用轿车、商用 MPV、新能源汽车、高级跑车等等,琳琅满目,每款汽车都有自己的**客户(功能)定位**,很少与其它类型的车有交集。该车企只专注于乘用汽车,对于运输车辆不会涉足,这就是**系统划分**。为了增加可信度,车企还有可能告知观众自己的上游原材料、零件进货商,下游的强大销售、保养、维修实力,先进的自动化生产线,良好的历史业绩和用户口碑等等,这就是**外部关系**。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
业务场景架构 = 系统划分 + 功能覆盖 + 外部关系 \tag{13.4.1}
|
||||
$$
|
||||
|
||||
软件系统建设是为业务服务的,所有的系统建设都是为了解决业务问题。因此,首先做的是进行战略(可行性)分析和业务场景架构设计,但是战略(可行性)分析一般是由甲方来做。比如学校想建设数字化校园系统,那么应该由学校的人员来做这个分析,以确定是否可以开始咨询乙方给出技术方案。
|
||||
|
||||
作为软件开发者,我们只关心业务场景架构,不关心可行性分析,除非甲方冒傻气让乙方来做。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 这张图的目的是把用户的需求做分类,把一个复杂的系统分解成若干个子系统。那么要不要分解到模块级别呢?如果系统复杂,就不需要,而是把模块分解留到下一个阶段(子系统的概要设计)去做。
|
||||
|
||||
- 关注点在业务上,强调功能覆盖,不要包含过多的软件工程领域的技术词汇或概念,否则无法和用户沟通。
|
||||
|
||||
- 这个级别的设计是给用户的上层领导看的,所以也不用涉及很多业务细节,比如只需要知道有课程管理,而无需描述课程管理内部的具体细节。
|
||||
|
||||
- 但是,上层领导很敏感的领域是对外关系,比如校园网和教育网如何连接、外部用户能不能访问或使用校园网信息、如果校长在外出差时能不能及时收到校内的电子邮件或审批请求等等,所以与外部系统的关系要有描述。
|
||||
|
||||
- 通过讨论后,用户可能会提出新的需求,看看是不是能合并到已有的子系统中,如果不能,需要增加子系统。
|
||||
|
||||
### 13.4.3 逻辑功能架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
**逻辑、结构、功能**,这些是描述静态结构和逻辑功能的词汇;
|
||||
|
||||
业务需求分析结束后,应该进入技术阶段了,根据需求分析的用户需求和功能需求建立软件系统的静态模型。如 (R)逻辑视图、(U)结构模型、(C)容器、(T)应用架构、(O)逻辑架构。这张图是给开发团队的所有人(但主要是管理者)看的,可以掌控全局,所以应该是分层、分模块的软件功能静态模型,可以叫做**逻辑功能架构**。见图 13.4.3。
|
||||
|
||||
<img src="img/Slide17.SVG"/>
|
||||
|
||||
图 13.4.3 逻辑功能架构图
|
||||
|
||||
在图 13.4.3 中,我们使用了 12.4.2 节的分层模式,12.6.1 节的 MVC 模式(业务实体模型是 M)。
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
逻辑功能架构,就好比一个仿真汽车模型,所有可以看到的**零配件**(功能模块)都配齐了,比如轿厢、车门、车窗、座椅、方向盘、尾灯,甚至 logo,都制作得惟妙惟肖,甚至车门还可以手动开启,车漆锃光瓦亮,装上电池以后,车灯还能闪亮。但是这些,都属于汽车的用户(驾驶)子系统,位于**表层**,还应该有用户看不见的行驶子系统和动力子系统位于**底层**,三者**配合**才能带来好的驾驶体验。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
逻辑功能架构 = 静态结构 + 功能模块 + 协作关系 \tag{13.4.2}
|
||||
$$
|
||||
|
||||
如果把业务场景架构看作是俯视图,那么逻辑架构就是一个横向的切片,类似医学上的 CT。比如,在图 13.4.3 中,在表示层、应用层、数据访问层,都会出现“生活(服务)”,这是一种技术上的划分,三个层次的模块串联在一起共同实现“生活服务”子系统。
|
||||
|
||||
本任务介于业务语言与技术语言之间,是对整个系统实现的总体上的架构,需要指出系统的层次、系统开发的原则、系统各个层次的应用服务,例如,上述系统中可以分为数据层(资源层)、数据服务层、中间构建服务层、业务逻辑层、表现层,并写明每个层次应用服务。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 需要一个静态的、基本的结构,我们可以采用第十二章中学习的架构模式,比如 12.4.2 节的分层模式。也可以是多个架构模式的组合。
|
||||
|
||||
- 软件功能子系统/模块的划分,比如生活服务、行政服务等。那么“生活服务”中的“食堂管理”、“宿舍管理”要不要列出来呢?在这个阶段中不需要列出来,而是在做生活服务子系统的概要设计时再考虑。
|
||||
|
||||
- 公共业务下沉(比如单点登录),因为这是所有子系统必须都调用的业务。
|
||||
|
||||
- 在 7.4 节中我们讲过三种层次的需求:业务需求,用户需求,功能需求。在本图中要发掘出功能需求,如“管理员子系统”,这是用户自己想不到的,只能由系统设计人员提出。
|
||||
|
||||
- 结构单元之间的相互协作关系要绘制出来,而不必关心具体的调用关系或者接口协议,只是表达“A 和 B 有联系”即可。
|
||||
|
||||
|
||||
### 13.4.4 运行过程架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
**过程、行为、运行**,这些是描述动态变化和行为模式的词汇。
|
||||
|
||||
既然有了静态模型,还应该有动态模型来描述系统内部的交互行为已经状态变化,如 (R)过程视图、(U)行为模型、(C)组件图、(T)应用架构、(O)运行架构。通过这个模型,可以知道软件系统在运行过程中的内部具体流程,用于分析性能质量问题,可以叫做**运行过程架构**。见图 13.4.4。
|
||||
|
||||
<img src="img/Slide18.SVG"/>
|
||||
|
||||
图 13.4.4 应用运行架构图
|
||||
|
||||
在图 13.4.4 中,我们使用了 12.3.2 节的浏览器/服务器模式,12.5.1 节的代理模式(应用网关),12.5.3 节的微服务模式。
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
逻辑功能架构中的汽车模型已经很完美了,但是,这个模型只能放在那里供观赏,车无法跑起来,虽然它也有 4 个车轮甚至备胎。那么在运行过程架构中,我们就需要给它安装底盘和发动机或电动机,再加上动力电池**驱动**,就可以驱动四个轮子前进了。如何使得动力子系统和行驶子系统**协调工作**,让汽车安全、稳定地**运行**并得到实时状态是这一小节的任务。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
运行过程架构 = 进程设定 + 状态转换 + 调用关系 \tag{13.4.3}
|
||||
$$
|
||||
|
||||
图 13.4.4 中绘制了一些关键的软件系统运行期元素,包括进程、模块(逻辑)、数据节点(库、表)、服务等等,展示了完整的运行过程。其中,进程都用浅黄色的图形表示,有两类:应用软件系统自己的进程用缺角矩形表示;操作系统或框架的进程用普通矩形表示。
|
||||
|
||||
基本的运行流程如下:
|
||||
|
||||
1. 用户浏览器发送 Http 请求;
|
||||
2. Web 服务器上的监听进程(建议用框架实现,但也可以自己实现)接收请求,启动(或激活)工作进程,调用表示层的逻辑代码;
|
||||
3. 调用应用网关接口,在预先注册好的服务注册表中发现对应的服务;
|
||||
4. 如果该用户没有登陆,就先进入单点登录逻辑;
|
||||
5. 然后启动应用层中的微服务,通过三个微服务的串联完成用户请求;
|
||||
6. 在微服务运行时,需要读写对应的数据库;
|
||||
7. 最后由微服务 3 把结果返回给表示层;
|
||||
8. 生成响应页面后通过 Http 响应发给用户;
|
||||
9. 在上面这个过程中,后台服务一直在工作。
|
||||
|
||||
关注的是应用程序运行中可能出现的一些问题。例如并发带来的问题,比较常见的“线程同步”问题、死锁问题、对象创建和销毁(生命周期管理)问题等等。但是在一张图中是不可能画出这么复杂的机制的,所以要写在文档中。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 确定哪些进程、服务是需要“常活”的,即一直在运行的程序模块,设定为进程。如果大量使用线程工作的话,还需要描述线程机制,比如线程池。
|
||||
|
||||
- 它们都位于哪个组织中,比如是 Web 服务器群中,还是应用层中,或者是后台服务层中。
|
||||
|
||||
- 调用关系如何,比如:读、写、执行、注册、发现、调用、返回等行为。
|
||||
|
||||
- 调用的类型是什么,比如是一个模块间的功能调用,还是 HTTP 请求,还是 RESTful API 调用,或者是一个通过代理的远过程调用,等等。
|
||||
|
||||
- 进程在处理完一个事件后,肯定要有状态转换,最基本的就是从“繁忙状态”转到“空闲状态”,复杂的系统可能有多种状态。
|
||||
|
||||
### 13.4.5 数据存储架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
关键词只有**数据**。
|
||||
|
||||
这一部分不是必须的,如果是面向企业的数据密集型的软件系统才会用到,叫做**数据存储架构**,而一般的计算密集型软件中,在逻辑功能架构和运行过程架构中中可以顺带给出。因为静态架构中可以有数据库的存在,在动态架构中,也离不开数据的读写过程,所以在图 13.4.3 和 图 13.4.4 中可以看到数据库的影子,只不过比较简单(当然也可以画得复杂)。反过来看,数据存储架构是不能独立于逻辑功能/运行过程而独立存在的。如 (T)数据架构、(O)数据架构。见图 13.4.5。
|
||||
|
||||
<img src="img/Slide19.SVG"/>
|
||||
|
||||
图 13.4.5 数据存储架构图
|
||||
|
||||
在图 13.4.5 中,我们使用了 12.3.4 节的主从模式(数据库),12.4.1 节的管道/过滤器模式,12.5.2 的事件驱动模式(消息队列)。
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
传统的汽车本身不是数据密集型的系统,所以它的数据存储架构并不典型。但是在自动驾驶技术中,数据确实无处不在。通过摄像头、传感器把外界的信息**采集**上来,然后**处理**,保留有用信息,做特征抽取、模式识别,再根据知识库(或通过强化学习得到的规则)发出指令,并随时监测汽车本身的行驶数据,评估指令的有效性。在这个过程中,所有加工(非原始)数据都要**保存**下来备查。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
数据存储架构 = 内容格式 + 存储方式 + 读写控制 \tag{13.4.4}
|
||||
$$
|
||||
|
||||
数据架构,更关注的是数据持久化和存储层面的问题,也可能会包括数据的分布、复制、同步等问题。更贴切来讲,如何选择需要的关系型数据库、流行的 NoSQL,如何保障数据存储层面的性能、高可用性、灾备等等。很多时候,和物理架构是有紧密联系的,但它更关注数据存储层面的,物理部署架构更关注整个基础设施部署层面。
|
||||
|
||||
图 13.4.5 比较简约,越是高层次的设计,越是要避免陷入细节,给做后续设计的留出发挥的空间。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 系统需要什么样的数据?这需要通过需求分析得到,画数据流图可以帮助完成这一目标。但是在架构阶段,不需要绘制具体业务流程的数据流图,只需要把握大的脉络即可,比如图 13.4.5 中的“业务数据库”,并没有指定是哪个业务,而是通用的称呼。
|
||||
|
||||
- 如何存储这些数据?其中存储的数据可以是文件、关系数据库、实时数据库;存储格式包括文件格式、数据库图表;数据分布及同步,以及数据管理。比如读写操作的性能、存储容量限制、有无并非读写、事务回滚操作等等。通常要求读的时候比较快,而写的时候比较稳。
|
||||
|
||||
- 确定数据的类型/来源分类,以及数据模型的设计,即代码和内存中的数据结构,如图 13.4.5 中的“业务实体模型”;以及它们之间的联系,比如数据流的方向,要求与数据存储有关的周边模块要同时绘制出来。
|
||||
|
||||
- 消息队列、缓存等组件(中间件)也需要绘制出来,因为它们也是数据架构设计的一部分,是为了保证性能等质量属性的。
|
||||
|
||||
|
||||
### 13.4.6 软件开发架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
**开发、实现、代码**,这些是描述编写代码和技术实现的词汇。
|
||||
|
||||
按照软件工程,有了静态和动态设计后,在数据密集型应用中再有了数据存储设计,就可以进行软件开发了,所以需要有**软件开发架构**来指导、统一开发者的行为。如 (R)开发视图、(U)实现模型、(C)代码图、(T)技术架构、(O)开发架构,都是用于描述源代码组织方式、使用的框架组件、依赖关系等等。见图 13.4.6。
|
||||
|
||||
<img src="img/Slide20.SVG"/>
|
||||
|
||||
图 13.4.6 软件开发架构图
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
在汽车生产中,这一部分相当于是生产(自己写代码)或购买(使用第三方框架)零配件,比如发动机、变速箱、方向盘、座椅、轮胎等等,数量可以达到上万个。在哪个车间生产哪种零配件,先后顺序是什么(有依赖关系),如何运输到装配车间等等,都有全套的计划。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
软件开发架构 = 代码结构 + 依赖关系 + 框架组件 \tag{13.4.5}
|
||||
$$
|
||||
|
||||
在图 13.4.6 中:
|
||||
|
||||
- 我们使用了各种框架,如 ASP.NET、Spring Cloud、Data Bus。
|
||||
- 这些框架下面有很多技术可以选择使用,比如 ASP.NET 下面有 MVC 和 Web API,Spring Cloud 下面有 Eureka、Zuul 等很多可选组件。
|
||||
- 使用这些框架,需要有对应的开发工具和编程语言,比如在 Visual Studio 里面用 C#。
|
||||
- 用这些开发工具编译出来的安装包(业务包、服务包、数据访问包等等名字只是个代号,它们都有自己特定的名字)需要部署在物理节点上运行。
|
||||
- 一般情况下,甲方都要求有二次开发的 SDK 或 API,尽管它们从来不会用到。
|
||||
|
||||
软件开发架构关注程序包,不仅仅是我们自己写的程序,还包括应用程序依赖的SDK、第三方类库、中间件等。尤其是像目前主流的Java、.NET等依靠虚拟机的语言和平台,以及主流的基于数据库的应用,都会比较关注。和逻辑功能架构有紧密的关联。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 首先要确定开发语言和开发工具。通常的情况是,一个开发团队熟悉什么语言,就使用什么语言开发,这是错误的。在笔者曾经“被迫”参与的一个小项目中,最底层用 C++ 做 CUDA 编程,用户接口 API 层用 Python,内部的通信用了 GO,而笔者负责的 Pipeline 用纯 YAML,说实话有点儿过分。一个开发者在职业生涯中至少要会三种以上的主流开发语言。
|
||||
|
||||
- 确定程序单元以及组织结构(即代码结构)。其中程序单元包括:源文件、配置文件、程序库、框架、目标单元;程序单元组织包括 project 划分、project 目录结构、编译依赖关系。但是在框架阶段可以粗略,在概要设计阶段再做细致一些。
|
||||
|
||||
- 确定需要使用的框架、组件、第三方库等等。建议小的项目尽量不依赖这些东西,而大的项目要有选择地使用。在上述的“被迫”参与的小项目中,有人使用了 Linux 上一个编译后的尺寸有 2M 的开源 C++ 库,但是只为了其中一两个函数调用。
|
||||
|
||||
- 制定开发政策和标准,如代码库权限管理、编码规范、测试策略、集成方式、部署流程等。其中 CI/CD 持续开发/持续集成的 Pipeline 必不可少,以满足开发期质量需求,包括:代码可读性、可维护性、可复用性、可测试性、可扩展性、可移植性等。
|
||||
|
||||
### 13.4.7 物理部署架构
|
||||
|
||||
#### 1. 关键词理解
|
||||
|
||||
**物理、环境、技术**,这些是描述网络节点和硬件环境的词汇。
|
||||
|
||||
开发完毕后才能进行产品部署,虽然是最后的动作,但是需要在前几步就有统一考虑,而不是等软件开发完毕后再想。这一部分叫做**物理部署架构**,主要是设计师根据系统非功能质量的要求做出的设计。如 (R)物理视图、(U)环境模型、(T)技术架构、(O)物理架构。见图 13.4.7。
|
||||
|
||||
<img src="img/Slide21.SVG"/>
|
||||
|
||||
图 13.4.7 物理部署架构图
|
||||
|
||||
【以汽车为例】
|
||||
|
||||
所有的机械零件、电子系统都生产完毕,需要拿到自动化生产线上去装配,先组装底盘等行驶子系统,再安装发动机变速箱等动力子系统,最后再安装座椅、气囊、仪表盘等用户子系统。最后还要试运行。
|
||||
|
||||
#### 2. 设计任务与关注点
|
||||
|
||||
【设计任务】
|
||||
|
||||
$$
|
||||
物理部署架构 = 技术选型 + 节点规划 + 拓扑关系 \tag{13.4.6}
|
||||
$$
|
||||
|
||||
物理部署架构,更关注的系统、网络、服务器等基础设施。例如:如何通过服务器部署和配置网络环境,来实现应用程序的“可伸缩性、高可用性”。或者举一个实际的例子,如何通过设计基础设施的架构,来保障网站能支持同时10W人在线、7*24小时提供服务,当超过10W人或者低于10W人在线时,可以很方便的调整部署架构来支撑。
|
||||
|
||||
该图中的所有连线都是表示网络连接。如图 13.4.7 中,从反向代理服务器有三条线发散出去,是分别与文件缓存、认证服务器、Web 服务器的网络直连,当然也可以在机房内网中配置特殊的网段。而对于虚线框内的服务器群,需要配置同一段的 IP 地址,便于内部访问。
|
||||
|
||||
除了用这种总线方式绘制以外,还可以用有层次的区块方式绘制,假设从上到下有 A、B、C 三层,每层中有很多组件,那么 A 层的组件只与 B 层的组件有网络连接,那么 B 层的组件只与 C 层的组件有网络连接。
|
||||
|
||||
【关注点】
|
||||
|
||||
- 确定物理节点和物理节点的拓扑结构,包括节点和群集的可靠性、可扩展性、可用性、安全性等。
|
||||
|
||||
- 技术选型。在哪个节点上选择哪种设备,计算机的 CPU 主频和数量、内存大小、硬盘大小、网卡的数量和速度等等、交换机的数量和端口数。
|
||||
|
||||
- 规划软件和硬件节点的对应关系,软件安装的步骤以及验证方法。在服务器、PC机、专用机上安装部署软件,根据开发人员的配置文档进行软件内的网络地址和端口的配置的规划。
|
|
@ -0,0 +1,216 @@
|
|||
|
||||
## 13.5 架构设计最佳实践
|
||||
|
||||
### 13.5.1 设计任务简化
|
||||
|
||||
#### 1. 六视图法
|
||||
|
||||
现在读者再回过头来看表 13.4.1,就可以理解这些名词的含义了。我们把它们再次总结成表 13.5.1。
|
||||
|
||||
表 13.5.1 架构设计任务与关注点小结
|
||||
|
||||
|顺序|设计步骤名称|设计任务|关注点|
|
||||
|-|-|-|-|
|
||||
|1|**业务场景架构**|系统划分 + 功能覆盖 + 外部关系|业务齐全,子系统划分合理,用户可以看懂|
|
||||
|2|**逻辑功能架构**|静态结构 + 功能模块 + 协作关系|功能全面覆盖业务需求,层级划分合理|
|
||||
|3|**运行过程架构**|进程设定 + 状态转换 + 调用关系|运行过程合理,接口清晰,运行期质量可控|
|
||||
|4|**数据存储架构**|内容格式 + 存储方式 + 读写控制|业务数据齐全,存储方式可靠,读写性能高|
|
||||
|5|**软件开发架构**|代码结构 + 依赖关系 + 框架组件|代码文件结构清晰,依赖关系明确|
|
||||
|6|**物理部署架构**|技术选型 + 节点规划 + 拓扑关系|系统、网络、节点的功能和性能|
|
||||
|
||||
按照软件工程的工作流程顺序,可以顺理成章地得到表 13.5.1 中的六个架构设计步骤,我们可以称之为**六视图法**,如图 13.5.1 右侧和式 13.5.1 所示。
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
架构设计 &= 业务场景架构 + 逻辑功能架构 + 运行过程架构
|
||||
\\
|
||||
&+ 数据存储架构 + 软件开发架构 + 物理部署架构
|
||||
\end{aligned}
|
||||
\tag{13.5.1}
|
||||
$$
|
||||
|
||||
但是,一般的情况下,开发人员无缘遇到这么复杂的系统,所以我们可以按照下面的简化步骤来做设计。如图 13.5.1 所示。
|
||||
|
||||
<img src="img/Slide22.SVG"/>
|
||||
|
||||
图 13.5.1 架构设计任务简化
|
||||
|
||||
#### 2. 两视图法
|
||||
|
||||
有些比较小的系统很难把上述架构图区分出来,可以只有两个元素组成:业务架构、技术架构。由此得到式 13.5.2:
|
||||
|
||||
$$
|
||||
\begin{cases}
|
||||
架构设计 = 业务架构 + 技术架构
|
||||
\\
|
||||
业务架构 = 业务场景架构
|
||||
\\
|
||||
\begin{aligned}
|
||||
技术架构 &= 逻辑功能架构 + 运行过程架构 + 数据存储架构
|
||||
\\
|
||||
&+ 软件开发架构 + 物理部署架构
|
||||
\end{aligned}
|
||||
\end{cases}
|
||||
\tag{13.5.2}
|
||||
$$
|
||||
|
||||
其中,业务架构即业务场景架构,技术实现架构的定义是表 13.5.1 中的 2,3,4,5,6 的加和。在第十二章中讲技术架构演化的故事时,基本上就是采用了这种办法。
|
||||
|
||||
在这种方法中:
|
||||
- 程序模块、数据存储、计算节点可以混在一起画,前提是在物理部署中的每个计算节点都可以明确地对应到一个程序模块上。比如在客户端-服务器模式中,客户端计算机运行的是客户访问模块,服务器上运行的是服务模块。
|
||||
- 运行过程可以不强调,在有联系的单元之间画一条线即可。
|
||||
- 软件开发可以不强调,还可以把依赖的框架、组件、中间件画在图中,而无需单独说明代码结构。
|
||||
|
||||
#### 3. 三视图法
|
||||
|
||||
如果对物理部署架构的要求较高,也可以把它从技术实现架构中拆出来,最后形成三个元素:业务架构、软件架构、硬件架构。由此得到式 13.5.3:
|
||||
|
||||
$$
|
||||
\begin{cases}
|
||||
架构设计 = 业务架构 + 软件架构 + 硬件架构
|
||||
\\
|
||||
业务架构 = 业务场景架构
|
||||
\\
|
||||
软件架构 = 逻辑功能架构 + 运行过程架构 + 数据存储架构 + 软件开发架构
|
||||
\\
|
||||
硬件架构 = 物理部署架构
|
||||
\end{cases}
|
||||
\tag{13.5.3}
|
||||
$$
|
||||
|
||||
同样,软件开发架构可以弱化。这种三视图法比较方便广大读者在任何规模的软件系统设计时使用。
|
||||
|
||||
在网络上的大多数架构图中,其实也是不知不觉中使用了这个实践方法,但是绘图的人或者传播的人自己还没有搞清楚,容易误导读者。一个有用的鉴别方法就是:当前后两张图风格不一致,或者项目不吻合时,基本上就是抄袭别人的,传播者按照自己的理解冠名,误人子弟。
|
||||
|
||||
#### 4. 其它组合方法
|
||||
|
||||
比如另外一种三视图法:业务架构,设计架构(= 逻辑功能 + 运行过程 + 数据存储),实现架构(= 软件开发 + 物理部署)。
|
||||
|
||||
如果强调数据的话,用四视图法:业务架构,设计架构(= 逻辑功能 + 运行过程),数据存储,实现架构(= 软件开发 + 物理部署)。
|
||||
|
||||
【最佳实践】不管用什么样的组合,也不管最终的视图个数,万变不离其宗,请读者记住最基础的六视图法,只要不遗漏其中的任何一个视图,就不会遗漏关注点,也就会保证设计的完整性。
|
||||
|
||||
### 13.5.2 让架构设计走下神坛
|
||||
|
||||
本小节中,我们要让架构设计这个高大上的名词走下神坛。为什么这么说呢?
|
||||
|
||||
在 13.1 节中,我们设定的目标就是总结出一套方法论,来应用到架构设计中。但由于所讨论的问题始终处于软件工程设计范畴,所以这套方法论同样可以用到中小型系统或者概要设计、详细设计中。这也就是为什么我们本章的标题是“概要设计”,但是却讨论了很多“架构设计”。
|
||||
|
||||
在表 13.1.2 中已经列出了一些设计的基本概念,在此我们结合 13.4 中学到的关于设计任务与关注点的知识再扩充一下,形成表 13.5.2。注意,在表 13.5.2 的设计任务中,我们把“xxxx架构”变成了“xxxx设计”(比如:业务场景**架构** $\rightarrow$ 业务场景**设计**),这样的话,这个设计任务就可以适用于概要设计和详细设计了。
|
||||
|
||||
表 13.5.2 各种系统设计任务
|
||||
|
||||
|系统设计|架构设计|概要设计|详细设计|
|
||||
|:-:|-|-|-|
|
||||
|系统规模|只应用于大型系统|主要应用于中型系统|主要应用于小型系统|
|
||||
|设计负责人|架构师|技术专家|开发人员|
|
||||
|用途|确定轮廓,系统分解|确定功能,模块分解|确定细节,函数分解|
|
||||
|输出文档|架构设计说明书|概要设计说明书|详细设计说明书|
|
||||
|**设计任务**|关注子系统划分|关注模块功能分配|关注代码实现细节|
|
||||
|业务场景设计|业务场景架构图|用户与功能之间的关系<br>(用例图、数据流图)|N/A|
|
||||
|逻辑功能设计|逻辑功能架构图|各个模块的层次、位置、作用<br>(类图)|具体的类定义<br>(对象图)|
|
||||
|运行过程设计|运行过程架构图<br>(可选)|各个模块之间的调用、交互关系<br>(时序图、协作图、活动图)|详细的状态转换定义<br>(状态图)|
|
||||
|数据存储设计|数据存储架构图<br>(可选)|数据库数据表数据文件定义<br>(类图)|详细的字段定义<br>(表格)|
|
||||
|软件开发设计|软件开发架构图<br>(可选)|代码文件组织、编译、框架选择<br>(组件图、包图)|N/A|
|
||||
|物理部署设计|物理部署架构图<br>(可选)|硬件中部署的软件及物理架构<br>(部署图)|N/A|
|
||||
|
||||
|
||||
|
||||
1. 架构设计
|
||||
|
||||
在 13.1 节中也讲过,对于大型系统,我们首先要做子系统划分,然后在此基础上做架构设计,设计任务就是六视图法(或者两视图法、三视图法、其它组合方法)。在这个层次上,设计元素都是大尺寸的子系统,所以很多细节可以留到后面的概要设计中去做,而只关心各个子系统公共的部分。
|
||||
|
||||
这个阶段要说明产品分哪些应用系统,应用系统间是如何集成的,考虑两点:
|
||||
- 子系统间的关系。
|
||||
- 可复用的组件或模块进行下沉,沉淀到平台层,为业务组件提供统一的支撑。
|
||||
|
||||
这一阶段的产出是《架构设计说明书》,实际上就是 13.4 节中的内容。当然,除了要画图外,还要给出对应的文字说明。
|
||||
|
||||
2. 概要设计
|
||||
|
||||
读者可以看到,概要设计实际上是工作量最大的设计阶段,承上启下,对六个设计任务都有详细的要求。
|
||||
|
||||
这一阶段的产出是《概要设计说明书》,将在 13.6 节中给出。
|
||||
|
||||
3. 详细设计
|
||||
|
||||
详细设计只针对具体模块,开发人员不必关系太多的外部的东西,比如可以忽略业务需求;在指定的项目目录中添加代码文件,所以软件开发设计也可以忽略;具体部署到什么地方也不需要知道。
|
||||
|
||||
这一阶段的产出是《详细设计说明书》,将在第十四章中给出。
|
||||
|
||||
总体看这三个阶段的任务,从任务量上看是一个枣核形,两头小中间大:
|
||||
- 第一阶段最关键,需要抽象思维能力和统筹全局的能力;
|
||||
- 第二阶段最艰巨,需要较深的技术积累、较强的逻辑思维能力和文字表达能力;
|
||||
- 第三阶段最细致,需要过硬的算法能力和编码能力。
|
||||
|
||||
### 13.5.3 阶段/职责/任务
|
||||
|
||||
在长期的软件工程实践中,笔者还总结出了一种划分方法,是按照不同阶段的职责划分的。如图 13.5.2 所示。
|
||||
|
||||
<img src="img/Slide23.SVG"/>
|
||||
|
||||
图 13.5.2 按阶段和职责划分的任务
|
||||
|
||||
这是按照完整的软件工程的顺序来划分的,不同阶段的任务分配给产经理、架构师、技术专家、开发人员、工程人员。整体分成三个部分:
|
||||
|
||||
1. 黄色的**需求部分**,由产品经理和架构师负责。
|
||||
|
||||
- 产品经理(PM)负责用户视图(User View)部分。
|
||||
- 架构师(Atchitect)负责场景视图(Scenario View)部分。
|
||||
|
||||
2. 绿色的**设计部分**,根据系统规模,由架构师或技术专家或开发人员负责。
|
||||
|
||||
- 逻辑视图(Logic View)完成逻辑功能设计任务。
|
||||
- 过程视图(Process View)完成运行过程设计任务。
|
||||
|
||||
3. 青色的**实现部分**,由工程师负责,注重软硬件的实现环节。
|
||||
|
||||
- 开发视图(Development View)由开发人员(包括技术专家)负责。
|
||||
- 物理视图(Physical View)由工程人员负责,与架构师及技术专家共同协商。
|
||||
|
||||
它与标准的六视图法的区别是:
|
||||
|
||||
1. 它更像 RUP 4+1 视图,只不过变成了 2+2+2。
|
||||
2. 没有单独的数据存储设计任务,而是把它归入到逻辑视图(Logic View)中。
|
||||
3. 把产品经理的任务规划在内,划分产品经理和架构师的职责。
|
||||
|
||||
|
||||
### 13.5.4 误区
|
||||
|
||||
#### 设计一个能为 1 亿用户服务的系统
|
||||
|
||||
在第十二章中的 12.1 节,我们已经用一个“技术架构演化的故事”来讲述了一个软件系统的发展过程,这虽然是一个故事,但是在软件领域确实普遍存在的事实。最开始的淘宝、京东、微信、微博,也都是只有十几台服务器为几万人服务,谁也不会那么傻一开始就搞几百台服务器在那里蓄势待发。等用户数量增加了,再成倍地增加服务器的数量,扩大系统规模。
|
||||
|
||||
所以,我们可以拥有为 1 亿用户服务的系统架构知识,但是不一定能用上。系统讲究“可伸缩性”,很多人只看到了“伸”,没有想到“缩”。现在的云服务如 Azure,为系统伸缩提供了非常便利的条件。
|
||||
|
||||
另一方面,有些技术在高并发大容量时才能呈现出优势来,比如吞吐量和稳定性,但是在小规模系统中还不如另外一个简单的架构模式有效。比如事件驱动模式,面对几千用户时,本来可以用管道-过滤器模式来简单实现,可以实现几十毫秒级别的处理速度,但是用事件驱动模式时,至少要花 1 秒的时间。当用户量增至几十万时(可能在该软件生命周期内不会发生),管道-过滤器模式要用10秒,事件驱动模式还是 1 秒。
|
||||
|
||||
虽然在 13.1 节的最开始讲架构概念的时候,我们使用了建筑行业来做类比,但是,建筑是永恒的,而软件架构是可以变化的。老板们当然愿意拥有一个为 1 亿用户提供服务的系统,但是架构师不能因此就好高骛远地顺着老板说话,非要把微信、淘宝的架构照抄过来用在校园网上。
|
||||
|
||||
#### 一定要用最新的架构模式来构建服务
|
||||
|
||||
第十二章中列出了很多架构模式,有些是传统的,有些是比较新的。
|
||||
|
||||
近几年微服务架构盛行,大家都想使用微服务。大多数都看到了它的优点,但忽略了它的缺点,原因是没有用过,不知道风险有多大,只看到了好的一面。
|
||||
|
||||
Microsoft Office 套件中的 OneNotes 后台就是用微服务开发的,它像一张白纸,可以在上面任何位置随意写任何支持的格式的文档,比如可以打字、写字、绘图、手绘图,还可以嵌入 Word,Excel 的文档并直接显示出来,就和在人们在纸质的笔记本上用圆珠笔记录东西一样方便,没有任何格式要求。
|
||||
|
||||
系统呈现这种便利性的同时,带了的是后台的复杂逻辑,所以项目组决定用微服务架构开发。在开发之初,大家觉得很爽,增加一个新的服务时,根本不用触动旧的代码,而且可以用任何语言编写,只要能部署到微服务平台上并提供 RESTful API 调用接口即可。
|
||||
|
||||
渐渐的,当需要维护以前的代码或者系统扩容时,比起单结构的代码来要复杂很多倍,所有东西都要拷贝一份,旧的版本还不能轻易扔掉,因为用户数据存储还是用的旧格式,于是,旧版本越来越多。而新增加的微服务也越来越多,已经不能用简单的 Excel 表来做统计了。
|
||||
|
||||
笔者相信,如果让 OneNotes 项目组重新设计系统架构,他们一定会引入别的架构模式(比如插件模式或者黑板模式)。
|
||||
|
||||
#### 等前期架构设计完善后再开始后续工作
|
||||
|
||||
我们又要拿建筑行业来做类比了。在建筑行业里,必须等到前期的设计完全结束,拿到一整套图纸后才能开始后续的工作。而且这套图纸就再也不能改动了,因为所有的建筑材料都是按图纸上的尺寸加工、购买的。
|
||||
|
||||
前面我们也说了,建筑是死的,软件是活的。在软件行业中,架构师不能为了体现自己的存在价值而做过度设计,也不能害怕别人挑刺而不停地迭代自己的设计,甚至把属于概要设计的工作也做了,“走别人的路让别人无路可走”。
|
||||
|
||||
一名架构师一定是从底层开发做起,慢慢成长起来的,所以 ta 也一定懂得后续的一系列工作该如何做。如果后续的工作有人做,就不要剥夺人家的权力;相反,如果后续的工作没人做,那么架构师也应该可以自己承担,能上能下。但无论如何都是要跟随项目一起走的,一直到系统部署上线运行。所以在前期不必花不必要的时间,非要把架构设计搞得尽善尽美才放手。
|
||||
|
||||
笔者以前曾经当过一段时间得设计师,只写文档,不写代码。当时就是为了对得起老板的信任,还有责任感(或者是面子),花很长时间把设计文档写得完美无比,每个字都要斟酌像是写书一样,每张图都要画得和机械制图同样的标准。但其实人家负责实现的开发人员也不是傻子,他们有很强的纠错能力。如果有些事情遗漏了,只要在每日例会上补充就好,然后再补到文档里,整个项目不会因为这些就走弯路。
|
||||
|
||||
简单的设计不等于是不好的设计,反而是给后面的变化留出空间;完美的设计不等于是好的设计,因为后面可能要修改。另外,简单的设计在初期也可以降低成本,帮老板省钱。
|
||||
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
|
||||
## 13.6 概要设计说明书
|
||||
|
||||
### 13.6.1 对一个标准模板的分析
|
||||
|
||||
以下模板是在众多概要设计模板中相对靠谱的一个,我们可以按照从 13.5 中得到的设计任务知识来审视这个模板,以便让领读者理解它到底要求我们开发人员做哪些事情。
|
||||
|
||||
下面的黑体字部分是原模板中的内容,【最佳实践】是笔者的建议。
|
||||
|
||||
#### 1. 引言
|
||||
|
||||
- **1.1 编写目的;1.2 项目背景;1.3 定义;1.4 参考资料。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
这一部分的 1.1 和 1.2 基本上就是套话,没有必要写;1.3 可以挪到后面写,主要是一些专有名词定义;1.4 一般情况下不用写,除非有一个重大的知识背景只有你知道,别人都不知道,但是对这个项目又特别关键,才应该在此强调。
|
||||
|
||||
#### 2. 任务概述
|
||||
|
||||
- **2.1 目标;2.2 运行环境;2.3 需求概述;2.4 条件与限制。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
2.1 是套话,不写;2.2 运行环境相当于**物理部署设计**,可以放到后面,但是在这里也可以当作条件和限制(需求的一部分)简单说一下;2.3 和 2.4 可以保留,做为需求应该放在前面,即**业务场景设计**。
|
||||
|
||||
#### 3. 总体设计
|
||||
|
||||
- **3.1 基本设计概念和处理流程;3.2 总体结构和模块外部设计;3.3 功能分配;3.4 功能需求和程序的关系;3.5 尚未解决的问题。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
3.1 有些模糊,不知道应该写什么;3.2 和 3.3 相当于**逻辑功能设计**;3.4 相当于**软件开发设计**,即模块和代码文件的对应关系;3.5 可以放到前面的任务概述中去。
|
||||
|
||||
#### 4. 接口设计
|
||||
|
||||
- **4.1 用户接口;4.2 外部接口;4.3 内部接口。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
4.1 用户接口应该在 designer 给出的界面与交互设计中单独提供,如果简单到只有一两个界面的话,也可以画在这里;4.2 是要描述系统内部的接口,因为前面有了模块,所以现在需要有模块间的交互,即接口;4.3 是系统对外的接口。
|
||||
|
||||
但是在 13.5 节中,我们并没有提到过接口设计,这是为什么呢?因为接口是连接静态结构与动态行为之间的桥梁,所以在讨论**运行过程设计**之前就把接口提出来,在顺序上不容易理解。所以笔者认为可以把 6 提到前面来,和 4 合并在一起。这样就和 13.5 中的六视图法完全一致。
|
||||
|
||||
#### 5. 数据结构设计
|
||||
|
||||
- **5.1 逻辑结构设计;5.2 物理结构设计;5.3 数据结构与程序的关系。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
5.1 和 5.2 即**数据存储设计**;5.3 应该属于**软件开发设计**。
|
||||
|
||||
#### 6. 运行设计
|
||||
|
||||
- **6.1 运行模块的组合;6.2 运行控制;6.3 运行时间。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
即**运行过程设计**,放到 4 中和接口设计一起提供。
|
||||
|
||||
#### 7. 出错处理设计
|
||||
|
||||
- **7.1 出错输出信息;7.2 出错处理对策。**
|
||||
|
||||
【最佳实践】
|
||||
|
||||
这一部分的内容其实更应该写在《用户手册》里。
|
||||
|
||||
### 13.6.2 建议的《概要设计说明书》模板
|
||||
|
||||
下面的黑体字部分是本书建议的《概要设计说明书》的模板的标题,其它部分都是属于【最佳实践】性质的的说明性文字。读者在使用前需要删除这些说明性文字,然后填写与项目有关的内容。
|
||||
|
||||
在微软,通常把《概要设计说明书》称作 Dev Spec 或 Design Spec,因为这是 Developer 要完成的任务。如果软件规模大的话,通常由 Dev Manager 或 Dev Lead 来撰写,如果是一个局部功能,就由 IC(Individual Contributor,即开发人员)来撰写。很少要求写《详细设计说明书》,或者只要求写一个 One-Page Spec(简单的单页设计文档)。
|
||||
|
||||
概要设计说明书是概要设计阶段完成后的输出,需要撰写人员讲解、项目组成员集体评审后通过并执行。
|
||||
|
||||
#### 1. 概述(Overview)
|
||||
|
||||
**1.1 任务概述**
|
||||
|
||||
可以摘抄需求规格说明书(PM Spec)的主要内容,用于说明项目背景。
|
||||
|
||||
**1.2 术语解释(Terminology)**
|
||||
|
||||
对与设计领域相关的、或者程序内部模块或子系统命名的术语加以说明。
|
||||
|
||||
**1.3 目标和非目标(Goal/Non-Goal)**
|
||||
|
||||
列出本设计关注的目标,以及不属于本设计范畴之内的但是有关联的项目。比如:
|
||||
|
||||
- 目标:通过简化文件内容设计来提高文件到磁盘的存取速度。
|
||||
- 非目标:对于标准格式的文件(如PDF)的简化不在此设计之内。
|
||||
|
||||
**1.4 尚未解决的问题(Open Issue)**
|
||||
|
||||
说明在概要设计过程中尚未解决、而设计者认为在系统完成之前必须解决的各个问题。可以是技术或其它方面的问题。比如:
|
||||
|
||||
- 如何处理过期的用户(如退休的教师和毕业的学生)的用户记录,目前还没有考虑。
|
||||
- PDF 阅读与标记控件,在 Windows Form 或 XAML 应用程序中不能工作。
|
||||
- 桥牌模型很有可能在 3 个月内无法训练出来,或者达不到预期要求的质量。
|
||||
|
||||
#### 2. 总体结构设计(Architecture)
|
||||
|
||||
即**逻辑功能设计**。推荐的方式有:
|
||||
|
||||
- 分层设计
|
||||
|
||||
分层,首先需要功能分类,相同类别的功能可以构成大模块或者子系统。最流行的三层结构,即表示层、业务逻辑层、数据访问层,可以普遍适用到所有软件架构中。因为在三层结构的框架上,最上层总是用户接口,最下层总是数据存储,当然如果你的软件是一个局部模块开发,就不需要三层结构,而是要简单的多,那么基本上也不用分层设计了。
|
||||
|
||||
- 模块/子系统设计
|
||||
|
||||
各个软件系统不同的地方在于中间的业务逻辑层,可以有很多变化,比如:
|
||||
|
||||
- 可以再细分成多层
|
||||
- 可以在同一层分割成很多模块
|
||||
- 可以是外部系统
|
||||
|
||||
这里并不涉及模块内部的细节(属于详细设计范畴),只考虑外部轮廓。
|
||||
|
||||
这一部分要用框架图绘出,加文字描述。
|
||||
|
||||
#### 3. 运行过程设计
|
||||
|
||||
**3.1 用户接口**
|
||||
|
||||
这是一个独立的文档,在第十章中有具体讲解。一般包含:
|
||||
|
||||
- 用户界面设计图
|
||||
|
||||
业内成为 Redlines(红线图),因为红色很少用于界面元素中,所以用红线来做标记。
|
||||
|
||||
- 用户交互流程图
|
||||
|
||||
用户在界面A上点击不同的按钮后,会跳到不同的新页面,从界面A的各个按钮上会引出一条条红线到其它界面。
|
||||
|
||||
由 Designer 完成,交给开发人员实现。
|
||||
|
||||
**3.2 运行模块组合**
|
||||
|
||||
说明对系统施加不同的外界运行控制时所引起的各种不同的运行模块组合,说明每种运行所历经的内部模块和支持软件。
|
||||
|
||||
如果流程复杂,可以用表格或文字的形式展现,避免绘制复杂的图形。如果绘图,可用流程图、时序图等。
|
||||
|
||||
**3.3 进程/线程**
|
||||
|
||||
进程、线程的实现存在于代码中,但是如果不运行起来,非常难以识别。即使运行起来,人们也是无法“看到”其运行过程。
|
||||
|
||||
进程、线程设计包括:
|
||||
|
||||
- 名称
|
||||
- 作用
|
||||
- 生命周期(启动、停止条件)
|
||||
- 实现机制(进程、线程、服务,或者是 Azure 上提供的一些其它计算服务)
|
||||
- 运行物理环境(架构设计中的哪个服务器或者子系统)
|
||||
|
||||
这一部分用文字描述即可,放在表格内。
|
||||
|
||||
**3.4 内部接口**
|
||||
|
||||
内部接口,一般包含以下几种形式:
|
||||
|
||||
- 对象方法调用或函数调用
|
||||
- 进程间通信(管道或者套接字)
|
||||
- RESTful API
|
||||
- 消息队列
|
||||
|
||||
内部接口众多,只描述大模块之间的接口即可。可以用两种图来表示:分层关系接口图,组件交互时序图。
|
||||
|
||||
**3.5 外部接口**
|
||||
|
||||
系统访问其它外部系统的出口,或者被外部系统访问的入口,一般是 HTTP 形式。
|
||||
|
||||
#### 4. 数据存储设计
|
||||
|
||||
数据设计,一般是指对永久存储的数据的设计,包含数据库和数据文件两类。
|
||||
|
||||
**4.1 数据库设计**
|
||||
|
||||
一般是指关系式数据库。主要涉及的内容包括:
|
||||
|
||||
- 表
|
||||
- 主键、外键、索引
|
||||
- 存储过程
|
||||
|
||||
**4.2 数据文件设计**
|
||||
|
||||
一般是从类的属性做序列化得到数据文件,可能包括的数据文件类型有:
|
||||
|
||||
- 二进制
|
||||
- 纯文本
|
||||
- XML 扩展标记语言
|
||||
- JSON 对象标记语言
|
||||
|
||||
如果是后两者,则格式中自带信息描述,就不用写设计文档了。如果是二进制文件,必须按字节描述文件格式。如果是纯文本格式,一般是按行分开记录,即 \r\n,使用 \tab 或逗号分割字段。
|
||||
|
||||
**4.3 数据结构/接口设计**
|
||||
|
||||
传统教材并不包含此类别的数据设计要求。
|
||||
|
||||
由于计算机软硬件技术的发展,有很多类型的数据是内存或网络中的临时运行用数据,比如:
|
||||
|
||||
- 栈
|
||||
- 堆
|
||||
- 数据包
|
||||
- 消息队列
|
||||
|
||||
消息队列组件,队列中的数据平时是在内存中的,为了怕丢失任务,所以组件把内存中的数据也存储在了磁盘上。
|
||||
|
||||
这类数据如果是绑定在数据发送或接收方的对象上(作为属性),则可以在类设计(属于详细设计范畴)里包含。如果是比较独立的,既不从属于数据发送方,也不从属于数据接收方,则需要单独在这里设计。
|
||||
|
||||
#### 5. 软件开发设计
|
||||
|
||||
用一张表说明,各项需求的实现同各程序模块的分配关系。
|
||||
|
||||
如果还有其它关于开发步骤、流程的说明,可以记录在这里。
|
||||
|
||||
#### 6. 运行环境设计
|
||||
|
||||
描述软件分发/上线后的运行环境:
|
||||
|
||||
- 软件运行环境
|
||||
|
||||
如 iOS 或 Android 智能手机系统、Windows 系统(台式机、HoloLens、Xbox、Surface Hub)、Linux 或 Mac 系统等。
|
||||
|
||||
- 硬件运行环境
|
||||
|
||||
比如:智能手机、独立计算机、服务器、虚拟机、云端服务等,应包含对内存、硬盘、网络的要求。
|
||||
|
||||
|
||||
- 网络系统设计
|
||||
|
||||
如果是分布式的系统,或者是涉及到多方通信的系统,包括对外部系统的依赖,都需要加入网络系统设计。包含:
|
||||
- 选择操作系统
|
||||
- 确定网络系统配置
|
||||
- 制定网络拓扑结构
|
||||
|
||||
|
||||
#### 7. 发布与维护设计
|
||||
|
||||
**7.1 日志(Telemetry)**
|
||||
|
||||
这个工作其实应该是 PM 的工作,定义哪一个用户行为需要记录日志,比如:
|
||||
|
||||
- 打开应用
|
||||
- 点击按钮
|
||||
- 打开某个网页
|
||||
- 收藏了某个网页
|
||||
- 关闭应用
|
||||
|
||||
这些日志会最终发送到服务器上,作为用户行为分析的数据源。
|
||||
|
||||
**7.2 试验(Flighting)**
|
||||
|
||||
这是对已经上线的系统的增量更新。
|
||||
|
||||
1. 事先在系统中实现一个功能 A;
|
||||
2. 如果你的系统是一个基于浏览器的服务,那么在服务器上可以随机地让某些用户看到功能 A 并使用,另外一些用户看不到;
|
||||
3. 如果是基于手机的 App 应用,应用商店会有这种“只给10%的用户提供试验性功能下载”的功能,其它用户收不到新 App 下载通知。
|
||||
|
||||
|
||||
**7.3 计划与安排(Timeline/Work Item)**
|
||||
|
||||
根据项目大小,有可能是多人合作完成,或者是单人独立完成:
|
||||
|
||||
- 多人合作
|
||||
|
||||
列出分工及计划,如:
|
||||
|
||||
|人员|工作|初步计划|
|
||||
|--|--|--|
|
||||
|木头|管理子系统|3周|
|
||||
|石头|推理子系统|4周|
|
||||
|肖哥|训练子系统|5周|
|
||||
|毛毛|存储接口模块|2周|
|
||||
|
||||
- 单人完成
|
||||
|
||||
列出模块划分、完成时间。
|
||||
|
||||
|模块名称|初步计划|
|
||||
|--|--|
|
||||
|Azure Blob 数据读写模块|2天|
|
||||
|Flask Restful 服务|10天|
|
||||
|发送邮件模块|3天|
|
||||
|开关 Azure 虚拟机模块|5天|
|
||||
|
||||
**7.4 中止机制(Exit Criteria)**
|
||||
|
||||
遇到何种情况时,即停止开发工作、放弃项目。
|
||||
|
||||
如:
|
||||
|
||||
- 甲方公司使用的网络环境发生变化,导致前期的开发工作浪费。
|
||||
- Windows 10 下一个发布计划中不再支持对浏览器的内置支持,所以依赖其开发的浏览器项目应该停止。
|
||||
|
|
@ -0,0 +1,379 @@
|
|||
|
||||
## 13.7 Edge浏览器概要设计说明书
|
||||
|
||||
在本节中,我们以手机 Edge 浏览器开发为例,书写一份完整的概要设计说明书。以下是正文(这是一个真实的案例),其中有一些用【最佳实践】开头的段落属于笔者的解释性文字。
|
||||
|
||||
### 1. 概述(Overview)
|
||||
|
||||
#### 1.1 任务概述
|
||||
|
||||
由于 Chrome 浏览器的各项指标普遍优于 Edge 浏览器,所以我司决定使用 Chromium 开源软件作为基础,搭建新的 Edge 浏览器。但是在正式开始之前,应该先在手机浏览器上试水,摸索出使用 Chromium 过程中的各种“坑”,验证它的能力。
|
||||
|
||||
所以,要使用 Chromium 在 iOS/Android 手机上各开发一个 Edge 浏览器软件,设计相同,以保证用户体验相同。最后在各自的应用市场上架供最终用户下载使用。
|
||||
|
||||
手机 Edge 浏览器的业务场景如图 13.7.1 所示。
|
||||
|
||||
【最佳实践】业务场景中的功能要分组,便于对该领域的知识有透彻的理解。
|
||||
|
||||
<img src="img/Slide24.SVG"/>
|
||||
|
||||
图 13.7.1 业务场景
|
||||
|
||||
从业务场景来看,用户从该 App 的界面上可以看到以下 6 组功能:
|
||||
|
||||
1. 用户输入功能
|
||||
|
||||
提供用户在网址栏中的文字、语音、二维码输入功能;如果输入的不是合法的网址,会看作是搜索词;如果输入的是预定义的 URI,可以启动本地的对应 App。
|
||||
|
||||
2. 浏览引擎功能
|
||||
|
||||
包括主框架的外观、网页标签(Tab)管理、显示弹出式菜单、进入到私有浏览模式(不留历史记录)、进入大界面的桌面模式。其中,接力到 PC 功能是独有的,可以把手机 Edge 浏览器上的工作用内置协议在 Windows 的 Edge 浏览器上继续显示,而无需重新输入网址。
|
||||
|
||||
3. 页面浏览功能
|
||||
|
||||
提供基本的卷滚、缩放、网页上的文字查找、前进后退等页面导航功能;起始页功能可以在打开一个空白网页时,定义缺省的网址;语音朗读功能可以读出网页上的问本内容。
|
||||
|
||||
4. 辅助工具集
|
||||
|
||||
辅助工具集中有任务管理器、文件下载管理、开发者工具(F12功能)、对扩展控件的支持、安全浏览、隐私保护等功能。
|
||||
|
||||
5. 页面操作管理
|
||||
|
||||
这里可以收藏、查看浏览历史、保存网页到本地、打印、分享等,页面合集可以提供一些特殊的分组内的功能。
|
||||
|
||||
6. 个人信息管理
|
||||
|
||||
账号、密码、表单等是为了再次重复在一个网页上登录时,免除重新填写信息和密码的麻烦;常用网站可以定制,方便再次浏览;更多信息可以在设置、帮助里看到,也可以提供用户反馈。
|
||||
|
||||
#### 1.2 术语解释(Terminology)
|
||||
|
||||
- Chromium:谷歌公司的浏览器开源软件框架,可以在上面进行二次开发,提供定制的用户体验。
|
||||
|
||||
- NTP:New Tab Page,打开一个新的空白页面时的逻辑。
|
||||
|
||||
- OOBE:第一次启动时的 App 安装设置逻辑。
|
||||
|
||||
- Roaming:漫游,用户可以在不同的机器上同步 Edge 浏览器的设置。
|
||||
|
||||
- Hub Page:一种比较大的页面切换框架。
|
||||
|
||||
- MSA SSO:微软账户的单点登录。
|
||||
|
||||
#### 1.3 目标和非目标(Goal/Non-Goal)
|
||||
|
||||
目标:
|
||||
- 使用 Chromium 在 iOS/Android 手机上开发 Edge 浏览器软件。
|
||||
- 可以兼容国内主流的 Android 手机型号。
|
||||
|
||||
非目标:
|
||||
- 暂不考虑在 PC 上的 Edge 浏览器开发。
|
||||
|
||||
#### 1.4 尚未解决的问题(Open Issue)
|
||||
|
||||
- 暂不考虑与 PC 端 Edge 浏览器做数据同步的问题。
|
||||
|
||||
### 2. 总体设计(Architecture)
|
||||
|
||||
系统的总体设计如图 13.7.2 所示,该图展示的是该系统的逻辑功能架构。
|
||||
|
||||
<img src="img/Slide25.SVG"/>
|
||||
|
||||
图 13.7.2 逻辑功能
|
||||
|
||||
|
||||
从上到下一共分为四层:
|
||||
|
||||
1. 用户界面层
|
||||
|
||||
包括三个功能组:一般性的浏览体验,搜索入口,其它辅助功能。这些都是用户可以直接看到或日常使用的交互功能。这一层完全用 Java 语言在 Chromium 外围开发或者修改。
|
||||
|
||||
2. 框架和支持层
|
||||
|
||||
提供基础框架供界面层使用,还有很多在交互过程中产生的用户数据,需要保存到本地数据库(文件)中。这一层也是用 Java,需要接触到 Chromium 较深层次的代码中。
|
||||
|
||||
3. 引擎和服务层
|
||||
|
||||
本层包括 Chromium 本身提供的浏览、渲染引擎,和需要额外定制的网络服务。其中,前一部分基本上不需要改动,后一部分是完全自定义的,需要在 Chromium 的 C++ 代码中做改动。
|
||||
|
||||
4. 基础层
|
||||
|
||||
基础层是一些外围辅助任务,完成非功能需求的质量保证工作,不包括在浏览器 App 的代码内,但是在整个开发过程中是不可或缺的。
|
||||
|
||||
【最佳实践】逻辑功能中的功能要分组,这样同一组的功能就可以由同一个/组开发人员来实现,效率最高。但是读者可以看到,这种分组和图 13.7.1 中的分组不完全相同,或者可以说完全不相同,图 13.7.2 中会出现很多底层支持的功能模块,用户在页面上是看不到的。
|
||||
|
||||
### 3. 运行过程设计
|
||||
|
||||
#### 3.1 用户接口
|
||||
|
||||
详见《手机Edge浏览器界面设计》文档(本书不提供)。
|
||||
|
||||
#### 3.2 运行模块组合
|
||||
|
||||
主要的运行模块组合如图 13.7.3 所示。
|
||||
|
||||
<img src="img/Slide26.SVG"/>
|
||||
|
||||
图 13.7.3 运行模块组合
|
||||
|
||||
本图从下向上看:
|
||||
|
||||
- 用户指定了网址后,Browser Engine 开始从 Cloud Service/Web Sites 抓取网页内容(Page Data)。
|
||||
- 传送给 Render Engine 进行渲染,并产生 Render View,显示给用户。
|
||||
- 这种行为在 Java 层(即业务逻辑层)发生,所以写入了 Telemetry,同时用户的一些其它操作也写入 Telemetry;Telemetry 要上传数据到 Telemetry Service。
|
||||
- 用户可以在网页上进行页面操作,如保存、打印等等。
|
||||
- 一些辅助工具(如历史记录、收藏夹、表单数据等)要通过个人数据管理从 Local DB 读取或写入。
|
||||
- Local DB 的内容定期漫游( Roaming),也是通过网络传递给远程的漫游数据服务。
|
||||
|
||||
#### 3.3 进程/线程
|
||||
|
||||
图 13.7.4 中,展示了页面浏览过程中所需要的主要进程和线程。
|
||||
|
||||
<img src="img/Slide27.SVG"/>
|
||||
|
||||
图 13.7.4 进程/线程设计
|
||||
|
||||
其中,有两类主要的进程:
|
||||
|
||||
1. Browser Process
|
||||
|
||||
包括一个主要的 Main Thread,和若干个工作线程完成辅助任务。浏览器把 WebContent 下载后,用 RenderViewHostManager 模块解析内容,然后决定产生几个 RenderViewHost 来做渲染工作,并通过 RenderProcessHost 创建对应的 Render Process 进程来具体执行。
|
||||
|
||||
2. Render Process
|
||||
|
||||
可以是多个进程,红色的 Main Thread 对应着 Browser Process 的 Main Thread 中红色的一个 RenderViewHost,而黄色对应着上方的两个 RenderViewHost,这是为多视图的页面而设计的。每个 WebContent 对应着一个 Render Process。
|
||||
|
||||
线程有很多,存活在父进程内,并行地完成独立的任务。如:
|
||||
|
||||
- Render Main Thread 完成对 Main Thread 的管理;
|
||||
- GPU Thread 完成使用 GPU 来做渲染;
|
||||
- Cache Thread 完成缓存查找或保存工作;
|
||||
- File Thread 完成文件下载工作;
|
||||
- DB Thread 完成数据库读写任务;
|
||||
- Process Launcher Thread 完成启动子进程的工作;
|
||||
- I/O Thread 完成与 Render Process 的通信工作。
|
||||
|
||||
|
||||
#### 3.4 内部接口
|
||||
|
||||
在图 13.7.5 中,描述了 Java/C++/Cloud 三层之间的漫游机制的内部时序及接口调用关系。
|
||||
|
||||
<img src="img/Slide28.SVG"/>
|
||||
|
||||
图 13.7.5 用运行时序图来描述内部接口
|
||||
|
||||
1. UI 界面写入本地数据库;
|
||||
2. 同时同步到 C++ 层的同步模块;
|
||||
3. 写入漫游数据库(数据2);
|
||||
4. 初始化漫游进程;
|
||||
5. 从 OneDrive 下载数据1;
|
||||
6. 数据1返回;
|
||||
7. 读取漫游数据2;
|
||||
8. 数据2返回;
|
||||
9. 合并数据1和数据2;
|
||||
10. 上传到 OneDrive;
|
||||
11. 成功;
|
||||
12. 数据更新成功通知;
|
||||
13. 更新本地数据库;
|
||||
14. 清理漫游数据2。
|
||||
|
||||
【最佳实践】在上述流程中,可以加入接口描述,如果是对象方法调用,就不用太细致了,留到详细设计再做;如果是远过程调用,可以再次描述。
|
||||
|
||||
【最佳实践】也可以根据图 13.7.3 运行模块组合中的逻辑来描述内部接口,因为每两个模块之间的连线都可以看作是一个内部接口。
|
||||
|
||||
#### 3.5 外部接口
|
||||
|
||||
根据图 13.7.3,在本软件中,外部接口一共有四种:
|
||||
|
||||
1. Telemetry 外部访问,通过 Asimov SDK 完成(下面有描述)。
|
||||
2. Smart Screen 外部访问,通过加密的 RESTful API 调用完成。
|
||||
3. 漫游数据的访问,通过 OneDrive SDK 完成。
|
||||
4. Bing Service 提供的是搜索服务接口,形式为 RESTful API。
|
||||
|
||||
### 4. 数据存储设计
|
||||
|
||||
本应用中有很多种数据需要存储,如图 13.7.6 所示。
|
||||
|
||||
<img src="img/Slide29.SVG"/>
|
||||
|
||||
图 13.7.6 漫游数据的存储处理
|
||||
|
||||
以下是需要存储的数据:
|
||||
|
||||
- Favorite 收藏夹
|
||||
- Reading List 阅读列表
|
||||
- Typed URL 输入过的合法网址
|
||||
- Password 密码
|
||||
- Top Sites 常用网站
|
||||
- Form Data 表单数据
|
||||
|
||||
在图 13.7.6 中,以 Sync Object 的派生类表示。其中橙色的部分是需要加密的。
|
||||
|
||||
#### 4.1 数据库设计
|
||||
|
||||
使用数据库的地方有两处:
|
||||
|
||||
1. 存储上述的 6 种本地数据,发生在 Java 代码层,叫做 Local DB;
|
||||
2. 这几种本地数据都需要漫游,所以会在 C++ 层同步一份,但这种同步是双向的,如果用户在另外一台机器上使用了 Edge 浏览器并用相同的账号登录,使用记录会同步过来,叫做 Roaming DB。
|
||||
|
||||
【最佳实践】这里可以列出每一种数据的表结构。
|
||||
|
||||
#### 4.2 数据文件设计
|
||||
|
||||
本应用中没有使用数据文件。
|
||||
|
||||
#### 4.3 数据结构/接口设计
|
||||
|
||||
需要强调的是,有两类数据需要加密,在图 13.7.7 中用橙色的元素表示:Password 数据和 Form Data 数据,避免明文存储和传输。
|
||||
|
||||
在 Java 用户界面层,可以读写本地的 Local DB 数据库,当发生写操作时,会同时向 C++ 层传输相同的数据,保存在 Roaming DB 中,然后由该层的同步机制定时与 OneDrive 同步。该 OneDrive 即用户登录的 MSA 账号所对应的云存储。
|
||||
|
||||
在 PC 端(即 Windows 10 Desktop),Edge 浏览器有同样的同步机制。
|
||||
|
||||
【最佳实践】如果内存中的数据结构与数据表的结构不同,或者还有一些特殊的设计,可以列在这里。略。
|
||||
|
||||
### 5. 软件开发设计
|
||||
|
||||
#### 5.1 版本更新策略
|
||||
|
||||
由于本项目基于 Chromium,而 Chromium 是一个开源项目,也在持续更新,所以本项目的开发在开始点上使用的是 Chromium 55 版本,但是后期要跟随 Chromium 的更新步伐,因为这些更新可以弥补以前的一些安全漏洞。
|
||||
|
||||
<img src="img/Slide30.SVG"/>
|
||||
|
||||
图 13.7.7 版本更新策略
|
||||
|
||||
图 13.7.7 展示了基线更新(Rebase)的策略,其中各种符合的含义如下:
|
||||
|
||||
- S:开始。
|
||||
- 55,58:Chromium 版本号。
|
||||
- R:Rebase。
|
||||
- M:Merge,即把 Chromium 的更新和 Edge 源代码的更新合并。
|
||||
- F:后续的版本。
|
||||
- Argo/Ruby:Edge 前身的 code name。
|
||||
|
||||
设计者在下方的文字中建议:因为基线更新每次要花费两个开发人员 4 周的工作,而且要花费未知问题修复和全套的测试工作,所以不建议在 GA(最终版本可用)之前再做更新,而是要把时间花在功能开发上。因为更新的好处是未知的,而坏处是已知的。
|
||||
|
||||
#### 5.2 代码结构设计
|
||||
|
||||
图 13.7.8 是示意性的软件开发架构(代码结构)设计,与本项目关系不大。
|
||||
|
||||
<img src="img/Slide31.SVG"/>
|
||||
|
||||
图 13.7.8 软件开发架构
|
||||
|
||||
在图 13.7.8 中,根据逻辑功能架构的三层设计,把代码组织成了三个大的部分:
|
||||
- UI Layer 表示层,这一部分(假设)用 C# 来实现,基于 ASP.NET 中的技术框架,如 MVC、WebAPI、SPA、WebHooks、Mobile Apps等等,而代码结构中包含 Models、Views、Controllers 等逻辑代码。
|
||||
- Service 服务层,这一部分(假设)用 Java 实现,基于 Spring Cloud 中的技术框架,比如 Eureka、Ribbon、Feign 等等,而目录中包含 filter、mapper 等逻辑代码。
|
||||
- Storage DAO 存储访问层,这一部分(假设)用 C++ 实现,基于 4 种异构的存储技术,如 Redis、SQL Server、Active MQ、Azure Blob Storage等,目录中包含根据具体的存储技术的读写逻辑代码。
|
||||
|
||||
|
||||
### 6. 运行环境设计
|
||||
|
||||
#### 6.1 运行环境
|
||||
|
||||
App 运行在 iOS 或 Android 智能手机系统上,用户可以在应用商店下载。在运行过程中,需要有互联网的连接。内部测试时,测试人员可以直接安装本地包。
|
||||
|
||||
因为本软件运行环境简单,所以下面提供两个额外的设计信息。
|
||||
|
||||
#### 6.2 测试环境
|
||||
|
||||
本部分可以单独写在《测试设计》中。
|
||||
|
||||
<img src="img/Slide32.SVG"/>
|
||||
|
||||
图 13.7.9 测试策略和自动化测试流程
|
||||
|
||||
在图 137.9 右侧,说明了测试栈的结构。从下到上依次为:
|
||||
|
||||
- Code Leve Test
|
||||
|
||||
代码级别的测试,主要是由开发人员提供 Unit Test,以保证代码的健壮性。
|
||||
|
||||
- Feature Level Test
|
||||
|
||||
功能级别的测试,除了传统的手工测试(Manual Test)外,Olympus Test 是一种自动化测试的代号,Instrumentation Test 是打点测试,看看业务逻辑是否完整。这一层的测试保证所有的功能(feature)都是可用的。
|
||||
|
||||
- UI Level Test
|
||||
|
||||
用户界面层的测试,但是目的是测试性能,界面响应速度要维持在正常水平。其内部列出了很多经常用到的功能作为测试点。
|
||||
|
||||
- App Level Test
|
||||
|
||||
主要是自动化测试,针对该浏览器软件进行盲测,遍历每个分支,包括压力测试和兼容性测试,以保证其高可靠性。
|
||||
|
||||
- Device Level Test
|
||||
|
||||
设备层面的测试,比如监控 CPU、内存、电池、网络、安装包尺寸、数据存储量、各类 Android 操作系统的变种和不同手机厂家的设备等等,以保证软件的高可用性。
|
||||
|
||||
|
||||
在图 13.7.9 左侧,说明了自动化测试的流程。比如:
|
||||
|
||||
- 最上方的测试角色,一共 5 种角色。最终的测试都是 Android 手机上进行。
|
||||
- 从上到下的一共 4 个测试任务,包括 Build(从源代码编译)、Test on Linux(在 Linux 上测试)、Test on Windows(在 Windows 上测试)、Test Report(生成并发送测试报告)。
|
||||
- 每个测试任务都有启示时间,运行预先编制好的测试用例,最后发送电子邮件。早晨上班后即可看到测试报告。
|
||||
|
||||
#### 6.3 安全设计
|
||||
|
||||
<img src="img/Slide33.SVG"/>
|
||||
|
||||
图 13.7.10 页面安全访问控制
|
||||
|
||||
为了保证安全,避免不小心进入钓鱼网站,设计了代号为 Smart Screen 的安全服务,用户的每次页面访问都会经过如下过程(图xxx 中有对应的序号):
|
||||
|
||||
1. 开始访问某个网址;
|
||||
2. 查询本地字典,该网址是否安全;
|
||||
3. 判断结果;
|
||||
4. 正常,继续,否则到 7;
|
||||
5. 获得网页;
|
||||
6. 返回给浏览器;
|
||||
7. 不确定;
|
||||
8. 把网址传给 Smart Screen Service 进行判别;
|
||||
9. 判断结果;
|
||||
10. 正常,继续,否则到 13;
|
||||
11. 获得网页;
|
||||
12. 返回给浏览器;
|
||||
13. 不正常,走异常处理过程;
|
||||
14. 返回一个警告页给浏览器。
|
||||
|
||||
【最佳实践】在详细设计中可以再具体描述加密解密的算法。
|
||||
|
||||
### 7. 发布与维护设计
|
||||
|
||||
#### 7.1 日志(Telemetry)
|
||||
|
||||
<img src="img/Slide34.SVG"/>
|
||||
|
||||
图 13.7.11 日志流程
|
||||
|
||||
已有的 PC 端 Edge 有一套完整的 Pipeline,本软件可以借用,所以需要写入相同的 Telemetry 格式。具体的格式如下:略。
|
||||
|
||||
【最佳实践】这里可以描述日志格式,需要注意的是,每条日志都必须有以下几个自动:消息ID,SessionID,时间,内容。由于日志可以记录 Session,所以在另外一条消息中,可以有不同的“消息ID”但相同的 SessionID。略。
|
||||
|
||||
在图 13.7.11 中,手机 Edge 浏览器在运行过程中收集各种事件(Event),通过 Telemetry Manager 写到不同的日志输出(Listner)上,其中,Asimov(阿西莫夫)Listner 可以把日志写入 Asimov SDK 中的缓存,然后再由后台进程传送到云端的日志库中。但是,所有事件必须先进行 Bond(相当于是格式注册)。
|
||||
|
||||
Telemetry Realtime Tool 可以帮助实时查看写入的日志格式是否正确。
|
||||
|
||||
#### 7.2 试验(Flighting)
|
||||
|
||||
由于是新 App,所以准备使用应用商店的 Flight 机制,即有些用户可以看到并下载测试,有些用户看不到。
|
||||
|
||||
#### 7.3 计划与安排(Timeline/Work Item)
|
||||
|
||||
下表是一个大致的时间安排,分为三个阶段:
|
||||
|
||||
- 8 月底,Private Preview,个人预览版,即内部可以开始试用;
|
||||
- 10 月底,Public Preview,公共预览版,即外部少数用户可以开始试用;
|
||||
- 12 月底,GA,即 Genaral Availability,正式版本。
|
||||
|
||||
|阶段|时间点|功能集|
|
||||
|-|-|-|
|
||||
|Private Preview|Aug.|StartPage, NTP, OverflowMenus, Settings, MainFrame, HugPage, TabManager, Telemetry, Favorites, Chromium58, RomaingFramework, AutomationTestFramework|
|
||||
|Public Preview|Oct.|ReadingMode, PrivateMode, FileDownload, SearchWidget, TypedURL, Reading List, History, SitePassword, ResumeOnPC, SmartScreen, PerformanceTest|
|
||||
|GA|Dec.|SpeechToTextSearch, OOBE, Branding, TopSites, FormData, Chromium61, MSA SSO, MemoryTest, BatteryTest, StressTest, CompatibilityTest, Accessibility, Security, PrivacyReview|
|
||||
|
||||
* 功能集,是按照图 13.7.2 中的任务来确定的。
|
||||
* 每个功能需要精确到人(开发人员的名字)和周(多长时间完成)。
|
||||
|
||||
#### 7.4 中止机制(Exit Criteria)
|
||||
|
||||
暂时没有中止计划。
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
|
||||
## 13.8 设计的核心目标与实现
|
||||
|
||||
软件的开发期和运行期质量目标有很多种,架构设计、概要设计、详细设计都是为了这些质量目标而服务的。而针对规模稍大的面向最终用户运行的软件系统,我们重点关注以下三个核心目标:
|
||||
|
||||
- 性能
|
||||
- 可用性
|
||||
- 可扩展性
|
||||
|
||||
其它的一些目标在本节的最后一个小节中描述,如规模、安全、成本、自动等。
|
||||
|
||||
### 13.8.1 性能(Performance)
|
||||
|
||||
软件设计和实现中,性能是每个设计人员和开发人员都要时刻考虑的问题,尤其是针对那种实时运行的线上服务系统而言,能用 20 毫秒完成的任务,就不要用 30 毫秒,可以说性能问题是点点滴滴组成的,做为架构师他看不到也不可能全看到具体的实现代码,所以需要层层把关。
|
||||
|
||||
<img src="img/Slide35.SVG"/>
|
||||
|
||||
图 13.8.1 提高性能指标的方法
|
||||
|
||||
#### 1. 数据结构
|
||||
|
||||
数据结构是最底层的环节。举例来说:当需要经常查找一个数组时,是什么时候用哈希表,什么时候用列表数组?
|
||||
|
||||
笔者以前曾经做过试验,当数组元素个数少于 10 个时,用列表数组遍历查找比较快;当元素个数大于 10 个时,用哈希表比较快。因为计算哈希值是需要时间的。
|
||||
|
||||
接下来的问题是:有没有比哈希表(即字典)更快的方法?有!那就是利用 ID 直接在内存中定位。比如想获得 ID=3 的数据记录,直接用 DataList[3] 获得即可。这就要求在设计 DataList 时最好是连续的,可以用整数值来表示 ID。
|
||||
|
||||
#### 2. 算法
|
||||
|
||||
算法和数据结构是相辅相成的,有了数据结构的支持,才有算法的实现。算法要考虑时间复杂度和空间复杂度。如读者都知道的排序算法,其时间复杂度和空间复杂度的比较如表 13.8.1 所示。
|
||||
|
||||
表 13.8.1 各种排序算法比较
|
||||
|
||||
|排序方法|时间复杂度|空间复杂度|稳定性|
|
||||
|-|-|-|-|
|
||||
|插入排序|$O(n^2)$|$O(1)$|稳定|
|
||||
|希尔排序|$O(n^{1.3})$|$O(1)$|不稳定|
|
||||
|选择排序|$O(n^2)$|$O(1)$|不稳定|
|
||||
|冒泡排序|$O(n^2)$|$O(1)$|稳定|
|
||||
|归并排序|$O(n \log n)$|$O(n)$|稳定|
|
||||
|快速排序|$O(n\log n)$|$O(\log n)$|不稳定|
|
||||
|堆排序|$O(n\log n)$|$O(1)$|不稳定|
|
||||
|计数排序|$O(n+k)$|$O(n+k)$|稳定|
|
||||
|桶排序|$O(n+k)$|$O(n+k)$|稳定|
|
||||
|
||||
有了这张表,读者当然可以很容易地选择性能最好的排序方法。但是,在实际应用中很少有如此“单纯”的问题可以轻松得到答案。比如在 2.3 节中的算法问题,实际上是可以用 $O(n)$ 时间复杂度来解决的,而它利用的就是在上面的数据结构中描述的 ID 定位法。
|
||||
|
||||
另外,要对代码中的循环语句保持高度的警惕,它很可能就是性能杀手!
|
||||
|
||||
举例来说:在一个统计英语词频的代码中,对每一个段落都使用了 str.split() 函数来做分词,因为每个英文单词之间都有空格或者标点符号。在做性能分析时,发现 split() 函数非常慢,占用了 80% 左右的运行时间,而原因是这个函数需要在内存中分配空间来保存 split 的结果,而且由于不能预先知道分配空间的大小,所以会动态地扩容。这听上去很方便、很自动,但实际上空间的分配、释放就是性能杀手,尤其是在成千上万次的循环中。
|
||||
|
||||
另外一个例子,是笔者遇到的计算股票交易数据中某个时间段(比如 5 分钟)之内的起始价、终止价、最高值、最低值、交易量等数据。这听上去很容易:找到一个时间段,比如 10:00~10:05,然后遍历每个交易记录即可。但是,问题在于股票的交易频率非常高,每分钟可能有上万次交易,而且股票有上千支,一年中又有 300 多个交易日,这样循环下来,计算量成几何级数增加。笔者的一个实习生用这种方法写的代码,处理一天的数据需要 20 多分钟。
|
||||
|
||||
对于这个问题,很多人会使用 Pandas 来承载数据,但是 Pandas 在易用性上的友好会降低它的性能,所以应该采用最原始的 Numpy 数组来加载数据,然后用 numpy.sum()、numpy.max()、numpy.min() 等函数一次搞定一批数据,避免最内层的遍历。笔者用这种方法写的代码处理一天的数据只需要 10 几秒。
|
||||
|
||||
#### 3. 并发
|
||||
|
||||
对于计算密集型的问题,并发意味着可以利用多颗 CPU 的优势,让它们同时完成不同的任务,即多进程或多线程。但是请注意编程语言的支持情况,如 Python 没有真正的多线程,所有的线程都是在一颗 CPU 上跑的。其它如 C++, JAVA, C# 都可以让多线程在多 CPU 上分享。
|
||||
|
||||
多线程编程相对较容易,尤其是在数据共享时在进程内线程间简单地加锁即可,而且系统开销比较小。但是传统的 Linux 系统对线程的支持没有 Windows 系统好,当然主要是因为内核优化和编译器优化上,Linux 还是以进程为主。笔者曾经在 Linux 上用多线程调用系统时间函数时出错,因为该函数要求线程安全。进程的开销比较大,但是相对安全。
|
||||
|
||||
所以,在 Windows 上用线程方便一些,在 Linux 上用进程保险一些。但是两者最好都用线程池或进程池机制比较好,避免频繁创建、销毁线程或进程的开销,而且能控制一台机器内线程和进程的总数量。
|
||||
|
||||
对于存储密集型的问题,并发意味着可以把数据拷贝到多个副本数据库中,在读取时可以根据负载均衡策略从不同的数据库读取不同的内容。但是在写入时,只能写入主数据库,然后同步到副本数据库,如 12.3.4 节中所讲。
|
||||
|
||||
#### 4. 缓存
|
||||
|
||||
“用空间换时间”就是缓存策略。缓存有两种:读缓存和写缓存,主要是针对磁盘而言。
|
||||
|
||||
读缓存:因为数据量较大时,不能把所有数据都放入内存,只能临时从磁盘读取。由于磁盘读的速度较慢,当遇到频繁读取时,就会有瓶颈产生。此时,可以考虑把常用的那部分数据经过加工处理后放到内存中当作缓存,不需要临时查询数据库。
|
||||
|
||||
写缓存:写磁盘的速度比读磁盘的速度还要慢,而且马虎不得,读错一次的话还有机会改正,写错一次就再也没机会。因此写磁盘是一个非常重要但不紧急的任务,所以可以考虑异步缓存,即:先把数据写到内存中的消息队列中,由后台进程从消息队列中读出来,再写入到磁盘数据库中。这种机制仅在刚写完消息队列就要立刻从磁盘里查询时会由于时效性问题而出错,但是不是致命错误,只是有延迟而已。
|
||||
|
||||
#### 5. 异步
|
||||
|
||||
上面提到了消息队列,使用消息队列有很多好处:
|
||||
|
||||
- 提高系统响应速度:接收到一个请求后,写入消息队列的速度非常快(相当于写到了内存中),然后立刻返回成功,后台再慢慢写到数据库中。
|
||||
- 提供稳定性:因为是异步方式,对服务方的性能和可用性要求不是非常高。
|
||||
- 消除峰值:大批请求到达后,把并行变成串行(消息队列就是串行),避免系统核心业务受到压力。
|
||||
|
||||
和上面的数据结构中讨论的用列表还是哈希表一样,消息队列在系统规模较大时,可以提供较高较稳定的吞吐量;但是规模小时,不如同步方式快。
|
||||
|
||||
#### 6. 集群
|
||||
|
||||
当我们利用以上手段在单台机器上优化,但是眼看着就要 CPU 达到满负荷时,就需要多台计算机做集群了,这些计算机都具有相同的服务功能。实际上,当单台计算机的负荷达到 70% 左右时,就应该考虑集群。
|
||||
|
||||
所谓集群,就是一台机器的多个副本(多台机器)具备相同的能力,由一个负载均衡器来分配任务,负载均衡的算法有很多种,可以分为静态算法和动态算法两类:
|
||||
|
||||
- 静态负载均衡算法
|
||||
- 轮询(Round Robin):服务器按照顺序循环接受请求。
|
||||
- 随机(Random):随机选择一台服务器接受请求。
|
||||
- 权重(Weight):给每个服务器分配一个权重值,根据权重来分发请求到不同的机器中。
|
||||
- IP哈希(IP Hash):根据客户端IP计算Hash值取模访问对应服务器。
|
||||
- URL哈希(URL Hash):根据请求的URL地址计算Hash值取模访问对应服务器。
|
||||
- 一致性哈希(Consistent Hash ):采用一致性Hash算法,相同IP或URL请求总是发送到同一服务器。
|
||||
|
||||
- 动态负载均衡算法
|
||||
- 最少连接数(Least Connection):将请求分配给最少连接处理的服务器。
|
||||
- 最快响应(Fastest Response):将请求分配给响应时间最快的服务器。
|
||||
- 观察(Observed):以连接数和响应时间的平衡为依据请求服务器。
|
||||
- 预测(Predictive):收集分析当前服务器性能指标,预测下个时间段内性能最佳服务器。
|
||||
- 动态性能分配(Dynamic Ratio-APM):收集服务器各项性能参数,动态调整流量分配。
|
||||
- 服务质量(QoS):根据服务质量选择服务器。
|
||||
- 服务类型(ToS): 根据服务类型选择服务器
|
||||
|
||||
#### 7. 分布式
|
||||
|
||||
集群能解决大部分问题,但是当一种计算密集型的应用无法在集群上应用时,就要考虑分布式了。不能让多台计算机干一件事,而是把一个大任务分成不同的子任务,让每台计算机做不同的事,然后把结果合并。
|
||||
|
||||
比如:神经网络训练,把训练数据等分成若干份,每台计算机上接收一份数据做一次正向计算和一次反向传播,然后把参数训练结果告诉主机;主机把参数做一个平均,再连同数据发送给多台计算机,做下一次训练。
|
||||
|
||||
对于数据密集型的应用,存储的高可用性可以用读写分离、分库分表等方式。
|
||||
|
||||
### 13.8.2 可用性(Availability)
|
||||
|
||||
在软件运行期质量中有两个比较接近的概念:
|
||||
|
||||
- 可用性(Availability):指系统长时间无故障运行的能力,与可靠性相关联。
|
||||
- 可靠性(Reliability):软件系统在一定的时间内无故障运行的能力。
|
||||
|
||||
用一个简单的计算器应用程序的例子来理解:
|
||||
|
||||
- 用户输入在合理范围内的数值时,比如 1+1,总可以返回正确的结果 2,这叫做可靠性;有时候返回 2,有时候返回 3。这叫做不可靠。
|
||||
- 输入两个 16 位的整数,做加法,应用程序不会由于溢出而宕机,叫做可靠性。宕机了就叫做不可靠。
|
||||
- 连续使用了 12 个小时后,做任何计算都可以返回及时正确结果且不宕机,叫做可用性;如果中间宕机了,或者返回结果不对了,或者响应很慢了,都叫做不可用。
|
||||
|
||||
但是在架构知识领域中,我们通常讲的是可用性,因为可靠性是必须的,可用性是需要额外付出其它成本的,而且可用性对软件运行的要求更高。
|
||||
|
||||
<img src="img/Slide36.SVG"/>
|
||||
|
||||
图 13.8.2 提高可用性指标的方法
|
||||
|
||||
|
||||
#### 1. 监控
|
||||
|
||||
最原始的监控方式是工程师们不时地跑到运行的屏幕前,看看应用程序是否还在正常地输出运行信息;“高级”一些的方式是架设一个摄像头,工程师在任何地方可以用手机查看实时视频。这些都属于直接监控,像是坐在监控室里的保安人员。
|
||||
|
||||
间接监控可以这么做:一个程序定期发送一些信号出来,比如电子邮件或者即时消息,如果发现很长时间没有收到消息了,在客户或老板找到你之前,赶紧去处理。
|
||||
|
||||
#### 2. 处理异常
|
||||
|
||||
如果想让一个程序永远运行,可以非常小心地在所有可能出现错误的地方都捕捉异常。但是在复杂的运行环境中,这一点很难办到。比如做梦都没想到磁盘满了而导致异常,而在平时的测试中,从来没有把 1T 的硬盘写满过。
|
||||
|
||||
#### 3. 守护
|
||||
|
||||
一旦程序挂掉,它无法自己重新启动。但是在同一台机器上,我们可以用另外一个程序来做监控,通过查看进程 ID 或者应用名称来检测被监控进程是否存活。
|
||||
|
||||
在 Linux 中的守护进程就有这个好处:把它 Kill 掉后,系统可以自动重启它,即守护进程是被系统守护的。在 Windows 中没有这种机制,但是可以做一个非常简单的程序(简单到它不可能出现异常退出的情况)来做监控者,每隔几秒钟做一次检测,如果被监控进程不存在了,就重新启动它。
|
||||
|
||||
#### 4. 心跳
|
||||
|
||||
当客户端和服务器位于不同的计算机上时,无法使用进程守护机制。比如,有两台服务器用负载均衡的方式为 100 台客户端服务,负载均衡器想知道这两台服务器是否还在工作,可以定时发送心跳信号,如果服务器有应答,说明正常;如果其中一台没有应答,说明挂了,就只能把任务都发送给另外一台服务器。这种方式可以称作失效转移。
|
||||
|
||||
心跳信号通常用 TCP 包来实现。在被监控的应用程序进程中开一个线程来监听心跳信号并应答,当该线程的父进程挂掉后,该线程也会被杀掉,就不会产生应答了。如果主进程内的逻辑中有循环,也可以在循环体中应答心跳信号,或者主动发送心跳信号,主动告诉对方“我还活着”。
|
||||
|
||||
如同病人心跳停止后仪器报警、医生来除颤一样,心跳机制并不能让远程的进程重启,只能监测。心跳和监控的原理一样,只不过自动化了。
|
||||
|
||||
#### 5. 冗余
|
||||
|
||||
上面说到有两台服务器为 100 台客户端服务,每台服务器的工作负载达到了 60% 以上。如果很不幸其中一台服务宕机了,另外一台服务器无法承担 120% 的负载,那么**整个系统就不可用了**。
|
||||
|
||||
从单个服务上升到系统层面后,我们可以采用冗余机制来提供高可用性。即,在上述的例子中,可以增加一台服务器,平时空闲。当其中一台工作的服务器挂掉后,负载均衡器会把任务分发到空闲的服务器上,这样还是 60% 的工作负载。工程师收到报警后手工处理挂掉的服务,恢复正常后让它处于空闲状态作为冗余。
|
||||
|
||||
这种方式也叫做 N+1。如果 N 比较大,意味着故障率影响的服务器的绝对数量也较大,那么可以变成 N+2 或 N+3。
|
||||
|
||||
#### 6. 备份
|
||||
|
||||
对于数据来说,备份是最重要的保持高可用性的方式,一旦数据损坏,连可靠性都不能保证,更不用说可用性了。
|
||||
|
||||
日常运维要做冷备份,在运行时数据损坏时,还可以手工恢复。实时业务中通常采用热备份方式,一种是同步热备份,一种是异步热备份。前者对用户的响应比较慢,后者比较快,但有可能在异步写入时失败。所以,关键性的任务都用同步方式,确保万无一失。比如我们在银行柜台办理业务的时候,总会觉得很慢,因为银行数据的同步要求非常高,恨不得把数据写到非洲大陆的服务器去保存一份,避免亚洲大陆的服务器有什么问题。
|
||||
|
||||
#### 7. 开关
|
||||
|
||||
这是不得已才使出的最后一招儿!
|
||||
|
||||
当某个服务在特定时间段内涌入了大量的用户,系统资源不堪重负,此时可以暂时关闭一些不关键的任务。比如:每到晚自习时间,作业管理系统会有大量的数据新增和修改,而在白天时的使用量基本为 0,学校没有很强的理由为此增加服务器,那就临时把生活服务中的一些功能关闭。
|
||||
|
||||
前面说的是 0 到 1 的硬性开关,还有一种软性开关,叫做限流。学生看教学视频进行复习时,占据了大量的带宽,此时可以用先到先得的原则来限制总在线人数。如果是其它非关键任务,如抽奖,也可以采用随机方式封掉一些用户的请求,直接返回繁忙状态即可。
|
||||
|
||||
### 13.8.3 可扩展性(Flexibility)
|
||||
|
||||
在软件运行期质量中还有两个比较接近的概念:
|
||||
|
||||
- 可扩展性(Flexibility):软件因适应新需求或需求变化而增加新功能的能力,也称为灵活性。
|
||||
- 可伸缩性(Scalability):指当用户数和数据量增加时,软件系统维持高服务质量的能力。例如,通过增加服务器来提高能力。
|
||||
|
||||
举例来说,如果一个计算器后台服务程序提供了加减乘除四则运算,可以通过 Web 页面供 100 人同时使用。
|
||||
|
||||
- 如果服务器负荷增加,需要很方便地增加新的服务器来供 200 人同时使用,叫做可伸缩性。
|
||||
- 如果没有那么多人用了,再减掉一台服务器只供 50 人同时使用,也叫做可伸缩性,不会因为少了服务器就不能提供完整功能。
|
||||
- 如果新增加了乘方、开方的运算,并且在不停止现有服务的情况下升级服务器功能,叫做可扩展性。
|
||||
|
||||
可伸缩性一般用于衡量性能指标,这里只讨论可扩展性。
|
||||
|
||||
<img src="img/Slide37.SVG"/>
|
||||
|
||||
图 13.8.1 提高可扩展性指标的方法
|
||||
|
||||
#### 1. 面向接口
|
||||
|
||||
在代码层设计和实现时,面向接口编程,符合开闭原则(OCP - Open/Close Principle)、依赖倒置原则(DIP - Dependency Inversion Pronciple)。一个对象实现了一个接口,说明它拥有了这项技能,就好比无论是奔驰还是奥托,只有价格上的差别,没有驾驶接口上的差别,那么经过培训的司机都可以驾驶它们。
|
||||
|
||||
在模块间的交互也是面向接口的,增加新的功能相当于扩展了新的接口,而不是在原有结构上修改,只有增加。另外,接口可以隐藏后面的细节,后台增加了一个新的数据库,只要提供和以前相同的访问接口,就可以什么都不改动。
|
||||
|
||||
接口类似于 12.5.1 的代理模式。
|
||||
|
||||
#### 2. 模块复用
|
||||
|
||||
在做架构设计时,通过全面观察各个业务的功能栈,抽取出具有共性的公共模块,比如日志写入、单点登录、在线支付等等,并把它们在逻辑功能架构中布置在最下方,上方已有的业务都可以使用它们。同样,对于新增加的业务也可以方便地使用这些公共模块,而无需重新开发或者改动已有模块。
|
||||
|
||||
模块也有大小之分,拆分的粒度如何掌控呢?应该用下述的单一职责来做指导方针。
|
||||
|
||||
#### 3. 单一职责
|
||||
|
||||
单一职责可以提高代码的可扩展性。当一个类只有一个职责时,它的变化引起的风险就会降低,因为它不会影响到其他的职责。这样,我们就可以更容易地修改、扩展和维护代码。
|
||||
|
||||
到模块级别时,一个模块承担的职责越多,被复用的可能性就越小。所以在上述的模块复用的基础上,应该再次审视该模块是否可以再细分成两个或多个模块,最大限度地提高复用程度。比如“提交作业”模块,从名字上看已经非常原子化了,但是它底层可能还包含“用户验证”、“作业历史”、“成绩排名”等步骤,都应该单独提出来。
|
||||
|
||||
#### 4. 即插即用
|
||||
|
||||
如同 12.5.3 中的插件模式,新的功能用插件的方式注册到平台(基础业务支持)上,需要平台开放并定义接口规范,新功能实现该接口规范即可接入。上文中的公共模块下沉是本方法的基础,但是还不够,还需要平台上可以有注册的地方以激活新功能。
|
||||
|
||||
这就好比在计算机主板上新插了一块声卡,首先要确定声卡是 PCI 接口,而不是老式的 ISA 接口;其次是要在计算机操作系统中安装驱动,在设备注册表中注册,这样应用程序才能通过注册表发现新设备。
|
||||
|
||||
#### 5. 事件驱动
|
||||
|
||||
如同 12.6.3 节中的例子,所有参与者都是高度松散耦合的,而松耦合是扩展的基础。其实事件驱动也是异步的一种方式,是消息队列的具体应用。在主业务中新增加了一个新的子业务或者业务环节,那么只需要向消息队列中添加一个新的事件源和监听者,而无需修改已有的架构。
|
||||
|
||||
消息队列既可以满足伸缩性,也可以满足扩展性。因为它只对容量敏感,对内容不敏感。容量不够时,可以新增加消息队列服务器到已有列表中。
|
||||
|
||||
#### 6. 横纵拆分
|
||||
|
||||
横向即水平拆分系统,形成典型的三层或多层结构,越靠近下层的模块被复用的次数越多,这是以上面的“模块复用”为基础的。另外,还可以设定“稳定层”和“不稳定层”,新业务所需要的特殊代码加入到不稳定层中,避免所有层都变动带来的风险。
|
||||
|
||||
纵向即垂直拆分系统,分割多个独立的业务。比如 13.2 节中的“教师管理”和“教学管理”要各自独立,在增加新的“学生管理”的时候,就不会触碰已有的业务单元,在软件上是不同的模块,在硬件上是不同的机器。
|
||||
|
||||
#### 7. 分布服务
|
||||
|
||||
仔细审查一个业务流程后,可以把整个流程当作一个服务来看待;也可以把流程拆开成几个子服务,每个子服务都提供好服务接口(一般是 HTTP 或 RESTful 调用),然后通过顺序调用来完成完整业务。
|
||||
|
||||
开发好各个子服务后,每个子服务都向服务注册中心注册,使用者通过注册中心获得可用服务列表和能力,然后调用该服务。与上述的即插即用的方式的区别是:即插即用的各个模块之间不能互相通信,必须通过接口和主板通信;而分布服务的各个子服务之间可用直接互相调用(当然要避免循环调用的情况发生)。
|
||||
|
||||
有了上述机制后,不同的子服务就可以用微服务或其它容器部署在不同的节点上。当有新的业务时,也用多个子服务实现,增量地部署到已有节点上或者增加新的节点,方便地实现扩展。
|
||||
|
||||
|
||||
### 13.8.4 其它
|
||||
|
||||
除了上述的性能、可用性、可扩展性三个重要的目标外,下面的四个目标也需要关注。
|
||||
|
||||
#### 1. 成本
|
||||
|
||||
架构就是一个无底洞,架构师很聪明,但是老板也不傻,看不到有明确的盈利趋势的话,老板是不会满足架构师的“花钱办大事”欲望的。
|
||||
|
||||
以前都需要自己租机房、买硬件、搭网络,现在国内外有很多云供应商,如 Azure,提供了丰富的软硬件平台,在基础设施运维上就给用户节省了不少钱。用户还可以随意选择自己熟悉的、价格合理的计算节点,恰当地满足自己的业务需求。
|
||||
|
||||
【最佳实践】租用云平台,随时缩放。清理不受欢迎的业务,降低维护成本。监控服务器运行负载,适当地共用硬件。
|
||||
|
||||
#### 2. 规模
|
||||
|
||||
如果软件系统满足了市场需求,并且采用了合理的架构,这种情况下的规模扩展是令人高兴的事。初始功能满足用户痛点,用户量逐步增大,数据量必然随之增大。而为了吸引更多的用户,会增加一些新的功能,也会带来规模的扩展。适当地控制扩展速度,密切注视系统运行指标,才能保证性能和可用性。
|
||||
|
||||
但是如果采用了不合理的架构,比如误用了微服务模式,或者其它内耗造成了非线性的规模扩展,会带来灾难性后果。
|
||||
|
||||
【最佳实践】逐步增加功能,控制规模增长速度,在必要的时候重构系统。
|
||||
|
||||
#### 3. 安全
|
||||
|
||||
软件系统的安全性体现在保密性、完整性、可用性、可控性、不可否认性五个方面。在软件开发设计中,避免一些基本的安全隐患,就可以防住 90% 的风险。体现在跨站点脚本防范、防 SQL 注入规范、页面组件安全防范、敏感数据安全防范等等。
|
||||
|
||||
【最佳实践】对敏感数据存储加密、传输加密。安全认证由独立的模块完成。永远不信任用户的输入。
|
||||
|
||||
#### 4. 自动
|
||||
|
||||
自动体现在几个方面:软件的自动化测试(Test Bed)、在集成环境的自动化验证(Integrate Bed)、在产品平台的自动化部署(Production Bed)。
|
||||
|
||||
在 Bing 搜索服务的工程中,就有上述三个平台:
|
||||
|
||||
- 新代码可以随时在 Test Bed 做测试,当然要先经过自己的 Local Box 环境的测试。
|
||||
- 有把握的可以放到 INT Bed 上做集成测试,以检测复杂环境下的运行效果。
|
||||
- 最终要部署到 Prod Bed,但是可以随时回滚。
|
||||
|
||||
【最佳实践】不要在节假日前夕发布任何新功能,一是避免人心浮躁带来的风险,而是避免假期时系统出现问题带来的加班。
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
### 13.9 练习
|
||||
|
||||
具体的设计实例:
|
||||
- 高并发的设计
|
||||
- 异地灾备的设计
|
||||
- 消息队列的设计
|
||||
- 微信系统的设计(点对点通信)
|
||||
- 微博系统的设计(消息订阅,发布)
|
||||
- 淘宝系统的设计
|
||||
- 异地访问延迟
|
||||
- 高性能
|
||||
- 微服务
|
||||
- 语音系统的设计
|
||||
- 地图系统的设计
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 55 KiB |
После Ширина: | Высота: | Размер: 100 KiB |
После Ширина: | Высота: | Размер: 62 KiB |
После Ширина: | Высота: | Размер: 127 KiB |
После Ширина: | Высота: | Размер: 41 KiB |
После Ширина: | Высота: | Размер: 60 KiB |
После Ширина: | Высота: | Размер: 136 KiB |
После Ширина: | Высота: | Размер: 520 KiB |
После Ширина: | Высота: | Размер: 93 KiB |
После Ширина: | Высота: | Размер: 89 KiB |
После Ширина: | Высота: | Размер: 81 KiB |
После Ширина: | Высота: | Размер: 43 KiB |
После Ширина: | Высота: | Размер: 63 KiB |
После Ширина: | Высота: | Размер: 78 KiB |
После Ширина: | Высота: | Размер: 64 KiB |
После Ширина: | Высота: | Размер: 65 KiB |
После Ширина: | Высота: | Размер: 103 KiB |
После Ширина: | Высота: | Размер: 90 KiB |
После Ширина: | Высота: | Размер: 105 KiB |
После Ширина: | Высота: | Размер: 119 KiB |
После Ширина: | Высота: | Размер: 132 KiB |
После Ширина: | Высота: | Размер: 137 KiB |
После Ширина: | Высота: | Размер: 56 KiB |
После Ширина: | Высота: | Размер: 179 KiB |
После Ширина: | Высота: | Размер: 60 KiB |
После Ширина: | Высота: | Размер: 226 KiB |
После Ширина: | Высота: | Размер: 167 KiB |
После Ширина: | Высота: | Размер: 140 KiB |
После Ширина: | Высота: | Размер: 35 KiB |
После Ширина: | Высота: | Размер: 35 KiB |
После Ширина: | Высота: | Размер: 152 KiB |
После Ширина: | Высота: | Размер: 76 KiB |
После Ширина: | Высота: | Размер: 46 KiB |
После Ширина: | Высота: | Размер: 55 KiB |
После Ширина: | Высота: | Размер: 41 KiB |
После Ширина: | Высота: | Размер: 46 KiB |