14. 物理架构、运行架构、开发架构

我认识一些架构师,他们的生活都是失控的。因为架构天性范围宽广,涉及人、工作量都非常多。一些架构师把他们的时间整天整天的花在跟“项目干系人”开会上,然后夜以继日,再搭上周末去实际的架构工作。 -- Eric Brechner, 《代码之道》

多重软件架构视图之所以必不可少,是因为各类涉众(用户、客户、开发人员、测试人员、维护人员、内部操作人员、其他人员)需要从各自角度理解和使用架构。 -- Barry Boechm

架构要涵盖的内容和决策太多了,超过了人脑“一蹴而就”的能力范围,因此采用“分而治之”的办法从不同视角分别设计;同时,这样也为对软件架构的理解、交流和归档提供了方便。

接下来会介绍物理架构、运行架构、开发架构作为软件架构的不同视图,它们分别关注不同的方面、针对不同的目标和用途。

14.1. 为什么需要物理架构

硬件强大了,但数据量在增加,计算复杂度也在提高,所以增加硬件未必能解决问题。

相反,计算与计算往往不是孤立的,它们之间存在着复杂的“生产者-消费者”关系,所以软件的实际服务能力不仅受到“硬件资源”的制约,也受到了“数据短缺”和“数据争用”的制约。每个架构师都应该懂得:

增加硬件 = 增加计算能力 ≠ 软件的实际服务能力增强

多视图方法中,物理架构视图着重考虑运行软件的计算机、网络、硬件设施等情况,还包括如何将软件包部署(如果是嵌入系统则是烧写)到这些硬件资源上,以及它们运行时的配置情况。另外,物理架构还要考虑软件系统和包括硬件在内的整个IT系统之间是如何相互影响的,由于一部分运行质量属性需要硬件或网络的支持,所以物理架构必须关注如何配置硬件和网络来满足软件系统的可靠性、可伸缩性、持续可用性、性能、安全性等方面要求。

14.2. 物理架构设计的工作内容

物理架构设计主要有3项任务:

  • 硬件选择和物理拓扑
  • 软件到硬件的映射关系
  • 方案的优化

14.3. 探究:物理架构的设计思维

相对逻辑架构设计而言,物理架构视图的设计是不是就乏善可陈呢?不,一般架构师最缺的就是物理架构的设计思路。

从设计结果层面,决策无非围绕物理节点、网络、软件单元、数据单元等内容展开。

但是,思维当中经历了哪些思考、判断和权衡呢?

从最终目标层面,决策要兼顾多方涉众的不同利益,可从“攻”与“守”两个方面理解:

  • 高性能(攻)
  • 持续可用性(攻)
  • 可伸缩性(攻)
  • 技术可行性(守)
  • 易维护性(守)
  • ......

从思维要点层面,“开销”和“争用”是核心。架构师正是通过“降低开销”、“避免争用”来实现高性能、高伸缩性等最终目标。

  • 如何降低物理节点“内”的计算开销?
  • 如何降低物理节点“间”的通信开销?
  • 如何避免物理节点“内”CPU、内存、硬盘等资源的争用?
  • 如何避免物理节点“间”网络的带宽资源冲突?

这样,我们了解了物理架构设计的理性思维框架。

14.4. 为什么需要运行架构

如果系统中,并不准备引入任何并行或并发处理,并且系统也没有基于SDKAPI等基础软件进行定制开发,那就不需要设计运行架构的设计--这相当于将5视图方法裁剪为4个视图。

相反,很多系统为了应对复杂的业务逻辑或复杂的互操作逻辑(含硬件交互),或者为了优化关键资源使用效率,而必须借助多条控制流或并发执行时,就需要设计运行架构

14.5. 运行架构设计的工作内容

14.5.1. 工作内容

根据具体情况不同,运行架构设计可能包括下列工作内容

  • 确定引入哪些控制流
  • 确定每条控制流的任务
  • 处理相关问题:控制流的创建、销毁、通信机制等
  • 进一步考虑:控制流之间的同步关系,若有资源争用还要引入加锁机制

