Web开发工程师必看的一篇文章——《架构风格与基于网络的软件架构设计》

序言

看了一篇Roy Thomas Fielding于2000年发表的博士论文《Architectural Styles and the Design of Network-based Software Architectures》,总想留下点什么,这里记录一下一些内容。开发过一年多的web应用,了解一下软件架构设计还是很有必要的。下面大部分内容都出自原文,个人建议大家还是去看原文,原文链接参看文末链接1。

万维网(World Wide Web)

万维网是基于客户机/服务器方式的信息发现技术和超文本技术的综合。它的成功很大程度是因为其软件架构的设计满足了Internet规模(Internet-scale)的分布式超媒体系统的需求。

软件架构(Software Architecture)

软件架构的研究探索了如何以最佳的方式划分一个系统、如何标识组件、组件之间如何通信、信息如何沟通、系统的元素如何能够独立地进化,以及上述的所有东西如何能够使用形式化的和非形式化的符号加以描述。

运行时抽象

一个软件架构是一个软件系统在其操作的某个阶段的运行时(run-time)元素的抽象。一个系统可能由很多层抽象和很多个操作阶段组成,每个抽象和操作阶段都有自己的软件架构。

软件架构的核心是抽象原则:通过封装来隐藏系统的一些细节,从而更好地识别和支持系统的属性。一个复杂的系统包含有多层的抽象,每一层抽象都有自己的架构。架构代表了在某个层次上系统行为的抽象,架构的元素被描述为提供给同层的其他元素的抽象接口。在每一个元素之中,也可能还存在着另一个架构,定义了子元素的系统,这个系统实 现了由父元素的抽象接口所展示的行为。这样的架构可以递归下去直到最基本的系统元素:它们不能再被分解为抽象层次更低的元素。

除了架构的层次,软件系统通常拥有多个操作阶段,例如启动、初始化、正常处理、重新初始化和停止。每个操作阶段都有自己的架构。例如,配置文件在启动阶段会被当作架构的一个数据元素来处理,但是在正常处理阶段则不会当作一个架构元素,因为在这个阶段这些信息已经分布到了系统中的各处。

软件架构 (Software Architecture)和软件结构(Software Structure)之间的一个重要的区别是:

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

元素(Elements)

一个软件架构由一些架构元素(组件、连接器和数据)的配置来定义,这些元素之间的关系受到约束,以获得想要得到的一组架构属性。

软件架构定义为一组架构元素,这些元素具有通过一组基本原理(rationale)来描述的特殊形式。架构元素包括处理、数据、连接元素。形式则由元素的属性和元素之间的关系(即元素之上的约束)来定义。这些基本原理通过捕获选择架构风格、选择元素和形式的动机为架构提供了底层的基础。

组件(Components)

一个组件是软件指令和内部状态的一个抽象单元,通过其接口提供对于数据的转换。

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

连接器(Connectors)

一个连接器是对于组件之间的通讯、协调或者合作进行仲裁的一种抽象机制。

连接器是是对于组件之间的通讯、协调或者合作进行仲裁的一种抽象机制。连接器的例子包括共享的表述、远程过程调用(RPC)、消息传递协议和数据流。

数据(Data)

一个数据是组件通过一个连接器接收或发送的信息元素。

配置(Configuration)

一个配置是在系统的运行期间组件、连接器和数据之间的架构关系的结构。

属性(Attributes)

软件架构的架构属性集合包括了对组件、连接器和数据的选择和排列所导致的所有属性。 架构属性的例子包括了可以由系统获得的功能属性和非功能属性。

架构设计的目标是创建一个包含一组架构属性的架构,这些架构属性形成了系统需求的一个超集。不同架构属性的相对重要性取决于想要得到的系统本身的特性。

风格(Style)

一种架构风格是一组协作的架构约束,这些约束限制了架构元素的角色和功能,以及在任何一个遵循该风格的架构中允许存在的元素之间的关系。

因为一种架构既包含功能属性又包含非功能属性,直接比较不同类型系统的架构,或者甚至是比较在不同环境中的相同类型的系统会比较困难。风格是一种用来对架构进行分类和定义它们的公共特征的机制。

模式和模式语言(Pattern And Pattern Language)

面向对象编程社区一直在基于对象的(Object-Based)软件开发领域探索如何使用设计模式和模式语言来描述重复出现的抽象。一种设计模式被定义为一种重要的和重复出现的系统构造。一种模式语言是一个模式的系统,以一种对这些模式的应用加以指导的结构来进行组织。

视图(View)

一种架构视图常常是特定于应用的,并且基于应用的领域而千变万化......我们已经看到架构视图解决了很多的问题,包括:与时间相关的问题(Temporal Issues)、状态和控制方法、数据表述、事务生命周期、安全保护、峰值要求和优雅降级(Graceful Degradation)。无疑,除了上述的这些视图,还存在着很多可能的视图。

基于网络的应用的架构

架构可以存在于软件系统的多个层次上。基于网络的架构(Network-Based Architectures)与软件架构(Software Architectures)的 主要区别通常是:组件之间的通信仅限于消息传递(Message Passing)或者消息传递的等价物。

一种架构风格是一组协作的架构约束,给它取一个名称是为了便于引用。每个架构设计决策可以被看作是对一种风格的应用。因为添加的一个约束可能是从一种新的风格继承来的,我们可以将所有可能的架构风格的空间看作是一棵继承树,这棵树的根节点是“空”风格(没有约束)。当它们的约束不存在冲突时,多种风格可以进行组合,形成混合风格,最终可以形成一种代表了架构设计的完整抽象的混合风格。因此一个架构设计能够通过将约束的集合分解到一棵继承树的方法来加以分析,并且可以评估由这棵树所代表的约束的累积效果。

关键的架构属性

下面列举的是明显受到该风格影响的属性,而并非是一个全面的架构属性清单。

  • 性能(Performance):包含吞吐量(Throughput)、负载(Overhead)、带宽(Bandwidth)、延迟(Latency)、完成时间(Completion)等。
  • 可伸缩性(Scalability)
  • 简单性(Simplicity)
  • 可修改性(Modifiability):包含可进化性(Evolvability)、可扩展性(Extensibility)、可定制性(Customizability)、可配置型(Configurability)、可重用性(Reusability)。
  • 可见性(Visibility)
  • 可移植性(Portability)
  • 可靠性(Reliability)

基于网络的架构风格

  • 数据流风格(Data-flow Styles):管道和过滤器
  • 复制风格(Replication Styles):复制仓库、缓存
  • 分层风格(Hierarchical Styles):客户-服务器(Client-Server)、分层系统(Layered System)、客户-无状态-服务器(Client-Stateless-Server)、客户-缓存-无状态-服务器(Client-Cache-Stateless-Server)、分层-客户-缓存-无状态-服务器(Layered-Client-Cache-Stateless-Server)、远程会话(Remote Session)、远程数据访问(Remote Data Access)
  • 移动代码风格(Mobile Code Styles):虚拟机(Virtual Machine)、远程求值(Remote Evaluation)、按需代码(Code on Demand)、分层-按需代码-客户-缓存-无状态-服务器(Layered-Code on Demand-Client-Cache-Stateless-Server)、移动代理(Mobile Agent)
  • 点对点风格(Peer-to-Peer Styles):基于事件的集成(Event-Based Integration)、C2、分布式对象(Distributed Objects)、被代理的分布式对象(Brokered Distributed Objects)

设计Web架构:问题与洞察力

Web的主要目的是旨在成为一种共享的信息空间(A Shared Information Space),人们和机器都可以通过它来进行沟通。”我们需要的是一种人们用来保存和构造他们自己的信息的方式,无论信息在性质上是永久的还是短暂的,这样信息对于他们自己和其他人都是可用的,并且能够引用和构造由其他人保存的信息,而不必每个人都保持和维护一份本地的副本。

上面是万维网应用领域的需求,持续至今,Web是成功的。Internet开发者社区曾担心Web使用的快速增长率伴随着HTTP的一些糟糕网络特性,将很快超过Internet基础设施容量,并导致崩溃。

早期的 Web 架构基于一些可靠的原则:分离关注点、简单性、通用性,但是缺乏对于 架构的描述和基本原理。

万维网架构需求的挑战是开发一个用来设计架构改进的方法,使得能够在改进部署之前对它们进行评估。

表述性状态转移(Representational State Transfer,REST)

表述性状态转移是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。REST 强调组件交互的可伸缩性、接口的通用性、组件的独立部署、以及用来减少交互延迟、增强安全性、封装遗留系统的中间组件(intermediary components)。

REST风格推导

“空”风格仅仅是一个空的约束集合。从架构的观点来看,空风格描述了一个组件之间没有明显边界的系统。这就是我们描述 REST的起点。

客户-服务器(Client-Server)

客户-服务器约束背后的原则是分离关注点。通过分离用户接口和数据存储这两个关注点,我们改善了用户接口跨多个平台的可移植性;同时通过简化服务器组件,改善了系统的可伸缩性。然而,对于 Web来说,最重要的是这种关注点的分离允许组件独立地进化, 从而支持多个组织领域的 Internet 规模的需求。

无状态(Stateless)

再为客户-服务器交互添加一个约束:通信必须在本质上是无状态的,因此从客户到服务器的每个请求都必须包含理解该请求所必需的所有信息,不能利用任何存储在服务器上的上下文,会话状态因此要全部保存在客户端。

