推动软件工程不断发展的通常是实际开发或使用软件时遇到的问题。那么在软件性能优化中,架构起到了怎样的作用呢?讨论这个问题之前,我们需要先明确一些概念,了解软件架构的内涵和外延。遗憾的是,业界对软件架构的理解一直存在一些争议。从不同的时间和空间视角审视软件架构,或许才能真正理解软件架构的含义,统一其时空视角。
计算机学科以及软件工程中的很多概念都是从其他学科借鉴而来的,架构的概念也不例外,借鉴自建筑学中的architecture一词。IEEE从空间的视角对软件架构做出了定义: 架构是系统的基本结构,这种结构体现在组件内系统之间的关系、系统与环境之间的关系,以及指导系统设计与演化的原则上。
IEEE的这个定义侧重于空间的视角,软件架构表征的是软件的空间组成结构。软件存在的空间既包括物理空间,如软件运行所需的计算机硬件空间、网络物理空间等,也包括虚拟空间,如软件执行所需的指令空间和地址空间等。空间是客观存在的,所以架构也是客观存在的。不论人们是否关注或有意识地考虑架构,它都存在于系统的具体环境中,而且并不孤立,有着自己明确的目标。IEEE的定义中提及了系统的设计和演化,因此不可避免地融入了时间的因素。在考虑演化时,需要结合软件的生命周期,否则演化将失去意义。
计算机专家Grady Booch对软件架构给出了不同的定义: 软件架构代表形成系统的重要设计决策,这些决策涉及软件的组织、组成系统的结构化元素及其接口的选择、元素之间协作时特定的行为、结构化元素和行为元素形成更大子系统时所用的组合方式、引导这一组织(也就是这些元素及其接口)的协作风格等。其中,不同对象的重要程度用其变化带来的成本变化来衡量。 这个定义侧重于时间的视角,更注重一系列动作。这个定义通过“重要设计决策”形成流程,并指出架构与成本之间的相关性。
关于软件架构,还有很多类似的定义和理解,但基本上可以分为两类:从空间角度看,是面向体系结构建设的“组成论”;从时间角度看,是面向设计流程实现的“决策论”。然而,空间和时间是密不可分的。维基百科上对软件架构的定义就是从时空统一的视角给出的: 软件架构是规划、设计和构建软件及其组成结构的过程和最终成果。
任何概念都有其时空边界,软件架构也有自己的环境约束。引入约束条件后,软件架构就可为软件提供一个高级抽象,其中包括结构、行为和属性。软件架构由组件的描述、组件间的相互作用、指导组件集成的模式和这些模式的约束组成。软件架构不仅显示了软件需求和软件结构之间的对应关系,还规定了整个软件的组织和拓扑结构,并提供了一些设计决策的基本原理。
综上所述,笔者认为: 软件架构是软件的空间体系结构和系统构建的时间决策流程的统一。
既然软件架构是空间体系结构和时间决策流程的结合,我们就可以从时空视角来对软件架构进行分类了。当然,时空是一体的,只是侧重的视角不同而已。
软件架构是面向计算机系统的。因此,回顾计算机系统的体系结构有助于理解软件架构的分类视角。尽管计算机系统的体系结构不断演变,但基本的抽象仍然是冯·诺依曼提出的计算机体系结构,如图1-1所示。
图1-1 冯·诺依曼的计算机体系结构
冯·诺依曼体系的核心是将需要的程序和数据送入计算机中。计算机必须具备长期记忆程序、数据、中间结果和最终运算结果的能力。计算机能完成各种算术运算、逻辑运算和数据传送等加工处理工作,并能根据需要控制程序的执行路径,同时根据指令控制机器的各个部件协调工作,以便将处理结果按照要求输出给用户。
将指令和数据同时存放在存储器中是冯·诺依曼计算机方案的一个特点。在计算机体系结构中,软件架构可以分为逻辑架构、数据架构和物理拓扑架构。
1.逻辑架构
逻辑架构是由软件(用户)需求驱动的,是基于对用户需求的考量形成的。软件的逻辑架构是对整个系统进行抽象分解,将系统划分为多个逻辑单元,每个逻辑单元都能实现自己的功能。
逻辑架构关注功能模块的职责划分和接口定义。其中,需要特别注意的是不同粒度的功能职责,如逻辑层、功能子系统、模块和关键类等。不同通用程度的功能需要进行分离,并分别封装到专门模块、通用模块或通用机制中。
2.数据架构
数据架构是根据业务功能需求的数量来设计的,所有业务都是围绕数据展开的。数据是软件的核心,数据架构将逻辑架构中确定的功能映射到数据处理中。数据架构明确了支持业务所需的各种数据以及这些数据之间的关系。
3.物理拓扑架构
物理拓扑架构指的是软件具体的部署和运行环境。它描述了软件运行所需的计算机、网络、硬件设施等情况,以及将软件部署到硬件资源的情况、运行期间的配置情况。换句话说,物理拓扑架构明确了程序在计算机系统上的映射方式,以及数据在计算机系统中的存储、读写和传输方式。它同时考虑了具体业务功能在逻辑架构中的分布和数据处理功能在数据架构中的分布。
架构的本质就是对系统进行有序化重构,不断降低系统无序的程度,使系统不断进化。从这个角度看,软件架构可以分为开发架构、运行架构。
1.开发架构
开发架构明确了软件开发流程中的重要决策。它涵盖了具体模块(如源代码、程序包、编译后的目标文件、第三方库和配置文件等)的组织方式。开发架构确保了软件开发期间的质量,包括代码的可扩展性、可重用性、可移植性、易理解性和易测试性等。
2.运行架构
运行架构描述了软件的运行状态,如软件中的对象交互、用户与软件之间的交互、系统间通信等。运行架构还涵盖了软件的非功能性需求,如安全性、可靠性、可伸缩性等质量相关的需求,以及系统响应时间、吞吐量等性能相关的需求。
与软件需要进行设计一样,软件架构也需要进行设计。软件架构设计是开发者对一个软件内部元素及其关系的主观映射,是一系列相关的抽象模式,用于指导大型软件各个方面的设计。在进行软件架构设计时,空间体系结构设计通常是核心,时间流程决策是关键。软件架构的设计通常遵循空间体系结构的设计原则,并与时间流程上的决策相结合。
面向对象的编程和软件设计模式通常会遵循6个原则,其中,前5个组合到一起就是我们常说的SOLID设计原则。这些原则也可以扩展到软件架构设计领域。
1.单一职责原则
所谓单一职责原则(Single Responsibility Principle,SRP),是指对一个类、模块或子系统而言,应该仅有一个引起它变化的原因。在软件空间体系结构中,元素的划分需要保持职责的清晰,最好不要满足多种不同的需求,否则会导致耦合过强,不利于后期的扩展和维护。划分职责的困难在于缺乏一个标准,最终需要从实际需求出发去考虑。领域驱动的软件架构设计在很多情况下是一种行之有效的方法。
2.开闭原则
软件空间体系结构中的类、函数、模块乃至子系统等元素应该对扩展开放,对修改封闭。也就是说,这些元素应该易于扩展,在需要根据需求变化时不需要去修改基础的逻辑代码。换句话说,一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现改变,这是高内聚的另一种体现,这就是所谓的开闭原则(Open Close Principle,OCP)。
3.里氏替换原则
在面向对象的编程设计中,里氏替换原则(Liskov Substitution Principle,LSP)指的是在基类出现的任何地方,其子类也可以出现,并且不会引发错误。换句话说,凡是基类适用的地方,子类一定也适用。在扩展的软件架构设计领域中,里氏替换指的是软件空间体系结构中类、函数、模块甚至子系统的可替代性。里氏替换原则是软件容错和灾备的指导原则,也是松耦合的一种体现。
4.接口隔离原则
软件空间体系中的不同元素,一般通过接口实现交互。但是我们必须避免建立臃肿庞大的接口,尽量建立功能单一的接口。换句话说,接口应该尽可能细化,同时方法的数量应该尽量少。单一职责原则的重点在于职责的划分,通常根据业务逻辑进行划分;而接口隔离原则(Interface Segregation Principle,ISP)则要求尽量减少接口中的方法,不依赖不需要的接口,这样更便于进行重构、变更和重新部署。接口是对外的承诺,我们应该提高接口和类的处理能力,减少对外的交互。简而言之,单一职责原则的目的是松耦合,接口隔离原则的目的是高内聚。
5.依赖倒置原则
依赖倒置原则(Dependence Inversion Principle,DIP)指的是高层次的软件空间体系结构元素不依赖于低层次的软件空间体系结构元素的实现细节,而是依赖于抽象,换句话说就是面向接口编程。在分布式软件中,通信方式和协议的实现是依赖倒置原则的具体体现。
6.迪米特原则
迪米特原则(Least Knowledge Principle,LKP)又称最少知识原则,指的是软件空间体系结构中类、函数、模块乃至子系统等元素应该对其他元素了解最少,也就是说,一个软件实体对自己的依赖感知最少,只关心那些必需的接口。它是对接口隔离原则的有益补充,是松耦合的另一种体现。
总而言之,在面向空间体系结构的软件架构设计中,可通过上述设计原则来实现松耦合和高内聚。
有交互就代表会涉及时序,软件架构中的空间体系结构必然会与时间流程决策相结合,而且随着软件工程的不断发展产生了一系列架构模式。
1.分层架构模式
分层架构模式是最常见的架构模式,也被称为 N 层架构模式。它是单一职责原则的宏观体现,强调分离。通过将组件划分到不同的层次,组件能够轻松实现各自的角色和职责。每个层次中的组件仅负责该层次的逻辑,这样更容易进行开发、测试、管理和维护。分层隔离有助于降低整个软件的复杂性。某些功能并不需要经过每一层,因此需要根据开闭原则来简化实现。
2.微内核架构模式
微内核架构模式也被称为插件架构模式,可用于实现基于产品的应用,如Eclipse。在微内核的基础上添加插件,可以提供不同的产品。微内核架构主要包含两个组件——核心系统和插件模块。应用逻辑通过分成核心系统和插件模块对外提供可扩展、高灵活和特性隔离的功能。
实际上,许多架构模式都可以作为整个系统的插件。换句话说,微内核架构模式可以嵌入其他架构模式中,通过插件还可以提供演化和增量开发的功能。
3.事件驱动架构模式
事件驱动架构模式是一种流行的分布式异步架构模式,用于创建可伸缩的软件。这种模式适用于各种规模的软件,并具有自适应性。它由高度解耦的、单一目的的事件处理组件组成,可以异步接收和处理事件。
一般来说,事件驱动架构模式主要包含4个组件:事件队列、调停者、事件通道和事件处理器。客户端创建事件并将其发送到事件队列,调停者接收事件并将其传递给事件通道,事件通道将事件传递给事件处理器,最终由事件处理器完成事件处理。调停者类似于集中调度中心。一种常见的事件驱动架构模式变体是使用分布式调度中心,将事件通道直接置于消息队列中。
4.微服务架构模式
微服务架构模式是SOLID设计原则的集中体现,其核心概念是具备高可伸缩性、易于部署和交付的独立部署单元,包含业务逻辑和处理流程的服务组件。内部服务组件之间的通信方式有两种:基于HTTP的同步机制(REST API和RPC
)和基于消息队列的异步消息处理机制。
从空间上来说,服务组件可以是单一的模块或者一个大的软件,这两者都代表单一功能。
5.云服务架构模式
云服务架构模式是XaaS的综合体,基于云的架构可以使应用规模对服务的影响最小化。云服务架构模式起源于分布式共享内存的想法,典型代表是无服务架构。
要打破规模化,就要移除中心数据库,使用可复制的内存网格。应用的数据保存在所有活动的处理单元的内存中。可以根据应用规模加入和移除处理单元。小型软件可以使用一个处理单元,大型软件可以分隔成多个处理单元。处理单元还包括数据网格。虚拟化中间件负责管理和通信,并处理数据的同步和请求。这就是云服务架构模式的工作原理。
各种架构模式都或多或少地体现了逻辑架构、数据架构、物理拓扑架构、开发架构和运行架构,所以说它们是软件架构时空视角的结合体。
如果缺乏良好的技术储备,那么就很难设计出良好的软件架构。而当前新技术层出不穷,若想涉足每个技术领域,则几乎是不可能的事情。怎么办?我们可以尝试从时空视角对软件架构中的常用技术栈进行分类。架构设计的常用技术栈如图1-2所示。
图1-2 架构设计的常用技术栈
图1-2左侧所示的是空间维度的相关技术。
· 操作系统: 确定了软件架构的环境边界。
· 数据存储: 因为数据是软件的核心,所以我们必须了解文件系统、对象存储、关系型数据库以及NoSQL数据库等与数据存储相关的技术。
· 网络通信: 这是一个覆盖更广泛的概念,至少要掌握7层协议模型和一些主流的通信协议,如DNS、TCP/IP、HTTP,以及不同网络协议对网络编程的影响。
· 框架与库: 与采用的编程语言密切相关,不同的语言有不同的框架与库,可选项众多,需要从面向领域和场景的角度进行选择。
· 安全: 这里就不展开介绍了。
· 微服务: 这是一种架构方法,这里仅将微服务架构作为典型代表进行介绍。服务的划分与业务紧密相关,服务独立后需要考虑服务的发现和服务间的通信,最后是服务治理。
· 云服务: 云服务的出现使得小团队也能完成大事情,这里的云服务指的是基础设施即服务(XaaS)。
· 大数据/AI: 这必将成为工程师团队的重要战力,涉及专业知识、数学算法和计算环境。大数据的相关技术也是人工智能赋能软件的基础。
图1-2右侧所示的是时间维度的相关技术,主要包括运行架构(尤其是与性能相关的)的技术,以及决策流程所涉技术和方法(尤其是与研发效率相关的)。
· 开发环境: 工具对工作的重要性不言而喻,开发环境在工程效率中占据首要地位,也是开发架构中的重要组成部分。
· 编程语言: 不同的编程语言适用于不同的场景。一般来说,编程语言并没有优劣之分,不同的编程语言各有所长。
· 敏捷开发: 每个人都不是“单打独斗”的,掌握协同工具以及支持CI/CD的工具链或者平台,可以使团队更加敏捷并提高整体的研发效率。此外,敏捷开发并不是以牺牲软件质量为代价的。
· DevOps: 开发架构中的一种组织方式。
· 业务与代码: 业务是软件提供的能力,代码是软件的实现载体,都是软件不可或缺的要素。
· 运行时调优: 在运行架构中,性能是诸多非功能性约束中的首要因素,直接影响用户的体验。首先,要从业务和代码层面确保性能,而单元测试是必要的条件。在运行时进行调优,或者说是单机性能优化时,通常从加载和依赖开始,包括代码优化和虚拟机优化,如Java语言的VM调优、Linux内核参数调优。
· 数据访问: 数据库往往是整个系统的性能瓶颈,数据访问必须具备高可用性,选择和使用数据连接池是必备条件。
· 缓存: 该技术是降低负载、提高系统性能的必备技术,可以在客户端、网络侧和服务端3个环节应用缓存技术。
· 均衡: 指的是负载均衡,这同样是一种以空间换时间的技术。
· 消息队列: 可以通过消息队列来提升传输性能。
那么,为什么系统性能在诸多非功能性约束中排在第一位呢?下一章将对系统性能展开进一步讨论。
软件架构是什么?不同的人或组织会给出不同的定义。不同观点只是在时空视角下侧重点不同而已。
时间和空间是密不可分的,“软件架构是规划、设计和构建软件及其组成结构的过程和最终成果”,这是一个时空统一的观点。同样,在时空视角下,软件架构可以分为多种类型。面向空间视角的软件架构包括逻辑架构、数据架构和物理架构,面向时间视角的软件架构包括运行架构和开发架构。
软件架构的设计遵循SOLID设计原则,目标是实现“高内聚,松耦合”的空间体系结构。软件架构中的空间体系结构与时间流程决策相结合,诞生了一系列架构模式,主要包括分层架构模式、微内核架构模式、事件驱动架构模式、微服务架构模式和云服务架构模式。
软件架构的设计需要软件技术和研发方法论的支持。本章介绍了软件架构设计中常用的技术栈,并提出了“系统性能是软件运行架构的关键”这一观点。