控制流(Control Flow)是一个在处理机上顺序执行的动作系列。确定引入哪些控制流,并没有固定不变的套路,但有几点考虑是必不可少:

  • 物理架构中的每个节点(node)之上至少有一条控制流
  • 为了实现节点(node)之间的通信,通常做法是引入一条控制流来专门负责
  • 在需求一级的描述中(例如用例规约中)就是并行或并发的,引入多条控制流
  • 来自用户或外部系统的并发访问,常要求后端服务支持多控制流
  • 如果控制流关系复杂,可以考虑引入对其他控制流进行协调的控制流

一旦系统中存在不止一条控制流,就产生了附近的工作量,例如控制流的创建、控制流的销毁、建立共享内存或消息等不同控制流之间的通信机制。

14.5.2. 控制流图是关键

控制流图显示了系统中不同控制流之间的关系。控制流的起点是“主动单元”,它会调用其他“被动单元”......如此层层调用,就形成了一个控制流。明确了系统中所有的主动单元,就抓住了每个控制流的源头,从而可以把并发执行的所有控制流梳理清楚。

例如:MN两个模块可能需要加锁,因为M模块的“入度”等于2,而NM的下游模块。

运行架构设计的工作看似多而杂,但其实只要把握“控制流图”,就能提供提纲擎领的开展其他相关设计。

14.6. 实现控制流的3种常用手段

在实践中,最常用于实现控制流的手段有3种。

  • 进程(Process)是重量级控制流,即是处理机资源的分配单元,又有其他计算机资源的分配单位。
  • 线程(Thread)是轻量级控制流,仅仅是处理机资源的分配单位。一个进程内可以包含多个线程,后者共享前者的资源:但处理机资源例外,线程是独立IDE处理机资源的分配单位。
  • 中断服务程序(Interupt Service Routine, ISR)也是常见的控制流实现机制。当没有OS的支持却要实现并发时,它非常必要。

例如,下图所示的多条控制流设计用到了线程,以及中断服务程序的技术(背景为设备调试系统):

  • 应用层中的线程代表主程序的运行,它直接利用了MFC的主窗口线程。无论是用户交互,还是串口的数据到达,均采用异步事件的方式处理,杜绝了任何“忙等待”无谓的耗时,也缩短了系统响应时间。
  • 协议层有独立的线程控制着“上上下下”的数据,并设置了数据缓冲区,使数据的接受和数据的处理相对独立,从而使数据接收不因暂时的处理忙碌而停滞,增加了系统吞吐量。
  • 硬件控制层的设计中,分别通过时钟中断和RS232中断来激发相应的处理逻辑,达到轮询和收发数据的目的。

14.7. 为什么开发架构是必须的

以前,很多企业不重视架构:现在,重视架构的企业远比不重视架构的企业多。但是,许多企业发现一个棘手的问题:开发人员不按照架构进行详细设计和编程。

大家一起来思考下面的问题。

问题,许多公司困扰于:开发人员不按照架构进行详细设计和编程。如何让开发人员更“拥护”架构?

A: 在架构设计中重视“开发架构视图”,让开发人员看到他最关心的“程序单元”、“源代码目标结构”等概念

B:架构设计不可“高来高去”,能支持并行的详细设计是“架构设计你进行到什么程度”的标志

C:应令HR对开发人员批评、教育

D:编程一线的程序经理参与架构设计

此题的答案是:A、B、D

一句话,架构师在抱怨研发管理、职位权力之前,还需自查!

首先,最基本的一点,架构师必须重视开发架构视图,并行开发所需的“程序单元”、“源码目录结构”等概念,是不同程序团队开展具体工作的基础。如果程序员们总不能从架构中农看到上述内容,就会认为架构是一类“高来高去”的概念,自然不会有积极态度。(A 正确)

另外,能不能更具体的“定义”架构设计应该达到的程度呢?答案是:能支持并行的详细设计。所以,架构师投标成功之后,切不可将投标中演示的“市场架构”直接作为架构设计的全部。因为这意味着很多影响全局的设计决策被“漏”到了后边,最终到大规模并行开发阶段才发现,造成“程序员碰头临时决定”的情况大量出现,必然导致软件质量下降甚至项目失败。(B正确)

