【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构设计

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

-- Eric Brechner, 《代码之道》

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

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

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

什么是物理架构?

软件的物理架构是相对于逻辑架构而言的。

我们知道,软件系统的逻辑体系结构,是由一组支持系统逻辑操作的相关技术概念和原理组成。它与软件系统的组织有关, 其主要目的是定义系统的结构,行为和接口协议。

软件的物理架构规定了组成软件系统的物理元素、这些物理元素之间的关系、以及它们部署到硬件上的策略。 

物理架构可以反映出软件系统动态运行时的组织情况。此时,上述物理架构定义中所提及的“物理元素”就是进程、线程、以及作为类的运行时实例的对象等,而进程调度、线程同步、进程或线程通信等则进一步反映物理架构的动态行为。

举个例子,一个云平台的物理架构图如下所示:

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构设计_02

为什么需要物理架构

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

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

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

物理架构设计的工作内容

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

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构师_03

物理架构的设计思维

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

从设计结果层面,决策无非围绕物理节点、网络、软件单元、数据单元等内容展开。但是,思维当中经历了哪些思考、判断和权衡呢?

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

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

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

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

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构_04

为什么需要运行架构

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

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

运行架构设计的工作内容

工作内容

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

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构师_05

控制流(Control Flow是一个在处理机上顺序执行的动作系列

确定引入哪些控制流,并没有固定不变的套路,但有几点考虑是必不可少:

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

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

TIPS:数据流 DataFlow VS. 控制流 ControlFlow

控制流是指程序中控制程序执行顺序的流程,它可以控制程序中的指令的执行顺序,以及程序中的分支和循环结构。

数据流是指程序中数据的流动,它可以控制程序中的数据的传输和处理。

一、控制流

从接触面向过程语言开始,使用控制流编程的概念已是司空见惯。

if (condition) {
// do something
} else {
// do something else
}

分支循环是最常见的控制流形式。由于控制条件的存在,总有一部分代码片段会执行,另一部分不会执行。

在控制流中,想要进行数据传递,最关键的是借助于变量保存中间状态。因此,控制流编程看起来是将数据嵌套在控制流内的编程方式。

使用变量保存程序状态有个很大的优势。通过变量缓存,可以将编程任务划分为不同的阶段,每个阶段只需要完成一部分功能子逻辑即可,这大大降低了复杂流程的思维成本。

但同时,也有一个比较大的劣势,就是在分布式处理环境下,中间状态的维护一直是一个很繁琐的问题。这从另一个方面加大了程序设计的成本。

二、数据流

而数据流编程的概念最初可以探寻到函数式编程语言,以及灵感源于此的FlumeJava类系统(如Spark、Flink等)的编程API。

rdd.map(lambda).filter(lambda).reduce(lambda);

这种类似管道流水线形式的编程接口,每次处理的数据是列表形式的(LISP)。当然,这些列表放在分布式环境下换了一个新的名词——分布式数据集(RDD/DataSet)。

数据流编程最大的特点是抽象了丰富的算子,通过UDF为算子指定用户处理逻辑。因此,数据流编程其实蕴含了控制流嵌套在数据流内的编程方式。

使用数据流编程最大的优势就是无需使用变量维护计算中间状态,另外基本的列表数据格式天然满足分布式数据存储的要求。这也是函数式语言在自我宣传时比较注重的一个优势:对并行计算支持得更好。

不过,数据流编程的方式也并不是完美。由于事先规划好的流水线结构,导致了数据处理无法自主地选择流水线分支进行处理。所以,有时候看似很简单的控制逻辑,使用数据流表达时就显得比较繁琐。

控制流图是关键

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构设计_06

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

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

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

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

  • 进程
  • 线程
  • 中断服务程序

进程(​​Process​​)是重量级控制流,即是处理机资源的分配单元,又有其他计算机资源的分配单位。

线程(​​Thread​​)是轻量级控制流,仅仅是处理机资源的分配单位。一个进程内可以包含多个线程,后者共享前者的资源:但处理机资源例外,线程是独立IDE处理机资源的分配单位。

实际上,中断服务程序(​​Interupt Service Routine, ISR​​​)也是常见的控制流实现机制。当没有​​OS​​的支持却要实现并发时,它非常必要。

什么是开发架构?

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构设计_07

Twitter Architecture 2022

软件架构中的开发架构是指软件开发过程中使用的技术架构,它描述了软件开发过程中使用的技术,技术架构的组件,以及它们之间的关系。

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构师_08

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构_09

为什么开发架构是必须的

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

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

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

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

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

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

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

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

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

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

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

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

开发架构设计的工作内容

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

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_架构_10

设计模式对重用的意义

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

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

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

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

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

【成为架构师课程系列】怎样进行物理架构、运行架构、开发架构的设计?_控制流_11