什么是软件架构?我们在聊软件架构时,到底在聊什么呢?
100位软件工程师可以给出120种对软件架构的理解和定义,争论哪一个软件架构的定义是最好的,意义不是很大。我们应该进一步思考的是:为什么要对软件架构进行定义,这样的定义能为我们带来什么?
明确软件架构的定义后,在面对纷繁复杂的需求时,我们就可以十分从容地进行软件架构的设计;软件架构的定义给了我们一个解决问题的公式或思维套路,有了这样的一个公式或套路,我们就可以有条不紊地对项目进行推进和落地。
举个例子,笔者在中学时特别喜欢物理,有一道题目仍记忆犹新:冬天的湖面上漂浮着一块冰,当这块冰全部融化成水后,湖面的水位是会涨?还是会降?还是不涨也不降呢?
从问题描述中寻找公式:
水对冰的浮力F1 = 水的密度 * g * 水面下冰的体积
冰的重力F2 = 冰的密度 * g * 冰的体积
围绕着这两个公式进一步思考和推导,可以很容易得到答案,否则解决这个物理问题就遥遥无期并且极易出错了。
所以,公式和套路,是我们解决问题的有力抓手。
基于这样的思考,我们总结软件架构的定义,从而分析和抽象出软件架构设计的公式和套路。见下图。
软件架构是指软件系统的顶层结构,包含具有明确职责的角色,这些角色通过相互协作使软件系统提供业务能力。
首先,软件架构是指系统的顶层结构,它应该有清晰的要解决问题的系统边界和上下层次边界。系统有自己的顶层结构,子系统和子子系统也有自己的顶层结构,顶层结构与要解决的问题映射。
举个例子:在一个 OA 系统部署架构中,包含 Nginx 代理、SpringBoot 应用程序、MySQL 数据库、RocketMQ 消息中间件等四个模块,这四个模块就是 OA 系统部署架构的顶层结构。
以上描述没有问题,但如下描述就不合适了: OA 系统部署架构中,包括 Nginx 代理、SpringBoot 应用程序、MySQL 数据库、NameServer 集群和 Broker 集群。因为描述中打开了 RocketMQ ,将 RocketMQ 的第二层放到了顶层结构中进行了组合,这样就打破了架构的上下层次边界。要打开 RocketMQ,可以对 RocketMQ 的顶层结构进行专门的分析。
软件架构有三个核心的要素:
要素一:角色
软件架构就是系统的顶层结构,在顶层结构中包含的角色,必须具有明确的职责。
比如:汽车系统中包含发动机、车轮、方向盘等角色,发动机负责提供引擎动力、车轮负载载重前行、方向盘负责调整前进方向。汽车系统的每一个角色的职责都是非常明确的。
在上述 OA 系统的例子中:Nginx 负责请求的反向代理和后端服务的负载均衡,SpringBoot 应用程序负责实现业务的规则和流程,MySQL 数据库负责对业务数据进行持久化存储,RocketMQ 消息中间件负责将 OA 系统中的业务事件通知到外部系统。
我们在对系统进行架构设计时,如果只凭感觉设计了一个职责并不明确或职责并非必须的角色时,就需要注意了,这样的架构很大可能存在问题。
要素二:协作
软件架构就是系统的顶层结构,顶层结构中的角色具有明确的职责,这些角色并非独立,它们之间一定存在协作关系,即角色之间的交互。
在汽车系统的例子中,发动机产生引擎动力通过传动轴传递给车轮,驱动了车轮转动;方向盘通过转向轴将转向扭矩传递给车轮,调整车轮的转动方向;汽车系统的三个角色通过相互协作,让汽车载重驶向目标。
在上述 OA 系统中,客户端通过 https 协议访问 Nginx、Nginx 通过 http 协议访问 SpringBoot 应用程序,SpringBoot 应用程序通过 MySQL 协议访问 MySQL 数据库,SpringBoot 应用程序通过 RocketMQ 协议访问 RocketMQ 消息中间件,四个角色相互协作,为用户提供 OA 能力。
要素三:业务能力
软件架构就是系统的顶层结构,顶层结构中的角色具有明确的职责,角色之间通过相互协作最终使软件系统提供了业务能力。业务能力是根本,软件架构设计需要对业务负责;看似完美的架构设计,如果不能提供业务能力,为业务赋能,一切都是白扯。
汽车系统的发动机、车轮、方向盘三个明确职责的角色通过相互协作,为车主负重前行,驶向重点。OA 系统的 Nginx、SpringBoot 程序、MySQL 数据库、RocketMQ 消息中间件四个明确职责的角色通过相互协作,为企业用户提供了 OA 服务,提高了企业的运作效率。
理解了“角色”、“协作”和“业务能力”等三个软件架构的核心要素后,就为我们在设计一个新系统的架构或对现有系统的架构分析时提供了一个抓手。上述汽车系统和 OA 系统的例子可能过于简单,似乎不解渴,我们举一个普适性 RPC 架构的例子。
RPC 包括客户端 RPC 和 服务端 RPC。
客户端 RPC 中包括 Proxy、序列化、反序列化、路由管理、负载均衡、超时管理和连接管理等角色;业务逻辑代码(Business Logic) 调用服务提供方的接口,最终都会由 Proxy(动态代理)的统一的方法(invoke 方法)来处理;在该方法中,首先将请求对象序列化成二进制流,然后分别经过路由选择和负载均衡来确定下游的服务提供方的节点;再由连接管理( 或服务连接池)将二进制流发送到下游节点;发送请求后,若对应的响应包按时返回,则将其反序列化成响应对象交给 Proxy 统一处理;若对应的响应包没有按时返回,则走超时流程,仍然由 Proxy 统一处理,整个流程一定是闭环的。
服务端 RPC 中包括I/O线程、反序列化、序列化、队列、线程池等角色。 在 RPC 服务端,通常会有一个 IO 线程接收 RPC 客户端发送的请求;接收到的二进制流,首先反序列化成请求对象,然后将请求对象写入请求队列 ;再开启一个工作线程池,由工作线程从请求队列中获取请求后进行相应的业务逻辑处理,服务提供方对接口的实现逻辑就是由1这里的工作线程池进行处理的;对业务请求处理后的结果进行序列化转换成二进制流后由 IO 线程返回到 RPC 客户端;至此,整个 RPC 框架对请求的处理流程完全闭环。
RPC 架构中每个角色都有十分明确的职责,这些角色之间通过相互配合提供了服务之间远程调用的业务能力。
软件架构,到底在谈什么呢?应该谈的是软件的顶层结构,这个结构中包含了哪些角色?每个角色担负起了怎样的职责?这些角色之间是如何相互协作的?通过相互协作提供了怎样的业务能力?
当要设计软件架构时,我们就可以逆向来操盘了:业务需要软件提供什么样的业务能力?要提供这些业务能力需要哪些角色?每个角色需要担负什么职责?角色之间应该怎样交互?
最后,总结文中关键:
-
公式和套路,是我们解决问题的有力抓手;
-
软件架构就是系统的顶层结构;
-
软件架构包含三个核心要素:
(1)具有明确职责的角色
(2)角色之间的相互协作
(3)业务能力
-
设计软件架构时的思考步骤:
(1)业务需要软件提供什么样的业务能力?
(2)要提供这些业务能力需要哪些角色?
(3)每个角色需要担负什么职责?
(4)角色之间应该怎样交互?