这个约束导致了可见性、可靠性和可伸缩性三个架构属性。

  • 改善了可见性是因为监视系统不必为了确定一个请求的全部性质而去查看该请求之外的多个请求。
  • 改善了可靠性是因为它减轻了从局部故障中恢复的任务量。
  • 改善了可伸缩性是因为不必在多个请求之间保存状态,从而允许服务器组件迅速释放资源,并进一步简化其实现,因为服务器不必跨多个请求管理资源的使用。

与大多数架构上抉择一样,无状态这一约束反映出设计上的权衡。其缺点是:由于不能将状态数据保存在服务器上的共享上下文中,因此增加了在一系列请求中发送的重复数据(每次交互的开销),可能会降低网络性能。

缓存(Cache)

添加缓存约束的好处在于,它们有可能部分或全部消除一些交互,从而通过减少一系列交互的平均延迟时间,来提高效率、可伸缩性和用户可觉察的性能。然而,付出的代价是,如果缓存中陈旧的数据与将请求直接发送到服务器得到的数据差别很大,那么缓存会降低可靠性。

早期的 Web 架构,如下图所示,是通过客户-缓存-无状态-服务器的约束集合来定义的。也就是说,1994 年之前的 Web 架构的设计基本原理聚焦于在 Internet 上交换静态文档的无状态的客户-服务器交互。交互的通信协议仅包含了对非共享缓存的初步支持,但是并没有限定接口要对所有的资源提供一组一致的语义。相反,Web 依赖于使用一个公共的客户-服务器实现库来维护 Web 应用之间的一致性。除了静态的文档之外,请求还能够识别出动态生成响应的服务,例如图像地图(Image-Maps)和服务器端脚本 (Server-Side Scripts)。

统一接口(Uniform Interface) 

使REST 架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口。通过在组件接口上应用通用性的软件工程原则,整体的系统架构得到了简化,交互的可见性也得到了改善。实现与它们所提供的服务是解耦的,这促进了独立的可进化性。然而,付出的代价是,统一接口降低了效率,因为信息都使用标准化的形式来转移,而不能使用特定于应用的需求的形式。REST 接口被设计为可以高效地转移大粒度的超媒体数据,并针对 Web 的常见情况做了优化,但是这也导致了该接口对于其他形式的架构交互并不是最优的。

为了获得统一的接口,需要有多个架构约束来指导组件的行为。REST 由四个接口约束来定义:资源的识别(Identification Of Resources)、通过表述对资源执行的操作、自描述的消息(Self-Descriptive Messages)、以及作为应用状态引擎的超媒体。

分层系统(Layered System)

为了进一步改善与 Internet 规模的需求相关的行为,添加了分层的系统约束。分层系统风格通过限制组件的行为,将架构分解为若干等级的层。通过将组件对系统的知识限制在单一层内,为整个系统的复杂性设置了边界,并且提高了底层独立性。我们能够使用层来封装遗留的服务,使新的服务免受遗留客户端的影响,通过将不常用的功能转移到一个共享的中间组件中,从而简化组件的实现。中间组件还能够通过支持跨多个网络和处理器的负载均衡,来改善系统的可伸缩性。

分层系统的主要缺点是:增加了数据处理的开销和延迟,因此降低了用户可觉察的性能。对于一个支持缓存约束的基于网络的系统来说,可以通过在中间层使用共享缓存所获得的好处来弥补这一缺点。在组织领域的边界设置共享缓存能够获得显著的性能提升。

按需代码(Code on Demand)

通过下载并执行Applet形式或脚本形式的代码,REST允许对客户端的功能进行扩展。这样,通过减少必须被预先实现的功能的数目,简化了客户端的开发。允许在部署之后下载功能代码也改善了系统的可扩展性。然而,这也降低了可见性,因此它只是REST的一个可选的约束。

REST所继承的风格约束

REST所继承的风格约束如下图所示,REST由一组选择用来在候选架构上导致想要得到的属性的架构约束组成。尽管这些约束每一个都能够独立加以考虑,但是根据它们在通用的架构风格中的来源来对它们进行描述,使得我们理解选择它们背后的基本原理更加容易。

REST架构的元素 

表述性状态转移(REST)风格是对分布式超媒体系统中的架构元素的一种抽象。REST忽略了组件实现和协议语法的细节,以便聚焦于以下几个方面:组件的角色、组件之间的交互之上的约束、组件对重要数据元素的解释。REST 包括了一组对于定义 Web 架构基础的组件、连接器和数据的基本约束,因此它代表了基于网络的应用的行为的本质。

数据元素(Data Elements)

在分布式对象风格中,所有的数据都封装在数据的处理组件之中,并且被数据的处理组件隐藏起来。