当然,有能力的架构师,再加上聪明的管理策略就更好。既然每个程序经理都深入理解架构,那何不让他们参与到架构实践的工作中来,免去了大量“单纯的架构交流”的工作量。更不必说,“了解产生爱”(程序人员不了解你的架构又如何喜欢你的架构)和“成就感”的心理因素会让程序经理支持架构设计方案。(D正确)

14.8. 开发架构设计的工作内容

一般而言,开发架构的设计应完成下列工作。

  • 将“逻辑职责”映射为“程序单元”
    • 要自主编写的源程序
    • 可重用的库、框架
    • 其他方式(如Shell脚本、平台支持下的配置文件)
  • 开发技术选型
    • 开发语言
    • 开发工具
  • “程序单元”间关系
    • Project划分(可选)
    • Project目录结构
    • 编译依赖关系

值得说明的是,此处的“Project”不是指Project Manager管的“项目”,而是指IDE等开发环境所支持的“Develop Studio Project”类似的概念。

14.9. 观点:重用测试是关键

广大软件企业系统通过为客户提供合格的软件系统达到获取利润的目标,于是,他们非常关注“可重用性”。但从重用的现实效果来看,显然远远不能令人满意......

14.9.1. 探究:我们为何年复一年修改者类似的Bug

在很多企业(包括很多程序员本人)都声称“我们很重视重用”的背景之下,下面这个问题尤其值得深思:

国内许多程序员年复一年的写着类似的程序--更要命的是,他们年复一年的修改者类似的Bug。

事出有因。以下两个问题是根源所在。

  • 第一,架构师“重交付、轻维护”。心理学告诉我们,自己的、眼下的痛苦是最大的痛苦。于是,在工期的巨大压力下,许多架构师最担心的是“项目能否按时交付”,于是就把维护问题仍在脑后。
  • 第二,架构师“重视小粒度重用、忽视大粒度重用”。一个孤立的类、一个小函数,每天都能被重用,却不能解决“年复一年修改类似Bug”的问题。最终我们发现,小粒度重用是有价值的,但和大粒度重用并不矛盾,而大粒度重用才是避免“重复组装、重复改Bug”式浪费的关键。

所以,请记住这个公式:

重用价值 = 重用次数 x 单次价值

14.9.2. 观点:为了从根本上降低维护成本,重用测试是关键

不要只关心重用的次数,为了重用而重用,而是时刻关注节省成本--这才是重用的目标所在。于是,当我们想到“维护是最昂贵的环境”,当我们看到经典书籍显示维护成本占总成不的67%之巨时,会作何感想?

图片来源:App Maintenance Cost Can Be Three Times Higher than Development Cost

想必你的结论和我一样:为了从根本上降低维护成本,重用测试是关键。这意味着大粒度重用。也就是,在重用这些代码时,并不对代码任何修改--它们的测试也被重用了。具体而言,FrameworkServiceServer、平台、中间件都算大粒度重用技术,它们已经成为,并继续是重用技术的未来方向。

14.9.3. 简评:设计模式对重用的意义

最后,评论有一个有趣的现象:很多架构师一提到重用首先会想到设计模式。那么,设计moose在重用技术中占据了什么位置呢?先看看Lethbridge在《面向对象软件工程》中的一段话:

下面是软件工程师实践过的一些重用类型,安装重用所节省的潜在工作量的升序排列。

  • 重用专家经验
  • 重用标准的设计和算法
  • 重用类库或程序,或者重用语言和操作系统中内置的强大命令
  • 重用框架
  • 重用完整的应用程序

设计模式属于上面的“专家经验”和“标准的设计”级别的重用策略--《面向对象软件工程》明白无误的告诉我们:这种“重用类型”节省的潜在工作量是比较有限的。

为什么呢?下图说明了“设计模式”和“框架”等技术在重用方面区别的根源:前者通用程度高和编程语言无关,后者实现程度高代码已提供。没有代码--无论是系统软件或平台内部的实现,还是库或者框架这种外部实现--就没有办法重用测试,就会面临较多的Bug而花费较高的维护成本。

14.10. 贯穿案例

继续PASS系统的架构设计。

14.10.1. 物理架构

