链接上文[解读REST] 2.REST用来干什么的?,上文中解释到什么是架构风格和应该以怎样的视角来理解REST(Web的架构风格)。本篇来介绍一组自洽的术语,用它来描述和解释软件架构;以及列举下对于基于网络的应用来说,哪些点是需要我们重点关注的。
1 软件架构
软件架构方面关注的是如何以最佳的方式划分一个系统、如何标识组件、组件之间如何通信、信息如何表达、组成系统的元素如何独立的进化,以及如何表达上述的内容。一个优秀的软件架构并非凭空靠想象得来的,每一个架构级的决策,都应该根据被设计的系统功能、行为和社交三方面的需求而作出。这是一个基本的设计原则,即“形式追随功能”,这条原则源自于建筑学领域在数百年中失败的建筑项目中获得的经验,但是却总被软件行业所忽略。“dedign-by-buzzword”的行为随处可见,出现这种行为的原因在于很多设计者在选择某一个优秀的软件架构时,不了解这个架构的适用场景是什么、背后是基于什么考量才得出的这样的架构,以及是否吻合自己的需求。下面先介绍一组自洽的术语来描述解释软件架构。
1.1 运行时抽象
软件架构是一个软件系统在其运行过程中某个阶段的运行时元素的抽象。一个系统可能会有多层抽象和多个运行阶段组成,每一个抽象和运行阶段都是自己的软件架构。
软件架构的核心是抽象:通过封装来隐藏系统的一些细节,从而更好的识别其架构属性。一个复杂的系统会有着多层抽象,每一层都有自己的架构,架构代表了某个层次上系统行为的抽象。除了层次之外,系统还会有多个运行阶段,比如启动、初始化、正常运行、停止、重新初始化等,每个阶段也都有自己的架构,比如配置文件在启动的时候会作为架构的一个数据元素来处理,到了正常运行的时候,这些数据元素已经分散到了系统的各个方面,在后续的阶段中就不会再把它作为一个数据元素了。
很多时候很容易把软件的源代码的静态结构视为软件架构,其实这是不妥的。软件结构是静态的源代码的组织结构方式,而非其真正运行起来时所表现出来的功能、层次和行为等。举个小例子,比如一个接口和它的一个实现者,它们在源代码级别是归属在一个地方的,但是在运行时我们关注的通常是其接口的行为,而非它的实现者身在何处。
1.2 架构元素
软件架构是由一些架构元素(组件、连接器和数据)的配置来定义的,这些元素之间的关系受到一组约束,以获得期望的架构属性。具体的元素类型如下:
- 组件:组件是软件指令和内部状态的抽象单元,通过其接口提供数据的转换能力。比如执行从硬盘加载数据到内存、执行一些计算、转换为另外一种格式等。每个组件的行为是架构的一部分,能够被其他组件观察到或者识别出来(即由某个组件为其他组件提供的接口和服务来定义该组件,而不是由该组件内部的实现来定义)。具体的例子有浏览器,网关,Web服务器(apache,ngnix.,iis)等。
- 连接器:对于组件之间的通信、配合或者协作进行中间周旋的一种抽象机制。区分连接器和组件的最佳方式是看其对数据的处理方式,连接器是把数据从一个接口移交到另外一个接口而不改变数据,以此来支持组件之间的通信。在连接器的内部,可能是由多个组件组成的子系统,为了移交的目的而对数据进行某种转换、执行移交然后做想法的转换并交付与原始数据相同的结果。然而在架构所能捕获的外部行为抽象中,可以忽略这些细节。具体的例子有缓存,SSL/TLS等等这类。
- 数据:数据是组件通过连接器接收或发送的信息元素。在基于网络应用的结构中,数据元素的特性往往会决定某一种架构风格是否适用,比如是直接的和组件进行交互,还是把组件转换为数据元素,通过网络移交该元素,然后再对该数据元素做相反的转换,得到一个能够在本地与之交互的组件。具体的数据例子有二进制数据,json,HTML文档,图片等。
1.3 配置
配置是系统在运行期间和组件、连接器和数据之间的架构关系的结构。此配置非彼配置,指的不是一个系统的各项配置参数,而是如何组织架构元素之间的关系。
1.4 架构属性
架构属性是软件架构对组件、连接器和数据的选择以及排列所产生的属性。其包含了系统的功能性属性以及非功能性属性(比如组件的可重用性、效率、扩展能力等)。架构属性是由一组架构约束产生的,而架构约束则是由在架构元素的某一个方面应用软件工程原则来驱动的。比如统一管道和过滤器架构风格通过在组件的接口上应用通用性的原则(强迫组件实现统一的接口),从而获得组件的可重用性和可配置性的架构属性。因此它的架构约束就是由通用性原则所驱动的“统一组件接口”,目的是获得上述的可重用性和和配置性这两个架构属性。举个简单的小实例,比如asp net mvc中的filter。
架构设计的目标是创建包含一组期望的架构属性的架构。这组架构属性是系统需求的一个超集,不同架构属性的在这组集合中的所占的比重取决于系统本身的需要。后面会介绍对于一个基于网络的应用来说,哪些架构属性是值得关注的。
1.5 架构风格
架构风格的定义在上一篇中已经简单的解释过了,这里更严谨的定义一下:架构风格是一组相互协作的架构约束,这些架构约束限制了架构元素的角色和功能,以及在任何一个遵循该架构风格的架构中允许存在的元素之间的关系。
一个架构中即会包含功能属性又会包含非功能属性,直接比较不同类型的架构时是比较困难的。架构风格是一种对架构进行分类并且定义它们的公共特征的机制。通过比较这部分公共部分的架构约束,来识别其能获得的架构属性的差异。好比上篇文章中说到的佛教建筑和古罗马建筑,比较它们的不同之处的办法在于用其各自的特征来对比。架构风格是比软件架构更加抽象的一个层次。比如说面向对象语言如果是架构风格,则Java,C#这些具体的实现了面向对象的语言则是具体的软件架构;当我们拿C#和C对比的时候,它们的本质差异归根到底是面向对象与面向过程的差异,而并不是分别实现了面向对象的C#和面向过程的C的具体细节差异。
2 基于网络应用的架构
软件架构可以存在于软件系统的多个层次之上。而REST则是聚焦于软件架构的最高层次的抽象,即跨越网络的运行环境,这里的组件是通过网络通信来实现组件的交互的。
软件方面的研究通常是关注软件设计如何分类和设计,但是却很少能客观的评估不同的设计对于系统行为有什么影响。
网络方面的研究则聚焦于系统之间的通信细节以及如何提高通信性能,却通常忽略一个事实,有时候改变一个应用的交互风格对于性能产生的影响要比改变所使用的通信协议更大。
基于网络 VS 分布式:基于网络的架构和软件架构的区别在于,组件之间的通信仅限于消息传递或者消息传递的等价物。基于网络的系统和分布式系统之间有细微的差异,比如一个分布式系统在其消费者看来和普通的集中式系统没什么区别(只是它背后是运行在多个独立的系统之上),也就是说其背后的实现对其消费者而言是透明的。而基于网络的系统则是无需对消费者透明的,比如消费者在执行一个网络请求时,如果本地的缓存可以满足其需要,则无需执行一个真正的网络请求。可以简单的认为分布式应用属于基于网络应用的一个子集。当然也并不是说分布式系统就没有缓存,这只是一种很细微的差异。
应用软件 VS 网络软件:应用软件通常来说指的是包含系统中包含业务行为的那部分功能。而网络软件的目的通常来说是关注与如何把数据跨越网络传递到另外一个地方,其关心的是如何传输,而不关心为何要传递这些数据。应用软件是对整个系统的一个抽象,比如用户执行一个动作,这个动作中会包含如何获取数据,执行网络请求和对请求结果进行呈现,而网络传输则是其中的一个环节。
3 基于网络应用的关键架构属性
REST关注的是基于网络的应用软件的架构设计,这也正是Web的诉求,Web是一个互联网规模的分布式超媒体系统,互联网规模意味着无法控制的可伸缩性和组件的独立部署。那么如何评估一个软件架构是否满足Web的需求,把它实现出来看一下?不现实吧。比较可取并且可行的办法就是利用上面所介绍到的架构属性来和Web的需求目的进行比较:首先是要满足功能性的要求,如果一个架构连基本的功能性需求都满足不了,评估对于它来说就没什么意义;其次则是非功能性的属性,这也是评估的主要关注点。下面列举下基于网络的应用的关键架构属性。
3.1 性能
性能这个话题或许是计算机行业永远也绕不开的话题。基于网络的应用的性能首先取决于应用的需求,然后是所选择的交互风格,接下来是实现的架构,最后则是每个组件的实现细节。换句话说,应用软件无法回避为了实现该软件的需求而付出的基本成本。比如说软件需要的数据位于系统A,并由系统B来处理,那么该软件就无法避免的要把数据从系统A移动到系统B(好比你要访问数据库,这部分网络开销是无论如何也绕不过去的)。无论架构怎样优秀,网络的速度再快,也不可能比直接在系统A处理要快。
- 用户感知的性能:用户的一个动作从发出,到其得到响应的时间,其主要有两个衡量指标,延迟和完成时间。比如打开一张图片,一边下载一边显示和等下载完后再显示,对于用户感知的性能是有明显的区别的。
- 网络性能:通常来讲,网络性能指的的网络传输的吞吐量以及其支撑这些吞吐量所产生的开销。
- 网络效率:一个有趣的现象是,最佳的应用性能通常时通过不使用网络而获得的。这也就是说,对于一个基于网络的应用来说,把对网络的使用降低到最少,才是最高效的架构风格。
3.2 可伸缩性
可伸缩性是可以通过配置来改变一个架构中的大量的组件之间的交互的能力。可以通过简化组件、去中心化、以及通过监视获得的信息来对交互进行配置和控制。
3.3 简单性
乍看之下这个架构属性有点笼统,其实也不然。比如http的无状态性特点,就使得服务器端的实现简单化,不用去保存或者恢复上一次的http请求,也使得中间的各个组件和连接器可以独立的理解单次请求的语义。通过对组件实施分离关注点原则,可以获得简单性;通过实施通用性原则,也可以提高简单性。
3.4 可修改性
可修改性指的是对于应用的架构做出改变的容易度。即使创造出了一个完全满足用户需要的系统,用户需求也总是会变化的,唯一不变的事情就是变化本身。基于网络的应用中的组件跨越了网络。比如系统中的一些组件难免的会升级为新组件,系统需要能面对新旧共存的局面等等。可修改性可以做如下更细节的划分:
- 可进化性:指一个组件改变了内部实现而不会对其他组件产生负面影响的程度。
- 可扩展性:指可以把一个新功能添加到系统中的能力。动态扩展性则是指将功能添加到一个已部署的系统中,而不会影响到系统中的其他部分。
- 可定制型:指临时性的规定一个架构元素的行为的能力。如果一个组件的客户端能够扩展该组件的服务,而不会对其他客户端产生影响,则该组件就是可定制的。
- 可重用性:指一个应用软件中的组件、连接器和数据元素能够不做修改的在其他应用重用。
3.5 可见性
可见性指的是一个组件对于其他组件之间的交互进行监视或者进行中间周旋的能力。拥有了可见性之后,就可以通过共享缓存来改善性能、通过分层来改善可伸缩性、通过允许中间件(比如防火墙)对交互做出检查来改善安全性、通过监视来改善可靠性等。
3.6 可移植性
指的是软件在不同的环境下运行的能力。比如那些将代码和代码处理的数据放在一起移动的架构风格可以获得此架构属性,比如虚拟机风格、移动代理风格,以及那些限制只能使用标准格式的数据元素的架构风格。
3.7 可靠性
从应用软件的角度来看,可靠性是在组件、连接器和数据之间出现部分故障时,一个架构受影响的程度。通过避免单点故障、增加冗余、系统监视等方法来改善可靠性。
4 总结
本篇主要介绍一组如何描述软件架构的自洽的术语,以及对于基于网络的应用来说,哪些架构属性是值得我们关注的。本篇均是笔者自己的一些理解,不免有错误之处,欢迎指正。
参考资料
理解本真的REST:http://www.infoq.com/cn/articles/understanding-restful-style/
架构风格与基于网络的软件架构设计-导读:http://www.infoq.com/cn/articles/doctor-fielding-article-review
架构风格与基于网络的软件架构设计:http://www.infoq.com/cn/minibooks/web-based-apps-archit-design
Architectural Styles and the Design of Network-based Software Architectures:https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm