现在的位置: 首页 > 综合 > 正文

Fielding博士论文导读—-第5章

2013年04月06日 ⁄ 综合 ⁄ 共 4881字 ⁄ 字号 评论关闭
大家如果跟着我一直读下来,我们已经走过了很长的一段路。在第五章,总算到了我们对于这篇论文最感兴趣的部分——满足Web需求的技术架构,即REST架构风格。很多人读Fielding的论文喜欢直接跳到第五章,但是这是一种糟糕的阅读方式。前面所有的章节对于推导出REST这种新型的架构风格来说都是必须的。前面的章节和第五章是因和果的关系,如果只读第五章,就只知道果而不知道因,知其然而不知其所以然。要完全理解REST,就必须完全理解Fielding所确立的这一套研究软件架构的方法,这一套方法论才是这篇论文最大的贡献。

如果理解了前面4章的内容,这一章的内容其实非常容易理解,那就是根据上一章所识别出的Web需求,推导出一种完全适合Web需求的架构风格。

一般来说,推导出一种架构风格有两种方法:做加法和做减法。做加法就是先从一个最简单的架构风格开始,逐个添加识别出的架构约束;做减法就是先从一个非常复杂的架构风格开始,逐个去除不需要的架构约束。第一种方法强调创造性和无限的想象力,而第二种方法则强调限制和对系统环境的理解。这两种方法都可以得到希望的架构风格,区别是实践的难度。我们都知道,为一个简单的设计添加新的设计元素,通常都要比为一个复杂的设计减少设计元素更加容易。因此推导REST架构风格采用了做加法。

架构风格的推导从一个“空”风格开始。“空”风格就是没有任何约束的风格。这种推导方法我非常欣赏,体现出老子的“无中生有”、“一生二,二生三,三生万物”的思想。让我们清晰地看到了Roy Fielding和Tim Berners Lee等先贤的设计思路,看到了Web的架构如何在保持相对最简化的设计的同时完美地符合Web的需求。

为“空风格”添加的第一个架构约束就是客户-服务器。添加这个约束是为了分离关注点。因为Web交互所使用的分布式超媒体的存储地和使用地大多数时候都是不同的,它们的分工和角色有明显的不同,有必要将它们区分为客户端和服务器。服务器主要负责数据存储,而客户端主要负责提供用户界面。对于Web来说,最重要的是这种关注点的分离允许组件独立地进化,从而支持多个组织领域的Internet规模的需求。

添加的第二个架构约束是无状态。
状态通常可以分成两类,特定于单个客户端的会话状态、由多个客户端共享的资源状态。Fielding在这里所说的无状态指的仅仅是会话状态。服务器应该只负责保存资源状态,保存会话状态是客户端的责任。

“从客户到服务器的每个请求都必须包含理解该请求所必需的所有信息,不能利用任何存储在服务器上的上下文,会话状态因此要全部保存在客户端”

这个架构约束对于设计高可伸缩的服务器来说是非常重要的,我们几乎可以将此看作一个铁律。只要你希望服务器具有最大的可伸缩性,都应该将服务器设计为无状态的。对于Web服务器是这样,对于数据库服务器也是这样。无状态的服务器设计做负载均衡、做集群都非常容易,很容易通过水平添加新的服务器来支持更大的负载。

很多人不喜欢完全无状态的服务器设计,他们很喜欢通过应用服务器内建的session将大量的状态保存在服务器端,因为这使得编程更加容易。此外,对于无状态的服务器,由于每次请求都需要发送很多会话状态信息,降低了网络性能。

架构的设计其实是一种权衡,设计师要有能力在多种因素之中判断出哪些更为重要。对于面向Internet的Web应用来说,由于设计者无法控制服务器并发访问量,因此应该优先考虑能够使得服务器具有最大可伸缩性的设计。无状态的服务器设计对于保证服务器在高并发访问量时的可靠性是必须的。然而,对于面向局域网的B/S架构企业应用来说,服务器的并发访问量是能够预测并且加以控制的,因此完全无状态的服务器设计就不是必须的了。我们回顾一下Fielding在第一章中一再强调的,软件架构是软件在运行时的特性,设计软件架构之前一定要先考虑清楚应用具体的运行环境。面向Internet的Web应用和面向局域网的B/S架构企业应用是两种不同的运行环境,其需求有很大的不同,因此也需要不同的架构设计。很多人简单地将适合企业应用运行环境的架构拿来做Web应用,仅仅是因为它们都使用浏览器作为客户端,都使用HTTP协议。这样的Web应用在小并发访问量的情况下貌似运行地非常好,但是一旦遇到高并发访问,很快就会出现问题。

添加的第三个架构约束是缓存。
缓存通过将数据搬移到距离其使用地更近的位置,提高了网络效率和性能;同时减少了对于服务器不必要的访问,因此提高了服务器的可伸缩性。

添加这个架构约束也是为了满足Internet规模的高可伸缩性需求。HTTP作为Web架构基础协议内建有很多对于缓存的支持。Web缓存一般可以出现在三个地方:客户端、代理、服务器端。使用最多的是客户端缓存,所有主流的Web浏览器都内建有对于客户端缓存的支持。

添加的第四个架构约束是统一接口。
客户端与服务器的所有交互都应该使用统一接口来完成。统一接口使得客户端和服务器端的实现完全解耦,因此它们可以独立进化。只要接口不变,就不会影响到对方和整个Web应用的功能。

按照Fielding的描述,REST的统一接口由4个部分组成:资源的标识、通过表述对资源执行的操作、自描述的消息、以及作为应用状态引擎的超媒体

更加具体地说,在通常情况下,资源的标识就是资源的URI;资源的表述携带了资源的状态,可以对资源进行操作;自描述的消息由一些标准的HTTP方法和HTTP头信息字段组成;超媒体就是HTML/XHTML。

添加的第五个架构约束是分层系统。
不能简单地将REST风格的网络交互认为只包括客户端和服务器两个参与者,REST风格的网络交互事实上可以分成很多层,它们合在一起构成一个完整的通信链。从具体的架构层面上说,HTTP协议允许插入很多中间组件,客户端、服务器和这些中间组件合在一起构成整个HTTP通信链。交互的参与者都是相对独立的组件,组件之间的交互通过内建在组件内部的连接器来完成,所有连接器之间的交互都使用REST的统一接口。 客户端组件和服务器组件的角色比较单一,但是对于中间组件来说,它对于通信链中位于服务器一端的组件是客户端,而对于通信链中位于客户端一端的组件是服务器。所有的组件都只与自己直接相邻的组件进行交互,就好像其他不相邻的组件不存在一样。一个组件完全不需要知道整个交互的拓扑结构,除了在通信活跃期间,甚至不需要知道相邻组件的存在。

通信链中最常见的中间组件包括代理、网关、防火墙等等,每个组件还可以内建自己的缓存实现。缓存根据它所在的位置又可以分为客户端缓存、代理服务器缓存、服务器端缓存,代理服务器缓存和服务器缓存是由多个客户端共享的,客户端缓存则是属于单个客户端私有的。

“分层系统约束和统一接口约束相结合,导致了与统一管道和过滤器风格类似的架构属性。”如果大家对于操作系统中的管道概念很熟悉的话,理解REST的分层架构约束是比较容易的。

为REST添加的第六个,也是最后一个架构约束是按需代码。
按需代码说的是客户端可以从服务器下载可执行的代码,在客户端运行。下载JavaScript就是一个最典型的例子,另外还可以下载Java Applet、Flash、Silverlight等等可执行的代码。
在架构层面支持按需代码,简化了客户端的开发。允许在部署之后下载功能代码也改善了系统的可扩展性。然而,这也降低了可见性,因为这些可执行代码所代表的语义对于中间组件来说是不可见的,因此中间组件无法对其做有效的缓存和安全审计。所以Fielding将按需代码设计为只是REST的一个可选的架构约束。

读到这里,我们看到构成REST架构风格的架构约束其实非常少,5个必需的架构约束加上一个可选的架构约束,不多也不少,完美地满足了Web的需求。这很符合我所欣赏的设计哲学:好的设计并不是无法再添加新的部分,而是任何部分都无法再减少。得到满足系统需求的最简化设计应该是一个好的系统架构师所追求的目标。