回顾之前第9章的“ 9.4. 贯穿案例 ”部分,你可能会有疑问:物理架构不是在概念架构时已设计吗?为什么又要设计?

所以,下面首先分析医学:概念架构设计和5视图方法是什么关系。

  • 总体而言,概念架构所包含的高层设计决策终究不会跳出5个架构视图的范围--逻辑架构、物理架构、开发架构、数据架构或运行架构
  • 只不过,概念架构设计的抽象程度比较高,设计程度也很不充分,而细分架构必须设计到可以指导开发的程度
  • 例如,一个分布式系统的概念架构,最常见的做法是包含逻辑架构和物理架构2个视图的高层次的考虑
  • 对于典型的嵌入式控制习题而言,概念架构设计时又经常运行架构中多条控制流的规划

总之,概念架构与5视图方法的区别及联系可以用两句话概况。

  • 概念架构从少数视角、重点视角进行概念设计
  • 细化架构从多个视角、全面视角进行充分设计

至此,我们明白了PASS系统的物理架构设计已经在概念架构时进行了一部分,进一步明确不同物理层(Tier)之间的协议类型、每一物理层是否需要部署集群、软件单元(例如Layer)到物理节点的映射关系等物理架构设计的内容即可。

14.10.2. 持续不断的考虑非功能需求

非功能需求的支持不可能是“速决战”。

非功能需求的考虑是“贯穿环节”,在概念架构设计阶段,以及细化架构设计阶段都应重视。

对细化架构设计阶段而言,这意味着非功能目标的考虑时刻伴随着5视图的设计。例如,为了支持“用药规则的实时更新”这一功能,会涉及不同视图的考虑,中间穿插着无数次“质疑-分析-决策”的“微过程”。

我们通过“目标-场景-决策表”来“重放”了我们的思维过程。

14.10.3. 开发架构

对大系统而言,开发架构设计中的“Project划分”是不可或缺的,因为即使一个Project可以“胜任”(例如没有多节点因素),我们也不推荐这样做。更何况,将一个系统组织成多个Project的形式进行开发,可以方便的单独控制每一个Project源码的保密性--这是很多软件企业都关心的一个问题。

我们将PASS系统划分为5个Project,分别为:

  • 院长Web应用
  • 管理员Web应用
  • 嵌入式工作站的DLL
  • 医生模块通用SDK
  • PASS Server

确定了PASS系统划分成哪几个Project,就可以进一步确定每个Project所包含的程序单元、这些程序单元的依赖关系、源码的基本结构等问题,并为每个Project选定具体的技术及所用到的Framework

14.10.4. 架构设计应进行到什么程度

最终,架构设计应进行到什么程度呢?

架构设计的程度应考虑3方面的问题。

  • 应为开发人员提供足够的指导和限制
    • 标志:可支持并行的详细设计
    • 说明:所谓架构设计、概要设计、总体设计,只是含义相同的不同词汇而已。因此,“架构设计->概要设计->详细设计”的观点是不够专业的
    • 解图:图中“架构设计”和“详细设计”之间没有缝隙
  • 因项目、开发团队情况的不同而变化
    • 说明:应考虑项目熟悉程度、风险高低,以及团队技能水平等
    • 解图:图中“架构设计”的“详细设计”之间的“线”上下浮动
  • 业务层、通用机制应更深入设计
    • 说明:核心模型影响可扩展性,应当更深入设计;通用机制影响易解性和Bug率,应当更深入设计
    • 解图:图中“架构设计”和“详细设计”之间的“线”为深深浅浅的锯齿状

下一节:压力、没时间进行充分测试、含糊不清的规格说明......无论多努力工作,还是会有错误,不过,造成无法挽回失败的,是数据库设计错误还是架构选择错误。 -- Stéphane Faroult,《SQL语言艺术》

针对不同的领域,由于信息资源类型及其存在的状态不同,信息资源整合的需求存在较大差异。 -- 彭洁,《信息资源整合技术》

确定数据分布方案是数据架构设计的难点。越是大系统,数据分布越关键。因此,一线架构师迫切须要建立数据分布策略的大局观。

我们接下来结合案例,讲讲如何运行数据分布的6种具体策略。