读《架构风格与基于网络运算软件的架构设计》 - tianlu1677/tianlu1677.github.io GitHub Wiki

REST强调组件交互的可伸缩性、接口的通用性、组件的独立部署、以及用来减少交互延迟、增强安全性、封装遗留系统的中间组件(intermediary components)。我描述了指导REST的软件工程原则和为支持这些原则而选择的交互约束,并将它们与其他架构风格的约束进行了对比。最后,我描述了从把REST应用于HTTP(超文本移交协议)和URI(统一资源标识符)两个规范,并进一步将这两个规范部署到Web客户端和服务器软件的过程中学习到的经验教训。

这篇论文探索了在计算机科学的两个研究学科(软件和网络)边界上的交汇点。软件方面的研究长期以来关注于对软件设计进行分类和开发设计方法学,但是却很少能够客观地评估不同的设计选择对于系统行为的影响。网络方面的研究则恰恰相反,聚焦于系统之间普通通信行为的细节和提高特殊通信技术的性能,却常常忽略了一个事实,即改变一个应用的交互风格(the interaction style)对于性能产生的影响要比改变交互所使用的通信协议更大。我的工作的动机是希望理解和评估基于网络应用的架构设计,通过有原则地使用架构约束,从而从架构中获得所期待的功能、性能和社交三方面的属性。当为一组相互协作的架构约束取了一个名字之后,这组架构约束就成为了一种架构风格。

软件架构

1.1 运行时抽象

软件架构是一个软件系统在其运行过程中某个阶段的运行时元素(run-time elements)的抽象

软件架构的核心是抽象原则:通过封装来隐藏系统的一些细节,从而更好地识别和支持系统的架构属性

软件架构是软件系统在运行时的抽象,而软件结构则是静态源代码的属性

1.2 架构元素

软件架构由一些架构元素(组件、连接器和数据)的配置来定义,这些元素之间的关系受到约束,以获得所期待的一组架构属性。 进而反驳了一些其他人的说法。

1.2.1 组件()处理元素

组件是软件指令和内部状态的抽象单元,通过其接口提供数据的转换能力。数据转换的例子包括从二级存储将数据加载到内存、执行一些计算、转换为另外一种格式、封装在其他数据之中等等。每个组件的行为是架构的一部分,能够被其他组件观察到(observed)或辨别出来(discerned)。

1.2.2 连接器

连接器是对于组件之间的通讯、配合或者协作进行中间斡旋的一种抽象机制。

连接器将数据元素从它的一个接口移交(transferring)到另一个接口而不改变数据,以此来支持组件之间的通信。在其内部,连接器可以包含一个由组件组成的子系统,为了移交的目的对数据进行某种转换、执行移交、然后做相反的转换并交付与原始数据相同的结果。例如: 共享的表述、远程过程调用、消息传递协议和数据流。

1.2.3 数据

数据是组件通过连接器接收或发送的信息元素。数据的示例有:字节序列、消息、编码过的参数、以及序列化过的对象,但是不包括那些永久驻留或隐藏在组件中的信息。

1.3 配置

配置是在系统运行期间的组件、连接器和数据之间的架构关系的结构。大意为对上述三中的约束和规范

1.4 架构属性

软件架构的架构属性集合包括了对组件、连接器和数据的选择和排列所产生的所有属性。架构属性的例子包括了由系统获得的功能属性(functional properties achieved by the system)以及非功能属性(例如,进化的容易程度、组件的可重用性、效率、动态扩展能力,这些常常被称作品质属性)(quality attributes)[9]。

架构属性是由架构中的一组架构约束所产生的。

架构设计的目标: 创建一个包含一组架构属性的架构,这些架构属性形成了系统需求的一个超集

1.5 架构风格

架构风格是一组相互协作的架构约束,这些架构约束限制了架构元素的角色和功能,以及在任何一个遵循该架构风格的架构中允许存在的元素之间的关系。主要是用来区分不同的架构的。

用来解释一类架构描述(architectural descriptions)的一个约定的集合(collection of conventions)。

之所以出现架构风格,是一种指导性原则,是为了能够达到可复制的一种软件风格

1.6 模式和模式语言

一种设计模式被定义为一种重要的和重复出现的系统构造。架构中出现的模式,是适应当时的场景和空间的,这样才能更好的解决问题。

1.7 视图

描述了三种重要的软件架构视图:处理、数据、连接。处理视图侧重于流经组件的数据流,以及组件之间连接的那些与数据相关的方面。数据视图侧重于处理的流程,而不是连接器。连接视图侧重于组件之间的关系和通信的状态。 让我想起了我们曾经画过的流程图之类的。

1.8.1 设计方法学

1.8.2 设计 设计模式 模式语言手册

软件设计模式与架构风格相比,更加倾向于面向特定的问题(problem-oriented)

1.8.3 参考模型和特定于领域的软件架

Hayes-Roth等人[62] 将特定于领域的软件架构(DSSA)定义为由以下部分组成:a) 一种参考架构,为一个重大应用领域描述了一种通用的概念框架。b)一个包含了可重用领域专家知识的组件库。c) 一种应用的配置方法,用来在架构中选择和配置组件,以满足特殊的应用需求。

1.8.4 架构描述语言(ADL)

ADL是一种语言,用来明确说明软件系统的概念架构(conceptual architecture)和为这些概念架构建模。ADL至少包括以下部分:组件、组件接口、连接器、以及架构配置。

1.8.5 形式化的架构模型

第2章 基于网络应用的架构

2.1 范围

2.1.1 基于网络 vs 分布式

组件之间的通信仅限于消息传递,这种方式是基于网络的。 分布式系统在用户看来好像是普通的集中式系统(centralized system),但是运行在多个独立的CPU之上。相反,基于网络的系统有能力跨越网络运行(capable of operation across a network),但是这一点无需表达为对用户透明的方式。

2.1.2 应用软件 vs. 网络软件

应用软件的架构是对于整个系统的一种抽象,其中用户动作的目的(the goals of a user action)可以被表示为功能性的架构属性(functional architectural properties)。

网络抽象之目的是将比特从一个地点移动到另一个地点,而不关心为何要移动这些比特。

2.2 评估应用软件架构的设计

架构是架构设计的实现

第一个层面的评估由应用的功能需求来设定。必须都能完成它所需要完成的功能。 每一个架构设计决策可以被看作是对一种架构风格的应用。我们可以将所有可能的架构风格的空间看作是一棵继承树(a derivation tree),这棵树的根节点是“空”风格(没有任何架构约束)。

所以采用可观测性的方式来测量架构。

2.3 关键关注点的架构属性

2.3.1 性能

组件交互产生的影响对用户和网络效率非常重要,所以性能四必须要关注的。应用的性能取决于需求,然后是交互风格,然后架构,然后实现。

2.3.1.1 网络性能

网络性能这个度量手段用来描述通信的某些属性。吞吐量(throughput)是信息(既包括应用的数据也包括通信的开销)在组件之间移交的速率。开销(overhead)可分为初始化开销(initial setup overhead)和每次交互(都会产生的)开销(per-interaction overhead),这种区分有助于识别出能够跨多次交互共享(分摊)初始化开销的连接器。带宽(bandwidth)是在特定网络连接上可用的最大吞吐量。可用带宽(usable bandwidth)是指应用实际可用的那部分带宽。

架构风格对于网络性能的影响是通过影响每个用户动作的交互数量和数据元素的粒度来实现的。

2.3.1.2 用户感知的性能(User-perceived Performance)

用户感知的性能(user-perceived performance)与网络性能(network performance)的区别是,根据一个动作对于使用应用的用户的影响来度量这个动作的性能,而不是根据网络移动信息的速率来度量。用户感知的性能的主要度量手段是延迟(latency)和完成时间(completion time)。

延迟(latency)是指从最初的触发请求(initial stimulus)到得到最早的响应指示(the first indication of a response)之间持续的时间。延迟会发生在基于网络应用的处理过程中如下几个点上:1) 应用对于触发动作的事件的反应时间;2) 在组件之间建立交互所需的时间;3) 将交互请求数据传输到每个组件所需的时间;4) 在那些组件上处理每个交互请求所需的时间;以及 5) 应用能够对可用结果做呈现之前,完成移交和处理交互结果数据所需的时间。重要的是要注意到:虽然只有在第(3)点和第(5)点中存在着真正的网络通讯,但是架构风格对以上所有五点都会产生影响。此外,每个用户动作的多次组件交互也会增加延迟,除非它们能够并行发生(take place in parallel)。

完成时间(completion)是完成一个应用动作所花费的时间。完成时间取决于所有上述的延迟点。动作的完成时间和它的延迟之间的区别在于,延迟代表了一种应用能够增量地处理正在接收数据的程度。慢慢接受慢慢显示,和接受完再显示。

2.3.1.3 网络效率(Network Efficiency)

最佳的应用性能是通过不使用网络而获得的。 因此要尽可能少的使用网络。缓存,减少数据交互,cdn等手段

2.3.2 可伸缩性(Scalability)

可伸缩性表示通过主动的配置(within an active configuration),一个架构支持大量的组件或大量组件之间的交互的能力。

我们能够通过以下方法来改善可伸缩性:简化组件、将服务分布到很多组件(对交互去中心化)、以及根据监视获得的信息对交互和配置加以控制。架构风格可以通过确定应用状态的位置、分布的范围以及组件之间的耦合度,来影响上述这些因素。 可伸缩性还受到以下几个因素的影响:交互的频率、组件负载(the load on a component)随时间的分布是平均的还是存在峰值、交互是保证送达(guaranteed delivery)还是只需要尽量送达(best-effort)、一个请求是否包括同步或异步处理、以及环境是受控的还是无法控制的(即,你是否可以信任其他组件?)。

2.3.3 简单性(Simplicity)我理解为简化性

对组件之间的功能分配应用分离关注点原则(principle of separation of concerns)。组件要单一、简单、可独立。 将复杂性(complexity)、可理解性(understandability)和可验证性(verifiability)统一在简单性这个通用的架构属性中。 对架构元素应用通用性原则(principle of generality)有助于提高简单性,因为它减少了架构中的变数。对连接器应用通用性原则就产生了中间件

2.3.4 可修改性(Modifiability)

可修改性是对于应用的架构作出改变的容易程度。

  • 2.3.4.1 可进化性(Evolvability) 可进化性代表了能够改变一个组件实现而不会对其他组件产生负面影响的程度

  • 2.3.4.2 可扩展性(Extensibility) 可扩展性被定义为将功能添加到一个系统中的能力 [108]。动态可扩展性意味着能够将功能添加到一个已部署的系统中,而不会影响到系统的其他部分

  • 2.3.4.3 可定制性(Customizability) 可定制性是指临时性地规定一个架构元素的行为的能力,随后该元素能够提供一种非常规的服务。

  • 2.3.4.4 可配置性(Configurability) 它是指对于组件或者组件的配置在部署之后做修改

  • 2.3.4.5 可重用性(Reusability) 可重用性是应用架构的一个属性,如果一个应用架构中的组件、连接器或数据元素能够在不做修改的情况下在其他应用中重用,那么该架构就具有可重用性。

在架构风格中产生可重用性的主要机制是降低组件之间的耦合(一个组件知道其他组件的标识)和强制使用通用的组件接口。统一管道和过滤器架构风格中包括了这两种架构约束。

2.3.5 可见性(Visibility)

可见性是指一个组件对于其他两个组件之间的交互进行监视或进行中间斡旋(monitor or mediate)的能力。

2.3.6 可移植性(Portability)

如果软件能够在不同的环境下运行,软件就是可移植的

2.3.7 可靠性(Reliability)

可靠性可以被看作当在组件、连接器或数据之中出现部分故障时,一个架构容易受到系统层面故障影响的程度。 架构风格能够通过以下方法来改善可靠性:避免单点故障、增加冗余、允许监视、以及将故障的范围缩小到一个可恢复的动作(reducing the scope of failure to a recoverable action)。

第3章 基于网络应用的架构风格

基于第二章 对按照不同的分类进行了比较,从各个方面

3.2 数据流风格

3.2.1 管道、过滤器(Pipe and Filter, PF)

在管道和过滤器风格中,每个组件(过滤器)从其输入端读取数据流,并在其输出端产生数据流,通常会对输入数据流应用一种转换并增量地处理它们,以便在完全处理完输入之前就能够开始输出. 这种风格也被称作单路数据流网络(one-way data flow network)[6]。这里的架构约束是一个过滤器必须完全独立于其他过滤器(零耦合):它不能在其上行和下行数据流接口与其他过滤器共享状态、控制线程(control thread)或标识信息(identity)[53]

优点

  • PF允许设计者将系统全部的输入/输出看作是个别过滤器行为的简单组合(简单性)
  • PF支持重用,任何两个过滤器都能够被连接(hooked)在一起,只要允许数据在它们之间传输(可重用性)
  • 对PF系统做维护和增强很容易:能够将新的过滤器添加到现有的系统中(可扩展性)
  • 而旧的过滤器能够被改进后的过滤器所替代(可进化性)
  • PF允许对系统做一些特定类型的专门性分析(可验证性),例如吞吐量和死锁分析
  • ,PF天生支持并发执行(用户感知的性能)

缺点:

  • 通过长的管道时会导致延迟的增加

  • 如果一个过滤器不能增量地处理输入,那么只能进行批量的顺序处理(batch sequential processing occurs),此时不允许进行任何交互。一个过滤器无法与其环境进行交互,因为它无法知道是哪个特定输出流与哪个特定输入流共享同一个控制器(shares a controller)

  • 如果正在解决的问题不适合使用数据流的模式,这些架构属性会降低用户感知的性能。

  • 看不见的手= 为了建立整个应用而安排过滤器的配置

3.2.2 统一管道和过滤器(Uniform Pipe and Filter,UPF)

在PF的基础上 添加了一个约束,即所有过滤器必须具有相同的接口。

优点:

  • 可以随意排列,而不用在意

缺点:

  • 当发生格式转换时候,可能会降低网络性能

3.3复制风格(Replication Styles)

3.3.1 复制仓库 (Replicated Repository,RR)

基于复制仓库风格 [6] 的系统通过利用多个进程提供相同的服务,来改善数据的可访问性(accessibility of data)和服务的可伸缩性(scalability of service)。如CVS

优点:

  • 改善了用户感知的性能。实现途径是减少处理正常请求的延迟,并在主服务器(primary server)故障或有意脱离网络(intentional roaming off the network)时支持离线操作(disconnected operation)

缺点:

  • 复杂性

3.3.2 缓存(Cache,$)

复制个别请求的结果,以便可以被后面的请求重用。 分为延迟复制 和 主动复制

只有当数据被请求时才会传输数据,因此缓存风格更加高效。

3.4 分层架构(Hierarchical Styles)

3.4.1 客户-服务器(Client-Server,CS)