识别出了满足Web需求的架构风格所应该具有的架构约束之后,Fielding接下来定义了REST风格的架构有哪些组成元素。一个REST架构的组成元素包括数据、连接器、组件三大类。

每一类架构元素都包括了多个成员,
数据元素包括资源、资源标识符、表述、表述元数据、资源元数据、控制数据
连接器元素包括客户端、服务器、缓存、解析器、隧道
组件元素包括来源服务器、网关、代理、用户代理

资源标识符即我们都非常熟悉的URI。
表述即HTTP消息体,它携带了资源的状态,通过表述来对资源执行各种操作。
元数据携带了关于表述或资源的描述。
控制数据用来对通信的某个方面进行控制,例如指示缓存连接器不要对这次响应进行缓存。

用户代理一般即客户端的浏览器,它和来源服务器在一起构成了通信链的两端,中间还可以插入代理和网关等多个中间组件。前面我们说过,组件之间的交互通过连接器完成。连接器根据它在通信链中所在的位置可以分为客户端连接器和服务器端连接器。此外,缓存也是一种最常见的连接器。解析器和隧道使用的比较少。

Fielding还定义了关于REST的三种视图。每种视图从不同的角度和观点来展示REST的设计原则。这三种视图是:
过程视图
连接器视图
数据视图

架构元素的定义描述定义了REST的组成部分,三种架构视图则描述了这些架构元素之间如何协作以形成一个架构。
在过程视图的描述中,Fielding以一个复杂的HTTP交互网络中存在的各种交互展示了REST的架构。

连接器视图集中于组件之间的通信机制。统一接口这个REST的主要的架构约束就是由连接器来实现的,连接器之间的交互必需通过统一的接口来完成。这个统一接口通常使用HTTP协议来实现,但是并不局限于HTTP协议。在REST的三大类架构元素中,连接器是直接与HTTP协议(或其他通信协议)打交道的部分。

数据视图的一个主要的侧重点是通信格式的定义:
“因为基于REST的架构主要通过转移资源的表述来进行通信,所以延迟会同时受到通信协议的设计和表述数据格式的设计两方面的影响。”
数据格式的定义会严重影响用户可觉察的性能,如果一种数据格式的定义允许增量地呈现,那么用户可觉察的性能要好的多。

同时,如果对数据进行有效地缓存,也能够非常有效地改善应用的性能。

对于通信协议的设计,Fielding举了一个微妙的“先响应后思考”的例子。尽管服务器不能确定客户端到底需要什么格式的内容,它还是在收到第一次请求之后就将最有可能的内容发给客户端。这样做要比先进行一轮内容协商再发送客户端需要格式的内容性能更好。

Fielding在对于数据视图的描述中写道:
“因此,REST的模型应用是一个引擎,它通过检查和选择当前的表述集合中的状态跃迁选项,从一个状态移动到下一个状态。毫不奇怪,这与一个超媒体浏览器的用户接口完全匹配。然而,REST风格并不假设所有应用都是浏览器。事实上,通用的连接器接口对服务器隐藏了应用的细节,因此各种形式的用户代理都是等价的,无论是为一个索引服务执行信息获取任务的自动化机器人,还是查找匹配特定查询标准的数据的私人代理,或者是忙于巡视破损的引用或被修改的内容的维护爬虫。”

这一段话对于理解REST架构的本质,REST为何称作“表述性状态转移”是非常重要的。

最后,Fielding回顾了对于Web架构的一些相关的研究工作,指出了这些研究工作中的局限,以及REST与它们的区别。
这一段中比较有趣的是解释了为何HTTP被设计为一种无状态的拉模型,而不是像EBI(基于事件的集成)风格的那种推模型。这是因为推模型需要服务器端保存每个客户端的状态信息,Web的规模使得我们不可能实现一种无节制的推模型。

到了这一章,REST架构风格的面貌已经完全确定,下面就是将REST付诸实践。Fielding和他的协议设计团队正是使用REST作为设计Web架构基础协议HTTP和URI的指导,REST正是HTTP和URI协议背后的设计意图,HTTP和URI正是用来实现REST这样一种风格的架构的。

抱歉!评论已关闭.