针对数据的处理,一个分布式超媒体系统的架构师仅拥有三种基本的选项:

  1. 在数据的所在地对数据进行呈现,并向接收者发送一个固定格式的映象(A Fixed-Format Image)
  2. 将数据和呈现引擎封装起来并将两者一起发送给接收者
  3. 发送原始数据和一些描述数据类型的元数据,这样接收者就能够选择它们自己的呈现引擎。

REST的数据元素如下,

  • 资源:REST 对于信息的核心抽象。任何能够被命名的信息都能够作为一个资源。
  • 表述:REST使用一个表述来捕获资源的当前的或预期的状态、在组件之间传递该表述。一个表述是一个字节序列,以及描述这些字节的表述元数据。表述的其他常用但不够精确的名称包括:文档、文件、HTTP消息实体、实例或变量。

连接器(Connectors) 

REST使用多种不同的连接器类型来对访问资源和转移资源表述的活动进行封装。连接器代表了一个组件通信的抽象接口,通过提供清晰的关注点分离、并且隐藏资源的底层实现和通信机制,从而改善了架构的简单性。REST的连接器如下,

所有的 REST 交互都是无状态的。也就是说,无论之前有任何其他请求,每个请求都包含了连接器理解该请求所必需的全部信息。这个约束能够实现四个功能:

  1. 它使得连接器无需保存请求之间的应用状态,从而降低了物理资源的消耗并改善了可伸缩性
  2. 它允许对交互进行并行处理,处理机制无需理解交互的语义
  3. 它允许中间组件孤立地查看并理解一个请求,当需要对服务作出动态安排时,这是必需要满足的
  4. 它强制每个请求都必须包含可能会影响到一个已缓存响应的可重用性的所有信息

组件(Components)

REST 组件根据它们在整个的应用动作(Application Action)中的角色来进行分类。如下图,

REST架构的视图

过程视图(Process View)

架构的过程视图的主要作用是,通过展示数据在系统中的流动路径,得出组件之间的交互关系。

一个基于REST架构的过程视图如下,其中WAIS(Wide Area Information Server)是一种与Web架构分离的信息服务,ORB(Object Request Broker)是对象请求代理。每一个组件只知道与它们自己的客户端或服务器连接器的交互,整个过程拓扑是视图的产物。

 连接器视图(Connector View)

架构的连接器视图集中于组件之间的通信机制。对一个基于 REST 的架构而言,我们对定义通用资源接口的约束尤其感兴趣。

REST 并不限制通信只能使用一种特殊的协议,但是它会限制组件之间的接口,因此也限制了交互的范围和在组件之间可能作出的有关实现的假设。例如,Web 的主要转移协议是HTTP,但是 REST 架构也包括了对来自 Web 出现之前就已存在的网络服务器的资源,包括 FTP、Gopher和 WAIS的无缝地访问。与那些服务的交互被限制为只能使用 REST连接器的语义。

数据视图(Data View)

一个架构的数据视图展示了信息在组件之间流动时的应用状态。因为REST被明确定位于分布式信息系统,它将一个应用看作是一种信息(Information)和控制(Control)的聚合体,用户可以通过这个聚合体执行他们想要完成的任务。

经验与评估

自从1994年以来,REST架构风格就被用来指导现代Web架构的设计和开发。

开发 REST 的动机是为Web应该如何运转创建一种架构模型,使之成为Web协议标准的指导框架。

超文本转移协议(HTTP,Hyper Text Transfer Protocol)在Web架构中有一个特殊的角色,既作为在 Web 组件之间通信的主要的应用级协议,也作为特别为转移资源的表述而设计的唯一的协议。与URI不同,需要做大量的修改才能使HTTP能够支持现代的Web架构。

HTTP协议从HTTP/1.0开始做了哪些改变?或者几个版本有什么区别?

  • 持久连接:每个连接单个请求/响应的行为实现起来很简单,但是它导致了对于底层TCP传输机制低效率的使用,因为每次交互都要重新建立连接的开销,以及TCP的拥塞控制慢启动(Slow-Start Congestion Control)算法的特性。作为其结果,出现了一 些提议的扩展,在单个连接中组合多个请求和响应。但它违反了 REST 的几个约束。最终决定在HTTP/1.1将持久连接作为默认的选项(Keep-Alive)。
  • 多路复用:HTTP/2.0在HTTP/1.1的基础上使用了多路复用技术。做到同一个连接并发处理多个请求,且并发请求数量比HTTP1.1多几个数量级。
  • 头部数据压缩:HTTP/2.0使用了HPACK(Header Compression for HTTP/2)头部压缩算法对请求头信息进行压缩编码,从而提高带宽利用率。
  • 其他:上面只写了部分,具体差别可以参看文末链接3。

参考链接:

1、Architectural Styles and the Design of Network-based Software Architectures

2、Java Applet 基础 | 菜鸟教程

3、首页 | HTTP协议中文网 HTTP/1.1 HTTP/2 HTTP/3

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值