0 引言
目前,互联网在社会中扮演的角色越来越重要。通过互联网为广大群众提供服务,也是互联网成功的关键。互联网服务架构目前大多数都是基于REST架构来完成的。REST从它诞生至今,可以说为互联网的繁荣做出了不可磨灭的贡献。REST架构到底是一种什么样的架构,而它为何有这种魔力,这里我们就来刨根问底,挖掘它的内在以及潜在意义。
1 绪论
1.1 软件架构
软件架构是一种软件运行时的抽象。软件运行的健壮性、扩展性、效率,软件使用的简洁性通常用来评判一种软件好坏的标志,也是一个软件架构优劣的标志。
软件架构包含以下要素:组件、连接器和数据。
软件架构设计的目标是创建具有一组架构属性的架构,这些架构构成了系统需求的超集。
构建派生树为架构设计提供了一种指导机制。
1.2 基于网络的软件架构
基于网络的软件架构,顾名思义,它是网络条件下提供服务的软件的架构。
关键利益的架构属性包括:表现(网络性能、用户感知的性能、网络效率)、可扩展性、简单、可修改性(进化性、可扩展性、可定制性、可配置性、可重用性)、可见性、便携性、可靠性
1.3 基于网络的软件架构样式
软件架构样式的分类是基于这些样式引起的架构特性来决定的。
基于网络的软件架构样式包括:数据流样式、复制样式、分层样式、移动代码样式、点对点样式。
1.3.1 复制样式
基于复制样式的系统通过让多个进程提供相同的服务来提高数据的可访问性和服务的可伸缩性。这些分散的服务器相互作用,为客户提供只有一个服务的错觉。
示例: 分布式文件系统和远程版本控制系统。
优势: 改善用户感知性能、简单性保持中立(因为复制的复杂性被允许网络无意识组件在本地复制数据上的透明操作)。
缓存: 缓存可以说是复制存储的变体
1.3.2 分层样式
客户端 - 服务器样式是基于网络的应用程序的架构样式中最常遇到的。提供一组服务的服务器组件侦听对这些服务的请求。希望执行服务的客户端组件通过连接器向服务器发送请求。服务器拒绝或执行请求并将响应发送回客户端。
客户端 - 服务器组件如下:客户端是一个触发过程; 服务器是一个被动的过程。客户端发出触发服务器响应的请求。因此,客户在其选择时启动活动; 它通常会延迟,直到其请求得到服务。另一方面,服务器等待发出请求然后对它们作出反应。服务器通常是非终止进程,通常为多个客户端提供服务。
客户端-服务器约束背后的主要原则的关注点分离。正确分离功能应简化服务器组件,以提高可伸缩性。这种简化通常采用将所有用户界面功能移动到客户端组件中的形式。分离还允许两种类型的组件独立发展,前提是接口不会改变。
客户端 - 服务器的基本形式不限制应用程序状态在客户端和服务器组件之间的分区方式。它通常由用于连接器实现的机制引用,例如远程过程调用或面向消息的中间件。
分层系统按层次结构组织,每个层为其上方的层提供服务,并使用其下层的服务。虽然分层系统被认为是“纯粹”的风格,但它在基于网络的系统中的使用仅限于它与客户端 - 服务器风格的结合,以提供分层客户端服务器。
分层系统通过将内层隐藏在除相邻外层之外的所有层来减少跨多层的耦合,从而改善了可演化性和可重用性。示例包括分层通信协议的处理,例如TCP / IP和OSI协议栈,以及硬件接口库。分层系统的主要缺点是它们增加了数据处理的开销和延迟,降低了用户感知的性能。
分层客户端服务器将代理和网关组件添加到客户端 - 服务器样式。代理充当一个或多个客户端组件的共享服务器,接收请求并将它们以可能的转换转发到服务器组件。网关组件似乎是请求其服务的客户端或代理的普通服务器,但实际上是将这些请求(可能的转换)转发到其“内层”服务器。可以在多个层中添加这些其他介体组件,以向系统添加负载平衡和安全检查等功能。
基于分层客户端服务器的体系结构在信息系统文献中被称为双层,三层或多层体系结构。
示例: 远程会话、远程数据库访问。
1.3.3 移动代码样式
移动代码样式使用移动性来动态地改变处理和数据源或结果目的地之间的距离。作为活动配置的一部分,在体系结构级别引入了站点抽象,以便考虑不同组件的位置。引入位置概念使得在设计级别对组件之间的交互成本进行建模成为可能。特别地,与涉及通过网络的通信的交互相比,共享相同位置的组件之间的交互被认为具有可忽略的成本。通过改变其位置,组件可以改善其交互的接近度和质量,降低交互成本,从而提高效率和用户感知的性能。
在所有移动代码样式中,数据元素被动态转换为组件。
所有移动代码样式的基础是虚拟机或解释器风格的概念。代码必须以某种方式执行,最好是在受控环境中,以满足安全性和可靠性问题,这正是虚拟机风格所提供的。它本身不是基于网络的样式,但当与客户端 - 服务器样式(REV和COD样式)中的组件结合使用时,它通常会被用作这种样式。
示例:
- 远程评估:客户端组件具有执行服务所需的专有技术,但缺少所需的资源(CPU周期,数据源等),恰好位于远程站点。因此,客户端将技术诀窍发送到远程站点的服务器组件,该服务器组件又使用那里可用的资源来执行代码。然后将执行结果发送回客户端。远程评估样式假定所提供的代码将在受保护的环境中执行,这样除了正在使用的资源之外,它不会影响同一服务器的其他客户端。
- 按需代码:在按需代码样式中,客户端组件可以访问一组资源,但不能访问如何处理它们的专有技术。它向远程服务器发送请求,以获取代表该技术诀窍的代码,接收该代码并在本地执行该代码。
- 移动代理:在移动代理风格中,整个计算组件与其状态,所需代码以及执行任务所需的一些数据一起被移动到远程站点。这可以被认为是远程评估和按需编码样式的推导,因为移动性可以双向工作。
2 设计Web架构:问题和见解
2.1 万维网应用程序要求
伯纳斯 - 李写道,“网络的主要目标是成为人和机器可以通信的共享信息空间。” 所需要的是人们存储和构建他们自己的信息的方式,无论是永久性的还是短暂的,以便它本身和其他人可以使用,并且能够参考和构建他人存储的信息,以便它没有必要让每个人保持和维护本地副本。
该系统的预期最终用户位于世界各地,通过互联网连接的各种大学和政府高能物理研究实验室。他们的机器是终端,工作站,服务器和超级计算机的异构集合,需要大量的操作系统软件和文件格式。信息范围从个人研究笔记到组织电话列表。面临的挑战是建立一个系统,为这种结构化信息提供通用的一致界面,尽可能在尽可能多的平台上提供,并在新人和组织加入项目时逐步部署。
2.1.1 低入口障碍
由于参与信息的创建和构建是自愿的,因此需要较低的准入门槛以实现充分的采用。这适用于Web体系结构的所有用户:读者,作者和应用程序开发人员。
超媒体被选为用户界面,因为它简单和通用:无论信息源如何,都可以使用相同的界面,超媒体关系(链接)的灵活性允许无限制的结构,直接操纵链接允许内部的复杂关系指导读者完成申请的信息。由于大型数据库中的信息通常更容易通过搜索界面而不是浏览来访问,因此Web还通过向服务提供用户输入的数据并将结果呈现为超媒体来结合执行简单查询的能力。
对于作者来说,主要要求是整个系统的部分可用性不得妨碍内容的创作。超文本创作语言必须简单并且能够使用现有编辑工具创建。作者需要保留这种格式的个人研究笔记,无论是否直接连接到互联网,因此暂时或永久地提供某些参考信息的事实不能阻止阅读和创作。可用的信息。出于类似的原因,必须能够在该参考的目标可用之前创建对信息的引用。由于鼓励作者在信息源的开发方面进行合作,因此参考文献需要易于沟通。
简单性也是应用程序开发人员的目标。由于所有协议都被定义为文本,因此可以使用现有网络工具查看和交互测试通信。尽管缺乏标准,这使得早期采用协议成为可能。
2.1.2 可扩展性
虽然简单性可以部署分布式系统的初始实现,但可扩展性使我们能够避免因部署的限制而永远陷入困境。即使可以构建完全符合其用户要求的软件系统,这些要求也会随着社会随着时间的推移而变化。一个打算像Web一样长寿的系统必须为变革做好准备。
2.1.3 分布式超媒体
超媒体的定义是信息呈现中嵌入的应用程序控制信息的存在或上面的层。分布式超媒体允许将演示和控制信息存储在远程位置。就其本质而言,分布式超媒体系统内的用户动作需要将大量数据从存储数据的位置传输到使用它的位置。因此,Web架构必须设计用于大粒度数据传输。
超媒体交互的可用性对用户感知的延迟高度敏感:选择链接和呈现可用结果之间的时间。由于Web的信息源分布在全球Internet上,因此架构需要最小化网络交互(数据传输协议内的往返)。
2.1.4 互联网规模
Web旨在成为一个互联网规模的分布式超媒体系统,这意味着不仅仅是地理分散。互联网是指跨多个组织边界互连信息网络。信息服务的供应商必须能够应对无政府可扩展性和软件组件的独立部署的需求。
2.1.4.1 无状态可扩展性
大多数软件系统是在隐含的假设下创建的,即整个系统处于一个实体的控制之下,或者至少参与系统内的所有实体都在朝着共同目标而不是交叉目的行事。当系统在因特网上公开运行时,不能安全地做出这样的假设。无状态可扩展性是指架构元素在遭受意外负载时或在给出格式错误或恶意构造的数据时需要继续运行,因为它们可能与组织控制之外的元素进行通信。该体系结构必须适合增强可见性和可伸缩性的机制。
无状态性可扩展性要求适用于所有架构元素。不能期望客户端保持所有服务器的状态。不能期望服务器跨请求保留状态知识。超媒体数据元素不能保留“反向指针”,即引用它们的每个数据元素的标识符,因为对资源的引用数量与对该信息感兴趣的人数成比例。特别值得注意的信息也可能导致“闪电人群”:随着其可用性的消息传播到全世界,访问尝试突然激增。
建筑元素的安全性以及它们运行的平台也成为一个重要的问题。多个组织边界意味着在任何通信中都可能存在多个信任边界。中间件应用程序(例如防火墙)应该能够检查应用程序交互并防止对组织安全策略之外的人员采取行动。应用程序交互的参与者应该假定所接收的任何信息都是不可信的,或者在获得信任之前需要一些额外的身份验证。这要求体系结构能够传递身份验证数据和授权控制。
2.1.4.2 独立部署
多个组织边界还意味着系统必须为逐步和零散的变更做好准备,新旧实现共存,而不会阻止新实现利用其扩展功能。需要设计现有的架构元素,期望将添加以后的架构特征。同样,需要轻松识别较旧的实现,以便可以封装遗留行为,而不会对新的体系结构元素产生负面影响。整个体系结构必须设计为以部分,迭代的方式简化体系结构元素的部署,因为不可能以有序的方式强制部署。
2.2 问题
在1993年末,很明显,不仅仅是研究人员会对网络感兴趣。网络首先发生在小型研究小组中,然后传播到校园宿舍,俱乐部和个人主页,然后传播到机构部门获取校园信息。当个人开始发布他们的个人信息集时,无论他们可能感到什么狂热的话题,社交网络效应推动了今天持续的网站的指数增长。对网络的商业兴趣才刚刚开始,但很明显,在国际范围内发布的能力对企业来说是不可抗拒的。
尽管互联网开发者社区的成功令人高兴,但他们开始担心网络使用的快速增长以及早期HTTP的一些不良网络特征将迅速超过互联网基础设施的容量并导致全面崩溃。由于Web上应用程序交互的性质不断变化,这种情况更加恶化。尽管初始协议是针对单个请求 - 响应对而设计的,但新站点使用越来越多的内嵌图像作为网页内容的一部分,从而产生用于浏览的不同交互配置文件。部署的体系结构在支持可扩展性,共享缓存和中介体方面存在很大的局限性,这使得难以针对不断增长的问题开发临时解决方案。与此同时,
Internet工程任务组内的工作组成立于Web的三个主要标准:URI,HTTP和HTML。这些组的章程是定义现有架构通信的子集,这些子集在早期Web架构中通常一致地实现,识别该架构中的问题,然后指定一组标准来解决这些问题。这给我们带来了一个挑战:我们如何为已经广泛部署的体系结构引入一组新功能,以及我们如何确保其引入不会对启用Web的体系结构属性产生负面影响甚至破坏成功?
2.3 方法
早期的Web架构基于可靠的原则 - 关注点分离,简单性和通用性 - 但缺乏架构描述和基本原理。该设计是基于一套非正式的超文本笔,对用户社区和Web开发者社区邮件列表存档的讨论(www-talk@info.cern.ch) 。然而,实际上,早期Web架构的唯一真实描述是在libwww(客户端和服务器的CERN协议库),Mosaic(NCSA浏览器客户端)以及与它们互操作的各种其他实现的实现中找到的。 。
架构风格可用于定义Web架构背后的原则,以便未来的架构师可以看到它们。如第1章所述,样式是体系结构元素上的一组命名约束,它引入了体系结构所需的属性集。因此,我的方法的第一步是确定早期Web架构中对其所需属性负责的约束。
假设I:WWW体系结构背后的设计原理可以通过一种体系结构样式来描述,该体系结构样式由应用于Web体系结构中的元素的约束集组成。
可以将附加约束应用于体系结构样式,以便扩展在实例化体系结构上引发的属性集。我的方法的下一步是确定互联网规模的分布式超媒体系统中所需的属性,选择诱导这些属性的其他架构风格,并将它们与早期的Web约束相结合,以形成现代Web的新的混合架构风格。建筑。
假设II:可以将约束添加到WWW体系结构样式中,以获得更好地反映现代Web体系结构所需属性的新混合样式。
使用新的体系结构样式作为指导,我们可以将建议的扩展和对Web体系结构的修改与样式中的约束进行比较。冲突表明该提案违反了Web背后的一个或多个设计原则。在某些情况下,可以通过在使用新功能时要求使用特定指示符来消除冲突,这通常对影响响应的默认可缓存性的HTTP扩展进行。对于严重的冲突,例如交互风格的改变,相同的功能要么被更有利于Web风格的设计所取代,要么被告知提议者将功能实现为与Web并行运行的独立体系结构。 。
假设III:可以将修改Web体系结构的提议与更新的WWW体系结构样式进行比较,并在部署之前分析冲突。
最后,通过参与构成大多数Web应用程序的基础架构和中间件软件的开发,部署了根据新架构风格指南编写的修订协议标准所定义的更新Web架构。这包括我直接参与Apache HTTP服务器项目和libwww-perl客户端库的软件开发,以及通过为W3C libwww和jigsaw项目,Netscape Navigator,Lynx和MSIE的开发人员提供建议,间接参与其他项目。浏览器和许多其他实现,作为IETF话语的一部分。
虽然我已将此方法描述为单个序列,但实际上它是以非顺序,迭代的方式应用的。也就是说,在过去六年中,我一直在构建模型,为架构风格添加约束,并通过客户端和服务器软件的实验扩展来测试它们对Web协议标准的影响。同样,其他人建议在架构中添加超出当前模型样式范围的功能,但不与其冲突,这导致返回并修改架构约束以更好地反映改进的架构。我们的目标始终是保持一致且正确的模型,即我打算如何构建Web体系结构,以便它可以用于指导定义适当行为的协议标准。
3 REST架构风格
REST是一种混合风格,它源自第1章中描述的几种基于网络的体系结构样式,并结合了定义统一连接器接口的其他约束。第1章的软件体系结构框架用于定义REST的体系结构元素,并检查原型体系结构的样本过程,连接器和数据视图。
3.1 派生REST
Web体系结构背后的设计原理可以通过体系结构样式来描述,该体系结构样式由应用于体系结构中的元素的约束集组成。通过检查每个约束在添加到演化样式时的影响,我们可以识别由Web约束引起的属性.然后可以应用其他约束来形成新的体系结构样式,以更好地反映现代Web体系结构的所需属性。本节通过介绍将其作为架构风格派生的过程,提供REST的一般概述。后面的部分将更详细地描述组成REST样式的特定约束。
3.1.1 从空样式开始
建筑设计过程有两个共同的观点,无论是建筑物还是软件。第一个是设计师从零开始 - 空白的板岩,白板或绘图板 - 并从熟悉的组件构建架构,直到满足预期系统的需求。第二个是设计师从系统需求开始,没有约束,然后逐步识别并应用系统元素的约束,以区分设计空间,并允许影响系统行为的力自然流动,与系统和谐相处。第一个强调创造力和无限视觉,第二个强调对系统环境的约束和理解。使用后一种方法开发了REST。
空样式只是一组空的约束。从体系结构的角度看,空样式描述了一个系统其中组件之间没有明显的边界这是我们描述REST的起点。
3.1.2 客户端-服务器
添加到混合样式的第一个约束是客户端 - 服务器体系结构样式。关注点分离是客户端 - 服务器约束背后的原则。通过将用户界面问题与数据存储问题分开,我们提高了跨多个平台的用户界面的可移植性,并通过简化服务器组件来提高可伸缩性。然而,对Web而言最重要的是,分离允许组件独立发展,从而支持多个组织域的Internet规模需求。
3.1.3 无状态
接下来我们为客户端 - 服务器交互添加一个约束:通信本质上必须是无状态的。这样每个请求从客户端到服务器必须包含理解请求所需的所有信息,并且不能利用服务器上任何存储的上下文。因此,会话状态完全保留在客户端上。此约束会引发可见性,可靠性和可伸缩性。
可见性得到改善,因为监控系统不必超出单个请求数据,以确定请求的完整性。可靠性得到改善,因为它简化了从部分故障中恢复的任务。扩展性得到改善,因为不必在请求之间存储状态允许服务器组件快速释放资源,并进一步简化实现,因为服务器不必跨请求管理资源使用。
与大多数架构选择一样,无状态约束反映了设计权衡。缺点是它可能通过增加在一系列请求中发送的重复数据(每个交互开销)来降低网络性能,因为该数据不能在共享上下文中留在服务器上。此外,将应用程序状态放在客户端会降低服务器对一致应用程序行为的控制,因为应用程序依赖于跨多个客户端版本的语义的正确实现。
3.1.4 缓存
为了提高网络效率,我们添加了缓存约束以形成客户端缓存无状态服务器样式。高速缓存约束要求将对请求的响应中的数据隐式或显式标记为可高速缓存或不可高速缓存。如果响应是可缓存的,则客户端缓存有权重用该响应数据以用于以后的等效请求。
添加缓存约束的优势在于,它们有可能通过减少一系列交互的平均延迟来部分或完全消除某些交互,从而提高效率,可伸缩性和用户感知性能。然而,权衡的是,如果高速缓存中的陈旧数据与请求被直接发送到服务器时获得的数据显著不同,则高速缓存会降低可靠性。
早期的Web架构是由客户端缓存无状态服务器约束集定义的。也就是说,1994年之前为Web架构提供的设计原理侧重于通过Internet交换静态文档的无状态客户端 - 服务器交互。用于通信交互的协议对非共享缓存具有基本支持,但并未将接口限制为所有资源的一致语义集。相反,Web依赖于使用通用客户端 - 服务器实现库(CERN libwww)来维护Web应用程序之间的一致性。
目前,Web开发已经超出了早期的设计。除静态文档外,请求还可以识别动态生成响应的服务,例如图像映射[Kevin Hughes]和服务器端脚本[Rob McCool]。代理和共享缓存 形式的中间组件也已开始工作,但需要对协议进行扩展才能使它们可靠地进行通信。以下部分描述了添加到Web体系结构样式的约束,以指导构成现代Web体系结构的扩展。
3.1.5 统一接口
将REST架构样式与其他基于网络的样式区分开来的核心功能是强调组件之间的统一接口。通过将通用性的软件工程原理应用于组件接口,简化了整个系统架构,提高了交互的可见性。实现与它们提供的服务分离,这鼓励了独立的可进化性。然而,权衡的是,统一的接口会降低效率,因为信息是以标准化的形式传输的,而不是特定于应用程序需求的形式。REST接口旨在高效地进行大粒度超媒体数据传输,并针对Web的常见情况进行优化,然而,它又会带来界面不适合其他形式的架构交互的负面效应。
为了获得统一的接口,需要多个架构约束来指导组件的行为。REST由四个接口约束定义:资源识别、 通过陈述来处理资源、 自我描述性的信息和超媒体作为应用程序状态的引擎。
3.1.6 分层系统
为了进一步改善Internet规模要求的行为,我们添加了分层系统约束。分层系统样式允许通过约束组件行为来使架构由分层组成,使得每个组件不能“看到”超出与它们交互的直接层。通过将系统知识限制在单一层,我们限制了整个系统的复杂性并促进了基板的独立性。层可用于封装旧服务并保护旧服务的新服务,通过将不常用的功能移动到共享中介来简化组件。通过在多个网络和处理器之间实现服务的负载平衡,中介还可用于提高系统可扩展性。
分层系统的主要缺点是它们增加了数据处理的开销和延迟,降低了用户感知的性能。对于支持缓存约束的基于网络的系统,这可以通过中间人的共享缓存的好处来抵消。将共享缓存放置在组织域的边界可以带来显着的性能优势。这些层还允许对跨越组织边界的数据实施安全策略,这是防火墙所要求的。
分层系统和均匀界面约束的组合引起类似于均匀管道和过滤器类型的建筑特性。虽然REST交互是双向的,但是超媒体交互的大粒度数据流可以像数据流网络一样被处理,其中过滤器组件选择性地应用于数据流,以便在内容通过时转换内容。在REST中,中间组件可以主动转换消息内容,因为消息是自描述的,并且它们的语义对于中介是可见的。
3.1.7 按需代码
REST的约束集的最终添加来自第1.3.3节的移动代码样式。REST允许通过以applet或脚本的形式下载和执行代码来扩展客户端功能。这通过减少预先实现所需的功能数量来简化客户端。允许在部署后下载功能可以提高系统的可扩展性。但是,它还会降低可见性,因此只是REST中的可选约束。
可选约束的概念可能看起来像矛盾。但是,它在包含多个组织边界的系统的体系结构设计中确实有用。这意味着当已知对于整个系统的某些领域有效时,该体系结构仅获得可选约束的益处(并且具有缺点)。例如,如果已知组织内的所有客户端软件都支持Java applet,然后,可以构建该组织内的服务,以便通过可下载的Java类获得增强功能的好处。但是,同时,组织的防火墙可能会阻止Java小程序从外部源传输,因此对于Web的其余部分,它看起来好像这些客户端不支持按需代码。可选约束允许我们设计一种在一般情况下支持所需行为的体系结构,但要了解它可能在某些上下文中被禁用。
3.2 REST架构元素
Representational State Transfer(REST)样式是分布式超媒体系统中的架构元素的抽象。REST忽略了组件实现和协议语法的细节,以便专注于组件的角色,与其他组件交互的约束以及对重要数据元素的解释。它包含对组件,连接器和数据的基本约束,这些约束定义了Web体系结构的基础,因此也就是其作为基于网络的应用程序的行为的本质。
3.2.1 数据元素
数据元素 | 现代web示例 |
资源 | 超文本引用的预期概念目标 |
资源标识符 | URL,URN |
表示 | HTML文档,JPEG图像 |
表示元数据 | 媒体类型,最后修改时间 |
资源元数据 | 源链接,交替,变化 |
控制数据 | if-modified-since,cache-control |
REST通过关注数据类型与元数据的共享理解,提供了所有三个选项的混合,但限制了标准化界面的显示范围。REST组件通过以匹配一组演进的标准数据类型之一的格式传送资源的表示来进行通信,所述标准数据类型基于接收者的能力或期望以及资源的性质而动态地选择。表示是否与原始源具有相同的格式,或者是否源自源,仍然隐藏在接口后面。通过发送由封装渲染引擎的标准数据格式中的指令组成的表示来近似移动对象样式的好处。因此,REST可以在没有服务器可伸缩性问题的情况下实现客户端 - 服务器风格的关注分离,允许通过通用接口隐藏信息以实现服务的封装和演进,并通过可下载的功能引擎提供各种功能。
3.2.1.1 资源和资源标识符
REST中信息的关键抽象是一种资源。可以命名的任何信息都可以是资源:文档或图像,临时服务(例如“洛杉矶的今天天气”),其他资源的集合,非虚拟对象(例如人)等等。换句话说,任何可能是作者超文本引用目标的概念都必须符合资源的定义。资源是到一组实体的概念映射,而不是与任何特定时间点的映射相对应的实体。
更确切地说,资源R是时间上变化的隶属函数M R(t),其对于时间t映射到一组实体或等价的值。集合中的值可以是资源表示和/或资源标识符。资源可以映射到空集,允许在任何实现该概念之前对概念进行引用 - 这一概念对于Web之前的大多数超文本系统来说是陌生的[ 61]]。某些资源在某种意义上是静态的,当它们在创建后的任何时间进行检查时,它们总是对应于相同的值集。其他人的价值随时间变化很大。对于资源而言,唯一需要静态的是映射的语义,因为语义是区分一个资源与另一个资源的区别。
例如,学术论文的“作者'首选版本”是其值随时间变化的映射,而映射到“在会议X的会议论文中发表的论文”是静态的。这是两个不同的资源,即使它们在某个时间点都映射到相同的值。区分是必要的,以便可以独立地识别和引用这两种资源。软件工程中的类似示例是在引用“最新版本”,“版本号1.2.7”或“橙色版本附带的修订版”时单独标识版本控制的源代码文件。
这种资源的抽象定义实现了Web体系结构的关键功能。首先,它通过包含许多信息来源提供了一般性,而没有按类型或实施人为地区分它们。其次,它允许将引用后期绑定到表示,从而允许基于请求的特征进行内容协商。最后,它允许作者引用该概念而不是该概念的某些单一表示,从而无需在表示发生变化时更改所有现有链接(假设作者使用了正确的标识符)。
REST使用资源标识符来标识组件之间交互中涉及的特定资源。无论如何定义成员函数或处理请求的软件类型,REST连接器都提供用于访问和操作资源值集的通用接口。分配资源标识符使得可以引用资源的命名机构负责维持映射随时间的语义有效性(即,确保成员函数不改变)。
传统的超文本系统通常在封闭或本地环境中运行,使用每次信息更改时都会发生变化的唯一节点或文档标识符,依靠链接服务器来维护与内容分开的引用。由于集中式链接服务器是对Web的巨大规模和多组织域要求的诅咒,因此REST依赖于作者选择最符合所识别概念性质的资源标识符。当然,标识符的质量通常与保留其有效性所花费的金额成比例,这导致链接断开,因为短暂(或支持不良)的信息随着时间的推移而移动或消失。
3.2.1.2 表示
REST组件通过使用表示来捕获资源的当前或预期状态并在组件之间传输该表示,从而对资源执行操作。表示是字节序列,加上用于描述这些字节的表示元数据。其他常用但不太精确的表示名称包括:文档,文件和HTTP消息实体,实例或变体。
表示由数据,描述数据的元数据以及有时描述元数据的元数据(通常用于验证消息完整性)组成。元数据采用名称 - 值对的形式,其中名称对应于定义值的结构和语义的标准。响应消息可以包括表示元数据和资源元数据:关于不是特定于所提供的表示的资源的信息。
控制数据定义组件之间消息的目的,例如所请求的操作或响应的含义。它还用于参数化请求并覆盖某些连接元素的默认行为。例如,可以通过请求或响应消息中包括的控制数据来修改高速缓存行为。
根据消息控制数据,给定表示可以指示所请求资源的当前状态,所请求资源的期望状态,或某些其他资源的值,例如客户端查询形式内的输入数据的表示,或者表示响应的某些错误条件。例如,资源的远程创作要求作者向服务器发送表示,从而为该资源建立可由后来的请求检索的值。如果给定时间的资源的值集由多个表示组成,则可以使用内容协商来选择包含在给定消息中的最佳表示。
表示的数据格式称为媒体类型。表示可以包括在消息中并由接收者根据消息的控制数据和媒体类型的性质进行处理。一些媒体类型旨在用于自动处理,一些媒体类型旨在被呈现以供用户查看,并且一些媒体类型能够用于两者。复合媒体类型可用于在单个消息中包含多个表示。
媒体类型的设计可以直接影响分布式超媒体系统的用户感知性能。在收件人开始呈现表示之前必须接收的任何数据都会增加交互的延迟。一种数据格式,可以预先放置最重要的渲染信息,这样可以在接收其余信息时逐步渲染初始信息,从而产生比以前必须完全接收的数据格式更好的用户感知性能渲染可以开始。
例如,即使网络性能相同,可以在接收大量HTML文档时逐步呈现大型HTML文档的Web浏览器提供的用户感知性能明显优于在呈现之前等待整个文档完全接收的文档。请注意,表示的呈现能力也可能受到内容选择的影响。如果必须在渲染动态大小的表和嵌入对象之前确定它们的尺寸,则它们在超媒体页面的查看区域内的出现将增加其延迟。
3.2.2 连接器
连接器 | 现代web示例 |
客户 | libwww,libwww-perl |
服务器 | libwww,Apache API,NSAPI |
高速缓存 | 浏览器缓存,Akamai缓存网络 |
分解器 | 绑定(DNS查找库) |
隧道 | HTTP CONNECT后的SOCKS,SSL |
REST使用上表中汇总的各种连接器类型来封装访问资源和传输资源表示的活动。连接器为组件通信提供了一个抽象接口,通过提供关注点的清晰分离和隐藏资源和通信机制的底层实现来增强简单性。接口的通用性还具有可替代性:如果用户只能通过抽象接口访问系统,则可以在不影响用户的情况下替换实现。由于连接器管理组件的网络通信,因此可以跨多个交互共享信息,以提高效率和响应性。
所有REST交互都是无状态的。也就是说,每个请求都包含连接器理解请求所需的所有信息,与之前可能存在的任何请求无关。此限制实现了四个功能:1)它消除了连接器在请求之间保留应用程序状态的任何需要,从而减少了物理资源的消耗并提高了可伸缩性; 2)它允许并行处理交互,而不需要处理机制理解交互语义; 3)它允许中介单独查看和理解请求,这在动态重新安排服务时可能是必要的; 4)它强制所有可能影响缓存响应的可重用性的信息出现在每个请求中。
连接器接口类似于过程调用,但在参数和结果的传递方面存在重要差异。参数包括请求控制数据,指示请求目标的资源标识符和可选表示。out参数包括响应控制数据,可选资源元数据和可选表示。从抽象的角度来看,调用是同步的,但in-out和out-parameters都可以作为数据流传递。换句话说,可以在完全知道参数的值之前调用处理,从而避免批量处理大数据传输的等待时间。
主连接器类型是客户端和服务器。两者之间的本质区别在于客户端通过发出请求来启动通信,而服务器侦听连接并响应请求以提供对其服务的访问。组件可以包括客户端和服务器连接器。
第三种连接器类型(缓存连接器)可以位于客户端或服务器连接器的接口上,以便保存对当前交互的可缓存响应,以便可以将它们重用于以后请求的交互。客户端可以使用高速缓存来避免重复网络通信,或者服务器可以使用高速缓存来避免重复生成响应的过程,两种情况都用于减少交互等待时间。缓存通常在使用它的连接器的地址空间内实现。
某些缓存连接器是共享的,这意味着其缓存的响应可用于回答除最初获得响应的客户端之外的客户端。共享缓存可以有效地减少“闪存拥挤”对流行服务器负载的影响,特别是当分层安排缓存以覆盖大型用户组时,例如公司内部网中的用户,Internet服务的客户提供者或共享国家网络骨干的大学。但是,如果缓存的响应与新请求所获得的响应不匹配,则共享缓存也会导致错误。REST尝试平衡缓存行为透明度的愿望与有效使用网络的愿望,而不是假设始终需要绝对透明度。
缓存能够确定响应的可缓存性,因为该接口是通用的,而不是特定于每个资源。默认情况下,对检索请求的响应是可缓存的,对其他请求的响应是不可缓存的。如果某种形式的用户身份验证是请求的一部分,或者响应指示不应该共享,则响应只能由非共享缓存缓存。组件可以通过包含控制数据来覆盖这些默认值,该控制数据仅在有限时间内将交互标记为可缓存,不可缓存或可缓存。
解析器将部分或完整资源标识符转换为建立组件间连接所需的网络地址信息。例如,大多数URI包括DNS主机名作为用于标识资源的命名权限的机制。为了发起请求,Web浏览器将从URI中提取主机名,并使用DNS解析器获取该权限的Internet协议地址。另一个示例是一些识别方案(例如,URN需要中间设备将永久标识符转换为更临时的地址以便访问所识别的资源。使用一个或多个中间解析器可以通过间接提高资源引用的寿命,但这样做会增加请求延迟。
连接器类型的最终形式是隧道,它简单地中继通过连接边界的通信,例如防火墙或低级网络网关。将其建模为REST的一部分并且不作为网络基础结构的一部分进行抽象的唯一原因是某些REST组件可以动态地从活动组件行为切换到隧道的行为。主要示例是HTTP代理,它响应CONNECT方法请求切换到隧道,从而允许其客户端使用不允许代理的不同协议(如TLS)直接与远程服务器通信。当两端终止通信时,隧道消失。
3.2.3 组件
组件 | 现代web示例 |
原始服务器 | Apache httpd,Microsoft IIS |
网关 | Squid,CGI,反向代理 |
代理 | CERN代理,Netscape代理,Gauntlet |
用户代理 | Netscape Navigator,Lynx,MOMspider |
用户代理使用客户端连接器来发起请求,并成为响应的最终接收者。最常见的示例是Web浏览器,它提供对信息服务的访问并根据应用程序需求呈现服务响应。
源服务器使用服务器连接器来管理所请求资源的命名空间。它是表示其资源的权威来源,并且必须是任何打算修改其资源价值的请求的最终接收者。每个源服务器都将其服务的通用接口作为资源层次结构提供。资源实现细节隐藏在界面后面。
中介组件既作为客户又作为服务器,以便转发可能的翻译,请求和响应。代理组件是客户端选择的中介,用于提供其他服务的接口封装,数据转换,性能增强或安全保护。网关(也称为反向代理)组件是由网络或源服务器强加的中介,以提供其他服务的接口封装,用于数据转换,性能增强或安全实施。请注意,代理和网关之间的区别在于客户端确定何时使用代理。
3.3 REST架构视图
现在我们已经单独了解了REST架构元素,我们可以使用架构视图来描述元素如何协同工作以形成架构。三种类型的视图 - 进程,连接器和数据 - 对于阐明REST的设计原则非常有用。
3.3.1 流程视图
REST的客户端 - 服务器关注点分离简化了组件实现,降低了连接器语义的复杂性,提高了性能调优的效率,并提高了纯服务器组件的可伸缩性。分层系统约束允许在通信中的各个点处引入中介 - 代理,网关和防火墙 - 而不改变组件之间的接口,从而允许它们通过大规模共享缓存来协助通信转换或提高性能。REST通过将消息约束为自描述来实现中间处理:交互在请求之间是无状态的,标准方法和媒体类型用于指示语义和交换信息,并且响应明确指示可缓存性。
由于组件是动态连接的,因此它们对特定应用程序操作的排列和功能具有类似于管道和过滤器样式的特征。虽然REST组件通过双向流进行通信,但每个方向的处理是独立的,因此易受流传感器(过滤器)的影响。通用连接器接口允许根据每个请求或响应的属性将组件放置在流上。
可以使用中间体和多个分布式源服务器的复杂层次结构来实现服务。REST的无状态特性允许每个交互独立于其他交互,无需了解整个组件拓扑,对于Internet规模的体系结构来说是不可能完成的任务,并且允许组件充当目的地或中介,动态确定按每个请求的目标。连接器只需要在通信范围内了解彼此的存在,但出于性能原因,它们可能会缓存其他组件的存在和功能。
3.3.2 连接器视图
架构的连接器视图集中于组件之间的通信机制。对于基于REST的体系结构,我们对定义通用资源接口的约束特别感兴趣。
客户端连接器检查资源标识符,以便为每个请求选择适当的通信机制。例如,客户端可以被配置为当标识符指示它是本地资源时连接到特定代理组件,可能一个充当注释过滤器。同样,客户端可以配置为拒绝对某些标识符子集的请求。
REST不限制与特定协议的通信,但它确实限制了组件之间的接口,因此限制了组件之间可能产生的交互和实现假设的范围。例如,Web的主要传输协议是HTTP,但该体系结构还包括对源自预先存在的网络服务器的资源的无缝访问,包括FTP ,Gopher 和WAIS 。与这些服务的交互仅限于REST连接器的语义。这种约束牺牲了其他体系结构的一些优点,例如像WAIS这样的相关性反馈协议的状态交互,以便保留连接器语义的单个通用接口的优点。作为回报,通用接口使得通过单个代理访问多种服务成为可能。如果应用程序需要另一个体系结构的附加功能,它可以作为一个并行运行的独立系统实现和调用这些功能,类似于Web体系结构与“telnet”和“mailto”资源的接口。
3.3.3 数据视图
体系结构的数据视图揭示了信息流经组件时的应用程序状态。由于REST专门针对分布式信息系统,因此它将应用程序视为信息和控制备选方案的内聚结构,用户可通过该结构执行所需任务。例如,在在线词典中查找单词是一个应用程序,如在虚拟博物馆中巡视,或查看一组课堂笔记以进行考试。每个应用程序都定义了底层系统的目标,可以衡量系统的性能。
组件交互以动态大小的消息的形式发生。小型或中型消息用于控制语义,但大部分应用程序工作是通过包含完整资源表示的大粒度消息完成的。最常见的请求语义形式是检索资源的表示(例如,HTTP中的“GET”方法),其通常可以被缓存以供以后重用。
REST将所有控制状态集中到响应交互所接收的表示中。目标是通过消除服务器的任何需要来提高服务器可扩展性,以保持对当前请求之外的客户端状态的了解。因此,应用程序的状态由其挂起的请求,连接组件的拓扑(其中一些可能是过滤缓冲的数据),这些连接器上的活动请求,响应这些请求的表示的数据流以及这些请求的处理来定义。用户代理收到的表示。
只要没有未完成的请求,应用程序就会达到稳定状态; 即,它没有待处理的请求,并且对其当前请求集的所有响应已被完全接收或接收到可被视为表示数据流的点。对于浏览器应用程序,此状态对应于“网页”,包括主要表示和辅助表示,例如内嵌图像,嵌入式小程序和样式表。应用稳态的重要性在于它们对用户感知性能和网络请求流量突发性的影响。
浏览器应用程序的用户感知性能由稳定状态之间的延迟确定:在一个网页上选择超媒体链接与为下一个网页呈现可用信息之间的时间段。因此,浏览器性能的优化集中在减少通信延迟。
由于基于REST的体系结构主要通过资源表示的传递进行通信,因此延迟可能受到通信协议的设计和表示数据格式的设计的影响。在接收响应数据时递增地呈现响应数据的能力由媒体类型的设计和每个表示内的布局信息(内嵌对象的视觉尺寸)的可用性确定。
一个有趣的观察是,最有效的网络请求是不使用网络的请求。换句话说,重用缓存响应的能力可以显着提高应用程序性能。尽管由于查找开销而使用高速缓存会为每个单独的请求增加一些延迟,但即使一小部分请求导致可用的高速缓存命中,也会显着降低平均请求延迟。
应用程序的下一个控制状态驻留在第一个请求资源的表示中,因此获得该第一个表示是优先级。因此,通过“先响应并稍后思考”的协议来改进REST交互。换句话说,每个用户操作需要多次交互的协议,以便在发送内容响应之前执行诸如协商功能能力之类的操作,将比发送最有可能首先优先的协议慢,然后提供如果第一个响应不令人满意,客户端要检索的备选列表。
应用程序状态由用户代理控制和存储,并且可以由来自多个服务器的表示组成。除了使服务器从存储状态的可伸缩性问题中解放出来之外,这允许用户直接操纵状态(例如,Web浏览器的历史),预期对该状态的改变(例如,链接映射和表示的预取)以及跳转。从一个应用程序到另一个应用程序(例如,书签和URI条目对话框)。
因此,模型应用程序是通过检查并从当前表示集合中的备选状态转换中进行选择而从一个状态移动到下一个状态的引擎。毫不奇怪,这与超媒体浏览器的用户界面完全匹配。但是,该样式并不假设所有应用程序都是浏览器。实际上,通用连接器接口会从服务器隐藏应用程序详细信息,因此用户代理同样可以是为索引服务执行信息检索的自动化机器人,寻找符合特定条件的数据的个人代理或维护蜘蛛忙着巡逻破损的参考文献或修改过的内容。
4 经验和评估
自1994年以来,REST架构风格已被用于指导现代Web架构的设计和开发。本章介绍了在创建超文本传输协议(HTTP)和统一资源标识符(URI)的Internet标准时应用REST所获得的经验和教训,这两个规范定义了Web上所有组件交互使用的通用接口,以及以libwww-perl客户端库,Apache HTTP Server Project和协议标准的其他实现形式部署这些技术。
4.1 标准化web
开发REST的动机是为Web应该如何工作创建一个架构模型,以便它可以作为Web协议标准的指导框架。REST已应用于描述所需的Web体系结构,帮助识别现有问题,比较替代解决方案,并确保协议扩展不会违反使Web成功的核心约束。这项工作是作为Internet工程任务组(IETF)和万维网联盟(W3C)为Web定义架构标准的工作的一部分完成的:HTTP,URI和HTML。
Roy Thomas Fielding参与Web标准流程始于1993年末,同时开发了libwww-perl协议库,作为MOMspider的客户端连接器接口。当时,Web架构是由一套非正式的超文本笔记,草拟超文本规范,代表网络的建议功能(其中一些已经实施),以及公共www-talk邮件列表的存档,用于全球WWW项目参与者之间的非正式讨论。与Web实现相比,每个规范都显着过时,主要是由于在引入Mosaic图形浏览器[NCSA]之后Web的快速发展。HTTP中添加了几个实验性扩展以允许代理,但在大多数情况下,协议假定用户代理与HTTP源服务器或遗留系统的网关之间存在直接连接。在缓存,代理或蜘蛛的体系结构中没有意识到,即使实现很容易获得并且无法正常运行。
与此同时,行业内的压力越来越大,要求对Web接口协议的某些版本或版本进行标准化。W3C由Berners-Lee 组成,作为Web架构的智库,提供编写Web标准和参考实现所需的创作资源,但标准化本身由Internet工程任务组管及其关于URI,HTTP和HTML的工作组。
REST的第一版是在1994年10月到1995年8月之间开发的,主要是作为一种在我们编写HTTP / 1.0规范和初始HTTP / 1.1提案时传达Web概念的方法。它在接下来的五年中得到了迭代性的改进,并应用于Web协议标准的各种修订和扩展。REST最初被称为“HTTP对象模型”,但该名称通常会导致将其误解为HTTP服务器的实现模型。名称“Representational State Transfer”旨在唤起设计良好的Web应用程序行为的图像:网页网络(虚拟状态机),用户通过选择链接(状态转换)在应用程序中前进,
REST并非旨在捕获Web协议标准的所有可能用途。HTTP和URI的应用程序与分布式超媒体系统的应用程序模型不匹配。然而,重要的一点是REST确实捕获了分布式超媒体系统的所有这些方面,这些方面被认为是Web的行为和性能要求的核心,因此优化模型中的行为将导致部署的Web中的最佳行为建筑。换句话说,REST针对常见情况进行了优化,以便适用于Web体系结构的约束也将针对常见情况进行优化。
4.2 REST应用于URI
统一资源标识符(URI)既是Web架构中最简单的元素,也是最重要的元素。URI已被许多名称所知:WWW地址,通用文档标识符,通用资源标识符,最后是统一资源定位符(URL)和名称(URN的组合。除了它的名字之外,URI语法自1992年以来一直保持相对不变。但是,Web地址的规范也定义了我们所说的资源的范围和语义,资源自早期的Web架构以来已经发生了变化。REST用于定义URI标准的术语资源,以及通过其表示操作资源的通用接口的整体语义。
4.2.1 资源的重新定义
早期的Web架构将URI定义为文档标识符。指示作者根据文档在网络上的位置定义标识符。然后可以使用Web协议来检索该文档。然而,由于许多原因,这一定义被证明是不令人满意的。首先,它表明作者正在识别传输的内容,这意味着只要内容发生变化,标识符就会发生变化。其次,存在许多与服务而不是文档相对应的地址 - 作者可能打算将读者引导到该服务,而不是通过先前访问该服务的任何特定结果。最后,在某些时间段存在与文档不对应的地址,
REST中资源的定义基于一个简单的前提:标识符应尽可能不经常更改。因为Web使用嵌入式标识符而不是链接服务器,所以作者需要一个与超媒体引用所关注的语义紧密匹配的标识符,允许引用保持静态,即使访问该引用的结果可能随时间而变化。REST通过将资源定义为作者想要识别的语义来实现这一点,而不是在创建引用时对应于那些语义的值。然后将其留给作者以确保为引用选择的标识符确实标识了预期的语义。
4.2.2 操纵阴影
定义资源使得URI标识概念而不是文档会给我们留下另一个问题:用户如何访问,操作或转移概念,以便在选择超文本链接时可以获得有用的东西?REST通过定义被操纵为被识别资源的表示的事物而不是资源本身来回答该问题。源服务器维护从资源标识符到对应于每个资源的表示集的映射。因此,通过通过资源标识符定义的通用接口传送表示来操纵资源。
REST对资源的定义源于Web的核心要求:跨多个信任域独立创建互连超文本。强制接口定义与接口要求相匹配会导致协议看起来模糊,但这只是因为被操作的接口只是接口而不是实现。协议特定于应用程序操作的意图,但接口背后的机制必须决定该意图如何影响资源映射到表示的底层实现。
信息隐藏是激发REST统一接口的关键软件工程原则之一。由于客户端仅限于表示的操作而不是直接访问资源的实现,因此可以以命名机构所需的任何形式构造实现,而不会影响可能使用其表示的客户端。另外,如果在访问资源时存在多个资源表示,则可以使用内容选择算法来动态地选择最适合该客户端的能力的表示。当然,缺点是远程创作资源并不像文件的远程创作那样简单。
4.2.3 远程创作
通过Web的统一接口进行远程创作的挑战是由于客户端可以检索的表示与可能在服务器上用于存储,生成或检索该表示的内容的机制之间的分离。单个服务器可以将其命名空间的某些部分映射到文件系统,而文件系统又映射到可以映射到磁盘位置的inode的等效物,但是那些底层机制提供了将资源与一组表示相关联的方法。而不是识别资源本身。许多不同的资源可以映射到相同的表示,而其他资源可能根本没有映射。
为了创作现有资源,作者必须首先获取特定的源资源URI:绑定到目标资源的处理程序底层表示的URI集。资源并不总是映射到单个文件,但是所有非静态资源都是从其他一些资源派生的,通过遵循派生树,作者最终可以找到所有必须编辑的源资源,以便修改资源的表示。这些相同的原则适用于任何形式的派生表示,无论是来自内容协商,脚本,servlet,托管配置,版本控制等。
资源不是存储对象。资源不是服务器用于处理存储对象的机制。资源是概念映射 - 服务器接收标识符(标识映射)并将其应用于其当前映射实现(通常是特定于集合的深层树遍历和/或散列表的组合)以查找当前负责的处理程序然后,实现和处理程序实现根据请求内容选择适当的操作+响应。所有这些特定于实现的问题都隐藏在Web界面之后; 它们的性质不能由只能通过Web界面访问的客户端承担。
例如,考虑当网站在用户群中增长并决定用基于XOS平台的旧的Brand X服务器替换在FreeBSD上运行的新Apache服务器时会发生什么。磁盘存储硬件已更换。操作系统已被替换。HTTP服务器已被替换。甚至可以替换为所有内容生成响应的方法。但是,不需要更改的是Web界面:如果设计正确,新服务器上的命名空间可以反映旧服务器上的命名空间,这意味着从客户端的角度来看,它只知道资源而不知道它们是如何实现的除了网站改进的稳健性之外,没有任何改变。
4.2.4 将语义绑定到URI
如上所述,资源可以具有许多标识符。换句话说,当用于访问服务器时,可能存在两个或更多个具有等效语义的不同URI。也可能有两个URI导致在访问服务器时使用相同的机制,但这些URI标识两个不同的资源,因为它们并不意味着相同的事情。
语义是分配资源标识符并用表示填充这些资源的行为的副产品。在任何时候,服务器或客户端软件都不需要知道或理解URI的含义 - 它们只是作为一个管道,资源的创建者(人类命名机构)可以通过该管道将表示与由表示的语义相关联。 URI。换句话说,服务器上没有资源; 只是通过资源定义的抽象接口提供答案的机制。这可能看起来很奇怪,但这是使Web在许多不同实现中工作的本质。
每个工程师的本质是根据将用于组成成品的组件的特征来定义事物。网络不是这样的。Web体系结构包括组件之间通信模型的约束,基于应用程序操作期间每个组件的角色。这可以防止组件假设超出资源抽象的任何内容,从而隐藏抽象接口任一侧的实际机制。
4.2.5 URI中的REST不匹配
与大多数现实世界系统一样,并非所部署的Web体系结构的所有组件都遵循其体系结构设计中存在的每个约束。REST既被用作定义架构改进的手段,也用于识别架构不匹配。当由于无知或疏忽而部署了违反架构约束的软件实现时,会发生不匹配。虽然通常无法避免不匹配,但可以在它们变得标准化之前识别它们。
虽然URI设计符合REST的标识符架构概念,但仅凭语法不足以迫使命名机构根据资源模型定义自己的URI。滥用的一种形式是包括标识超媒体响应表示所引用的所有URI中的当前用户的信息。此类嵌入式用户ID可用于维护服务器上的会话状态,通过记录其操作来跟踪用户行为,或跨多个操作(例如,Hyper-G的网关[ 84 ])携带用户首选项。但是,通过违反REST的约束,这些系统还会导致共享缓存变得无效,降低服务器可伸缩性,并在用户与其他人共享这些引用时导致不良影响。
当软件试图将Web视为分布式文件系统时,会发生与REST资源接口的另一个冲突。由于文件系统公开其信息的实现,因此存在将该信息“镜像”到多个站点的工具,作为负载平衡和将内容重新分布到用户附近的手段。但是,他们只能这样做,因为文件具有一组固定的语义(一个命名的字节序列),可以轻松复制。相反,尝试将Web服务器的内容镜像为文件将失败,因为资源接口并不总是与文件系统的语义匹配,并且因为数据和元数据都包含在表示的语义中,并且对于表示的语义很重要。Web服务器内容可以在远程站点复制,
4.3 REST应用于HTTP
超文本传输协议(HTTP)在Web体系结构中具有特殊的作用,它既是Web组件之间通信的主要应用程序级协议,也是专门为资源表示传输而设计的唯一协议。与URI不同,为了支持现代Web架构,需要进行大量更改。HTTP实现的开发人员在采用提议的增强功能时一直保守,因此扩展需要在部署之前进行验证并进行标准审查。REST用于识别现有HTTP实现的问题,将该协议的可互操作子集指定为HTTP / 1.0 [ 19 ],分析HTTP / 1.1的建议扩展[ 42]],并提供部署HTTP / 1.1的激励理由。
由REST识别的HTTP中的关键问题区域包括规划新协议版本的部署,将消息解析与HTTP语义和底层传输层(TCP)分离,区分权威和非权威响应,细粒度控制缓存,以及无法自我描述的协议的各个方面。REST还被用于模拟基于HTTP的Web应用程序的性能,并预测这些扩展对持久连接和内容协商的影响。最后,REST已被用于将标准化HTTP扩展的范围限制为适合体系结构模型的范围,而不是允许滥用HTTP的应用程序同等地影响标准。
4.3.1 可扩展性
REST的主要目标之一是支持在已部署的体系结构中逐步和分散的更改部署。通过引入版本控制要求和规则来修改HTTP以支持该目标,以扩展每个协议的语法元素。
4.3.1.1 协议版本控制
HTTP是一系列协议,由主要版本号和次要版本号区分,它们共享名称主要是因为它们对应于直接与基于“http”URL命名空间的服务通信时所期望的协议。连接器必须遵守每条消息中包含的HTTP版本协议元素的约束。
消息的HTTP版本表示发送方的协议功能以及正在发送的消息的总兼容性(主要版本号)。这允许客户端在发出正常的HTTP / 1.1请求时使用简化的(HTTP / 1.0)特征子集,同时向接收者指示它能够支持完整的HTTP / 1.1通信。换句话说,它在HTTP规模上提供了一种暂定形式的协议协商。尽管某些客户端或服务器是链的一部分,但请求/响应链上的每个连接都可以在其最佳协议级别运行。
协议的目的是服务器应始终使用它在客户端请求消息的相同主要版本中理解的协议的最高次要版本进行响应。限制是服务器不能使用更高级别协议的那些可选功能,这些功能被禁止发送到这样的旧版客户端。协议没有必需的功能,不能与该主要版本中的所有其他次要版本一起使用,因为这将是不兼容的更改,因此需要更改主要版本。可以依赖于次要版本号更改的HTTP的唯一功能是由通信中的直接邻居解释的那些功能,因为HTTP不要求中间组件的整个请求/响应链说出相同的版本。
存在这些规则以帮助部署多个协议修订并防止HTTP架构师忘记协议的部署是其设计的重要方面。他们通过简化协议的兼容更改和不兼容的更改来实现这一点。兼容的更改易于部署,并且可以在协议流内实现差异的通信。不兼容的更改难以部署,因为它们需要在协议流开始之前确定接受协议。
4.3.1.2 可扩展协议元素
HTTP包括许多单独的命名空间,每个命名空间都有不同的约束,但所有这些命名空间都共享无限制可扩展的要求。一些命名空间由不同的Internet标准管理,并由多个协议共享(例如,URI方案[ 21 ],媒体类型[ 48 ],MIME头字段名[ 47 ],字符集值,语言标签),而其他名称由HTTP,包括方法名称的命名空间,响应状态代码,非MIME标头字段名称以及标准HTTP标头字段中的值。由于早期的HTTP没有为如何部署这些命名空间中的更改定义一套一致的规则,因此这是规范工作所解决的首批问题之一。
HTTP请求语义由请求方法名称表示。只要可以在客户端,服务器和可能位于它们之间的任何中介之间共享可标准化的语义集,就允许方法扩展。不幸的是,早期的HTTP扩展,特别是HEAD方法,使HTTP响应消息的解析依赖于知道请求方法的语义。这导致了部署矛盾:如果接收方需要知道方法的语义,然后才能由中介安全地转发,则必须先更新所有中介,然后才能部署新方法。
通过将用于解析和转发HTTP消息的规则与与新HTTP协议元素相关联的语义分开来解决此部署问题。例如,HEAD是Content-Length头字段具有除表示消息体长度之外的含义的唯一方法,并且没有新方法可以更改消息长度计算。GET和HEAD也是条件请求头字段具有缓存刷新语义的唯一方法,而对于所有其他方法,它们具有前置条件的含义。
同样,HTTP需要一个通用规则来解释新的响应状态代码,这样可以部署新的响应而不会显着损害旧的客户端。因此,我们扩展了以下规则:每个状态代码属于由其三位十进制数的第一个数字表示的类:100-199表示该消息包含临时信息响应,200-299表示请求成功,300 -399表示请求需要重定向到另一个资源,400-499表示客户端发出不应该重复的错误,500-599表示服务器遇到错误,但客户端可能会更好稍后回复(或通过其他服务器)。如果收件人不理解给定消息中状态代码的特定语义,然后他们必须以与其类中的x00代码相同的方式对待它。与方法名称的规则一样,此可扩展性规则对当前体系结构提出了要求,以便预测未来的更改。因此,可以将更改部署到现有架构上,而不必担心不良组件反应。
4.3.1.3 升级
在HTTP / 1.1中添加Upgrade头字段通过允许客户端在较旧的协议流中进行通信时宣传其对更好协议的意愿来减少部署不兼容更改的难度。升级是专门添加的,以支持选择性地替换HTTP / 1.x与其他未来的协议,这些协议可能对某些任务更有效。因此,HTTP不仅支持内部可扩展性,还在活动连接期间完成自身的替换。如果服务器支持改进的协议并希望切换,则它只响应101状态并继续,就像在该升级的协议中接收到请求一样。
4.3.2 自描述信息
REST将组件之间的消息约束为自描述,以支持交互的中间处理。但是,早期HTTP的某些方面无法进行自我描述,包括请求中缺少主机标识,无法在语法上区分消息控制数据和表示元数据,无法区分仅用于直接连接对等的控制数据与针对所有收件人的元数据,缺乏对强制性扩展的支持,以及需要元数据来描述具有分层编码的表示。
4.3.2.1 主机
早期HTTP设计中最严重的错误之一是决定不发送作为请求消息目标的完整URI,而是仅发送那些未用于设置连接的部分。假设服务器将根据连接的IP地址和TCP端口知道自己的命名权限。但是,这未能预料到单个服务器上可能存在多个命名权限,这成为一个关键问题,因为Web以指数速率增长,而新域名(http URL命名空间内命名权限的基础)远远超出了可用性新的IP地址。
为HTTP / 1.0和HTTP / 1.1定义和部署的解决方案是在请求消息的主机头字段中包含目标URL的主机信息。此功能的部署被认为非常重要,以至于HTTP / 1.1规范要求服务器拒绝任何不包含主机字段的HTTP / 1.1请求。结果,现在存在许多大型ISP服务器,其在单个IP地址上运行数万个基于名称的虚拟主机网站。
4.3.2.2 分层编码
HTTP继承了其用于描述多用途Internet邮件扩展(MIME)中的表示元数据的语法。MIME不定义分层媒体类型,而是仅在Content-Type字段值中包含最外层媒体类型的标签。然而,这防止接收者在不解码层的情况下确定编码消息的性质。早期的HTTP扩展通过在Content-Encoding字段中单独列出外部编码并将最内层媒体类型的标签放在Content-Type中来解决此问题。这是一个糟糕的设计决策,因为它改变了Content-Type的语义而不改变其字段名称,导致每当旧用户代理遇到扩展时就会出现混淆。
更好的解决方案是继续将Content-Type视为最外层媒体类型,并使用新字段来描述该类型中的嵌套类型。不幸的是,第一次扩展是在发现故障之前部署的。
REST确实需要另外一层编码:连接器放置在消息上以便提高其在网络上的可转移性。这个新层(称为转移编码,参考MIME中的类似概念)允许对消息进行编码以进行传输,而不暗示该表示是按性质编码的。无论出于何种原因,转移代理都可以添加或删除转移编码,而无需更改表示的语义。
4.3.2.3 语义独立性
如上所述,HTTP消息解析已与其语义分离。消息解析(包括查找和汇总头字段)完全独立于解析头字段内容的过程。通过这种方式,中介可以快速处理和转发HTTP消息,并且可以在不破坏现有解析器的情况下部署扩展。
4.3.2.4 运输独立性
早期的HTTP,包括HTTP / 1.0的大多数实现,使用底层传输协议作为发送响应消息结束信号的手段。服务器将通过关闭TCP连接来指示响应消息正文的结束。不幸的是,这在协议中造成了严重的失败情况:客户端无法区分已完成的响应和因某些错误的网络故障而被截断的响应。为了解决这个问题,Content-Length头字段在HTTP / 1.0中被重新定义,以指示消息体长度(以字节为单位),每当事先知道长度,并且“chunked”传输编码被引入HTTP / 1.1。
分块编码允许在其生成开始时(当发送报头字段时)大小未知的表示使其边界由一系列块描绘,这些块在发送之前可以单独地大小。它还允许在消息末尾将元数据作为预告片发送,从而在生成消息时在原点创建可选元数据,而不会增加响应延迟。
4.3.2.5 大小限制
对应用层协议的灵活性的常见障碍是倾向于过度指定协议参数的大小限制。尽管在协议的实现中总是存在一些实际限制(例如,可用存储器),但是在协议内指定那些限制将所有应用限制到相同的限制,而不管它们的实现如何。结果通常是最低公分母协议,不能超出其原始创建者的设想。
HTTP协议对URI的长度,头字段的长度,表示的长度或由项列表组成的任何字段值的长度没有限制。尽管较旧的Web客户端存在一个众所周知的URI,其中包含超过255个字符的问题,但只需要注意HTTP规范中的问题而不是要求所有服务器都受到限制。这不会使协议最大化的原因是受控上下文(例如Intranet)内的应用程序可以通过替换旧组件来避免这些限制。
虽然我们不需要发明人为限制,但HTTP / 1.1确实需要定义一组适当的响应状态代码,以指示给定协议元素何时对服务器进行处理时间过长。这些响应代码是在Request-URI条件太长,标题字段太长,正文太长的情况下添加的。遗憾的是,客户端无法向服务器指示它可能具有资源限制,这在资源受限设备(例如PDA)尝试使用HTTP而没有特定于设备的中介调整通信时会导致问题。
4.3.2.6 缓存限制
由于REST试图平衡对高效,低延迟行为的需求与对语义透明缓存行为的需求,因此HTTP允许应用程序确定缓存要求而不是将其硬编码到协议本身中至关重要。协议要做的最重要的事情就是完全准确地描述正在传输的数据,这样就不会让任何应用程序误以为它实际上有其他东西时会有一件事情。HTTP / 1.1通过添加Cache-Control,Age,Etag和Vary头字段来实现此目的。
4.3.2.7 内容协商
所有资源都将请求(包括方法,标识符,请求标题字段,有时是表示)映射到响应(由状态代码,响应标头字段,有时是表示)组成。当HTTP请求映射到服务器上的多个表示时,服务器可以与客户进行内容协商,以确定哪个最符合客户的需求。这实际上比谈判更像是一个“内容选择”过程。
虽然有几个部署的内容协商实现,但它没有包含在HTTP / 1.0规范中,因为在发布时没有可互操作的实现子集。这部分是由于NCSA Mosaic中的实施不佳,无论资源的可转让性如何,都会在每个请求的头字段中发送1KB的偏好信息[ 125 ]。由于远低于所有URI的0.01%在内容中可协商,因此结果显着增加了请求延迟以获得非常小的增益,这导致后来的浏览器无视HTTP / 1.0的协商功能。
当服务器根据请求头字段的值或上述正常请求参数外部的某些内容改变特定请求方法标识符状态代码组合的响应表示时,发生抢占(服务器驱动)协商。发生这种情况时,客户端需要得到通知,以便缓存可以知道何时对于将来的请求使用特定的缓存响应在语义上是透明的,并且还使得用户代理可以提供比通常一次发送的更详细的首选项。它知道它们对收到的回复有影响。HTTP / 1.1为此目的引入了Vary头字段。Vary只列出响应可能变化的请求标头字段维度。
在抢先协商中,用户代理告诉服务器它能够接受什么。然后,服务器应该选择与用户代理声称其功能最匹配的表示。然而,这是一个不易处理的问题,因为它不仅需要关于UA将接受什么的信息,而且还需要它接受每个特征的程度以及用户打算表达的目的。例如,想要在屏幕上查看图像的用户可能更喜欢简单的位图表示,但是如果他们打算将其发送到打印机,则具有相同浏览器的相同用户可能更喜欢PostScript表示。它还取决于用户根据自己的个人内容偏好正确配置他们的浏览器。简而言之,
HTTP / 1.1增加了被动(代理驱动)协商的概念。在这种情况下,当用户代理请求协商的资源时,服务器以可用的表示列表进行响应。然后,用户代理可以根据自己的能力和目的选择哪一个最佳。关于可用表示的信息可以通过响应数据内部的单独表示(例如,300响应)来提供(例如,条件HTML),或者作为“最可能”响应的补充。后者最适合Web,因为只有当用户代理决定其他变体之一会更好时,才需要额外的交互。反应式协商只是普通浏览器模型的自动反映,这意味着它可以充分利用REST的所有性能优势。
抢先式和被动式协商都难以传达表示维度的实际特征(例如,如何说浏览器支持HTML表而不是INSERT元素)。然而,反应式协商具有明显的优点,即不必在每个请求上发送首选项,具有更多的上下文信息,在面对备选方案时可以做出决策,而不是干扰缓存。
第三种形式的协商,透明协商[ 64 ],是中间缓存代表其他代理充当代理的许可,用于选择更好的表示并发起检索该表示的请求。该请求可以通过另一个缓存命中在内部解析,因此可能不会进行额外的网络请求。但是,在这样做时,它们正在执行服务器驱动的协商,因此必须添加适当的Vary信息,以便其他出站缓存不会混淆。
4.3.3 性能
HTTP / 1.1侧重于改进组件之间通信的语义,但是对用户感知性能也有一些改进,尽管受到与HTTP / 1.0的语法兼容性要求的限制。
4.3.3.1 持久连接
虽然早期HTTP的单个请求/响应是针对简单实现的每个连接行为,但由于每个交互设置成本的开销和TCP的慢启动拥塞控制算法的性质,导致底层TCP传输的使用效率低下[ 63],125 ]。因此,提出了几个扩展,以在单个连接中组合多个请求和响应。
第一个提议是定义一组新的方法,用于在单个消息(MGET,MHEAD等)中封装多个请求,并将响应作为MIME多部分返回。这被拒绝了,因为它违反了几个REST约束。首先,客户端需要知道在第一个请求被写入网络之前它想要打包的所有请求,因为请求主体必须由初始请求头字段中设置的内容长度字段进行长度分隔。其次,中介必须提取每条消息,以确定它可以在本地满足哪些消息。最后,它有效地使请求方法的数量加倍,并使选择性地拒绝访问某些方法的机制复杂化。
相反,我们采用了一种持久连接形式,它使用长度分隔的消息,以便在单个连接上发送多个HTTP消息。对于HTTP / 1.0,这是使用Connection头字段中的“keep-alive”指令完成的。不幸的是,这通常不起作用,因为标题字段可以由中介转发给其他不了解保持活动的中介,从而导致死锁条件。HTTP / 1.1最终决定使持久连接成为默认值,从而通过HTTP版本值表明它们的存在,并且仅使用connection-directive“close”来反转默认值。
值得注意的是,只有在将HTTP消息重新定义为自描述且独立于底层传输协议之后,才能实现持久连接。
4.3.3.2 直写缓存
HTTP不支持回写缓存。HTTP缓存不能假定通过它写入的内容与从该资源的后续请求可检索的内容相同,因此它无法缓存PUT请求主体并将其重用于以后的GET响应。此规则有两个原因:1)可能在幕后生成元数据,2)无法从PUT请求确定以后GET请求的访问控制。但是,由于使用Web的写入操作极少,因此缺少回写式高速缓存不会对性能产生重大影响。
4.3.4 HTTP中的REST不匹配
HTTP中存在一些架构与REST不匹配,一些是由于部署在标准流程外部的第三方扩展,另一些是由于必须保持与部署的HTTP / 1.0组件兼容。
4.3.4.1 区分非权威影响
HTTP中仍然存在的一个缺点是,没有一致的机制来区分由原始服务器响应当前请求而生成的权威响应,以及从中间设备或缓存获取的非权威响应,而无需访问原始服务器。这种区别对于需要权威响应的应用程序非常重要,例如健康行业中使用的安全关键信息设备,以及返回错误响应并且客户端想知道错误是否源于原点的那些时间或某些中间人。尝试使用其他状态代码解决此问题并未成功,因为权威性质通常与响应状态正交。
HTTP / 1.1确实添加了一种机制来控制缓存行为,从而可以指示对权威响应的需求。请求消息上的“no-cache”指令要求任何缓存将请求转发到源服务器,即使它具有所请求内容的缓存副本。这允许客户端刷新已知已损坏或过时的缓存副本。但是,定期使用此字段会干扰缓存的性能优势。更通用的解决方案是,只要操作不会导致联系原始服务器,就要求将响应标记为非权威。为此目的(和其他)在HTTP / 1.1中定义了一个警告响应头字段,但它在实践中尚未广泛实现。
4.3.4.2 Cookies
举例说明对协议进行了不适当的扩展以支持与通用接口所需属性相矛盾的功能,即以HTTP cookie的形式引入站点范围的状态信息。Cookie交互无法与REST的应用程序状态模型匹配,通常会导致典型浏览器应用程序混淆。
HTTP cookie是不透明的数据,可以由源服务器分配给用户代理,方法是将其包含在Set-Cookie响应头字段中,目的是用户代理应该在将来对该服务器的所有请求中包含相同的cookie直到它被替换或过期。此类cookie通常包含一组特定于用户的配置选项,或者在将来的请求中与服务器数据库匹配的令牌。问题在于,cookie被定义为附加到对给定资源标识符集的任何未来请求,通常包含整个站点,而不是与浏览器上的特定应用程序状态(当前呈现的表示集合)相关联。当浏览器的历史功能(“返回”)随后用于备份到cookie反映的视图之前的视图,浏览器的应用程序状态不再与cookie中表示的存储状态匹配。因此,发送到同一服务器的下一个请求将包含一个错误表示当前应用程序上下文的cookie,从而导致双方混淆。
Cookie还违反了REST,因为它们允许在没有充分识别其语义的情况下传递数据,从而成为安全和隐私的关注点。Cookie与Referer [sic]标题字段的组合使得跟踪用户在站点之间浏览时成为可能。
因此,Web上基于cookie的应用程序永远不可靠。应该通过匿名身份验证和真正的客户端状态来完成相同的功能。通过明智地使用上下文设置URI而不是cookie,可以更有效地实现涉及首选项的状态机制,其中明智意味着每个状态一个URI而不是由于嵌入用户id而无限数量的URI。同样,通过在超媒体数据格式中定义购物项的语义,允许用户代理选择和存储这些项,可以更有效地实现使用cookie来识别服务器端数据库中的用户特定“购物篮”。在他们自己的客户端购物篮中,配有一个URI,用于在客户准备购买时签出。
4.3.4.3 强制扩展
HTTP标头字段名称可以随意扩展,但只有在它们包含的信息不是正确理解消息时才需要扩展。强制标头字段扩展需要主要协议修订或对方法语义的实质性更改,例如[ 94]。这是现代Web架构的一个方面,它尚未与REST架构风格的自描述消息传递约束相匹配,主要是因为在现有HTTP语法中实现强制扩展框架的成本超过了我们可能从中获得的任何明显好处。强制性扩展。但是,当对语法的向后兼容性的现有约束不再适用时,期望在HTTP的下一个主要修订版中支持必需的字段名称扩展是合理的。
4.3.4.4 混合元数据
HTTP旨在通过网络连接扩展通用连接器接口。因此,它旨在匹配该接口的特征,包括将参数描绘为控制数据,元数据和表示。但是,HTTP / 1.x协议系列的两个最重要的限制是它无法在语法上区分表示元数据和消息控制信息(都作为头字段传输),并且不允许元数据有效地分层以实现消息完整性检查。
REST在标准化过程的早期阶段将这些限制在协议中,预计它们会导致其他功能部署中的问题,例如持久连接和摘要式身份验证。开发了变通方法,包括添加Connection头字段以识别中间人转发不安全的每连接控制数据,以及标头字段摘要的规范处理算法。
4.3.4.5 MIME语法
HTTP继承了MIME 的消息语法,以保持与其他Internet协议的通用性,并重用许多标准化字段来描述消息中的媒体类型。不幸的是,MIME和HTTP具有非常不同的目标,并且语法仅针对MIME的目标而设计。
在MIME中,用户代理正在向未知的接收者发送一堆信息,这些信息旨在被视为一个连贯的整体,而这些信息从未直接与之交互。MIME假定代理希望在一条消息中发送所有信息,因为通过Internet邮件发送多条消息的效率较低。因此,构造MIME语法以将邮件打包在部件或多部件中,这与邮政运营商将包裹包装在额外纸张中的方式非常相似。
在HTTP中,除了安全封装或打包存档之外,在单个消息中打包不同对象没有任何意义,因为对尚未缓存的那些文档进行单独请求更有效。因此,HTTP应用程序使用HTML等媒体类型作为对“包”的引用的容器 - 然后用户代理可以选择要作为单独请求检索的包的哪些部分。尽管HTTP可能使用多部分包,其中第一部分之后仅包含非URI资源,但对它的需求并不大。
MIME语法的问题在于它假设传输是有损的,故意破坏换行符和内容长度之类的东西。因此,对于不基于有损传输的任何系统,语法都是冗长且低效的,这使得它不适合HTTP。由于HTTP / 1.1具有支持不兼容协议部署的能力,因此对于下一个主要版本的HTTP,保留MIME语法不是必需的,即使它可能会继续使用许多标准化协议元素来表示元数据。
4.3.5 匹配对请求的响应
在描述哪个响应属于哪个请求时,HTTP消息无法自我描述。早期的HTTP基于每个连接的单个请求和响应,因此不需要消息控制数据将响应绑定回调用它的请求。因此,请求的排序决定了响应的顺序,这意味着HTTP依赖于传输连接来确定匹配。
HTTP / 1.1虽然被定义为独立于传输协议,但仍假定通信发生在同步传输上。通过添加请求标识符,可以轻松扩展它以处理异步传输,例如电子邮件。这种扩展对于广播或多播情况下的代理是有用的,其中可以在与请求的信道不同的信道上接收响应。此外,在许多请求待处理的情况下,它将允许服务器选择响应被传送的顺序,从而首先发送更小或更重要的响应。
4.4 技术转让
虽然REST对Web标准的创作有最直接的影响,但是通过以商业级实现的形式部署标准来验证其作为架构设计模型的使用。
4.4.1 libwww-perl的实践经验
我参与Web标准的定义始于维护机器人MOMspider 及其相关协议库libwww-perl的开发。以Tim Berners-Lee开发的原始libwww和CERN的WWW项目为模型,libwww-perl提供了一个统一的界面,用于为使用Perl语言编写的客户端应用程序发出Web请求和解释Web响应。它是第一个独立于原始CERN项目开发的Web协议库,反映了Web接口比旧代码库中更现代的解释。该接口成为设计REST的基础。
libwww-perl由单个请求接口组成,该接口使用Perl的自我评估代码功能,根据请求的URI方案动态加载适当的传输协议包。例如,当要求在URL http://www.ebuilt.com/上发出“GET”请求时,libwww-perl将从URL(“http”)中提取该方案并使用它来构建一个调用到wwwhttp'request(),使用所有类型资源(HTTP,FTP,WAIS,本地文件等)共有的接口。为了实现这个通用接口,库以与HTTP代理大致相同的方式处理所有调用。它提供了一个使用Perl数据结构的接口,它具有与HTTP请求相同的语义,而不管资源的类型如何。
libwww-perl展示了通用接口的优点。在最初发布的一年内,超过600名独立软件开发人员使用该库创建自己的客户端工具,从命令行下载脚本到成熟的浏览器。它目前是大多数Web系统管理工具的基础。
4.4.2 Apache的实践经验
由于HTTP的规范工作开始采用完整规范的形式,我们需要服务器软件,既可以有效地演示建议的标准协议,也可以作为有价值扩展的测试平台。当时,最受欢迎的HTTP服务器(httpd)是由Rob McCool在伊利诺伊大学厄巴纳 - 香槟分校(NCSA)的国家超级计算应用中心开发的公共领域软件。然而,在1994年中期Rob离开NCSA后,开发工作陷入停滞,许多网站管理员开发了自己的扩展和错误修复,需要共同分发。我们一群人创建了一个邮件列表,目的是将我们的更改作为原始来源的“补丁”进行协调。在此过程中,我们创建了Apache HTTP Server Project 。
Apache项目是一项协作软件开发工作,旨在为HTTP服务器创建一个强大的,商业级的,功能齐全的开源软件实现。该项目由位于世界各地的一组志愿者共同管理,使用互联网和网络来交流,规划和开发服务器及其相关文档。这些志愿者被称为Apache Group。最近,该组织成立了非营利组织Apache Software Foundation,作为支持Apache开源项目持续开发的法律和金融伞形组织。
Apache以其强大的行为而闻名,以响应互联网服务的各种需求以及严格实施HTTP协议标准。我在Apache Group中担任“协议警察”,为核心HTTP解析函数编写代码,通过解释标准支持其他人的工作,并充当Apache开发人员对“正确实施方法”的观点的倡导者HTTP“在标准论坛内。本章中描述的许多课程都是在Apache项目中创建和测试HTTP的各种实现,并将协议背后的理论置于Apache Group的批判性审核中。
Apache httpd被广泛认为是最成功的软件项目之一,也是第一个主导市场的开源软件产品之一,其中存在重大的商业竞争。2000年7月Netcraft对公共互联网网站的调查发现,基于Apache软件的网站超过2000万个,占所有被调查网站的65%以上[http://www.netcraft.com/survey/]。Apache是第一个支持HTTP / 1.1协议的主要服务器,通常被认为是测试所有客户端软件的参考实现。Apache Group获得1999年ACM软件系统奖,以表彰我们对Web架构标准的影响。
4.4.3 部署URI和HTTP/1.1兼容软件
除了Apache之外,许多其他商业和开源项目都采用并部署了基于现代Web架构协议的软件产品。虽然这可能只是巧合,但微软Internet Explorer在第一个实现HTTP / 1.1客户端标准的主要浏览器之后不久便在Web浏览器市场份额中超越了Netscape Navigator。此外,在标准化过程中定义的许多单独的HTTP扩展(例如Host头字段)现在已经达到了通用部署。
REST架构风格成功地指导了现代Web架构的设计和部署。到目前为止,新标准的引入并没有出现重大问题,尽管它们与传统的Web应用程序一起进行了逐步和分散的部署。此外,新标准对Web的健壮性产生了积极影响,并通过缓存层次结构和内容分发网络实现了提高用户感知性能的新方法。
4.5 架构课程
从现代Web架构和REST识别的问题中可以学到许多一般的架构课程。
4.5.1 基于网络的API优点
现代Web与其他中间件的区别在于它使用HTTP作为基于网络的应用程序编程接口(API)的方式。情况并非总是如此。早期的Web设计使用了一个库包CERN libwww作为所有客户端和服务器的单一实现库。CERN libwww提供了一个基于库的API,用于构建可互操作的Web组件。
基于库的API提供了一组代码入口点和相关的符号/参数集,以便程序员可以使用其他人的代码来执行维护类似系统之间的实际接口的脏工作,前提是程序员遵循架构和语言该代码附带的限制。假设通信的所有方面都使用相同的API,因此接口的内部仅对API开发人员而非应用程序开发人员很重要。
单一图书馆方法于1993年结束,因为它与参与开发网络的组织的社会动态不匹配。当NCSA的团队通过比CERN更大的开发团队增加Web开发的速度时,libwww源代码被“分叉”(分成单独维护的代码库),以便NCSA的人们不必等待欧洲核子研究中心追赶他们的进步。与此同时,像我这样的独立开发人员开始为CERN代码尚未支持的语言和平台开发协议库。Web的设计必须从参考协议库的开发转向基于网络的API的开发,在多个平台和实现之间扩展Web的所需语义。
基于网络的API是一种线上语法,具有定义的语义,用于应用程序交互。除了需要读取/写入网络之外,基于网络的API不会对应用程序代码施加任何限制,但会对可以通过接口有效通信的语义集施加限制。从好的方面来说,性能仅受协议设计的限制,而不受该设计的任何特定实现的限制。
基于库的API为程序员提供了更多功能,但这样做会产生比任何一个系统所需的更多复杂性和包袱,在异构网络中的可移植性更低,并且总是导致通用性优于性能。作为一种副作用,它还会导致懒惰的开发(将所有内容归咎于API代码)以及未能解释通信中其他方的非合作行为。
但是,重要的是要记住,任何架构都涉及各种层,包括现代Web的层。像Web这样的系统使用一个库API(套接字)来访问几个基于网络的API(例如,HTTP和FTP),但套接字API本身位于应用层之下。同样,libwww是一个有趣的交叉品种,它已经演变成基于库的API,用于访问基于网络的API,因此提供可重用的代码,而不假设其他通信应用程序也在使用libwww。
这与CORBA 等中间件形成鲜明对比。由于CORBA只允许通过ORB进行通信,因此其传输协议IIOP对于各方的通信过多。HTTP请求消息包括标准化应用程序语义,而IIOP消息则不包括。IIOP中的“请求”令牌仅提供方向性,以便ORB可以根据ORB本身是否应该回复(例如,“LocateRequest”)或者它是否将被对象解释来路由它。语义由对象键和操作的组合表示,它们是特定于对象的,而不是跨所有对象的标准化。
独立开发人员可以在不使用相同ORB的情况下生成与IIOP请求相同的位,但这些位本身由CORBA API及其接口定义语言(IDL)定义。它们需要由IDL编译器生成的UUID,反映IDL操作签名的结构化二进制内容,以及根据IDL规范定义的回复数据类型。因此,语义不是由网络接口(IIOP)定义的,而是由对象的IDL规范定义的。这是否是一件好事取决于应用程序 - 对于分布式对象,它是必需的,对于Web来说它不是。
为什么这很重要?因为它区分了一个系统,在这个系统中,网络中介可以是有效的代理,而系统最多只能是路由器。
在将消息解释为单元或流时,也可以看到这种差异。HTTP允许收件人或发件人自己决定。CORBA IDL甚至不允许流(但是),但即使它扩展到支持流,通信的两端也将绑定到相同的API,而不是自由使用任何最适合其类型的应用。
4.5.2 HTTP不是RPC
人们经常错误地将HTTP称为远程过程调用(RPC)机制,因为它涉及请求和响应。RPC与其他形式的基于网络的应用程序通信的区别在于在远程机器上调用过程的概念,其中协议识别过程并向其传递一组固定的参数,然后等待在一个参数内提供的答案。使用相同的接口返回消息。远程方法调用(RMI)类似,只是该过程被标识为{object,method}元组而不是服务过程。Brokered RMI增加了名称服务间接和其他一些技巧,但界面基本相同。
HTTP与RPC的区别不在于语法。它甚至不是使用流作为参数获得的不同特性,但这有助于解释为什么现有的RPC机制不能用于Web。使HTTP明显不同于RPC的原因是请求是使用具有标准语义的通用接口定向到资源的,这些语义可以由中间人解释,也可以由发起服务的机器解释。结果是一个应用程序,它允许独立于信息源的转换和间接层,这对于Internet规模,多组织,无法扩展的信息系统非常有用。相反,RPC机制是根据语言API定义的,而不是基于网络的应用程序。
4.5.3 HTTP不是传输协议
HTTP不是设计为传输协议。它是一种传输协议,其中消息通过传输和操纵这些资源的表示对资源执行操作来反映Web体系结构的语义。使用这个非常简单的接口可以实现广泛的功能,但是需要遵循接口才能使HTTP语义对中间人保持可见。
这就是HTTP通过防火墙的原因。除了WebDAV之外,大多数最近提出的HTTP扩展只是使用HTTP作为一种通过防火墙移动其他应用程序协议的方法,这是一个根本误导的想法。它不仅没有达到防火墙的目的,而且从长远来看也不会起作用,因为防火墙厂商只需要执行额外的协议过滤。因此,在HTTP之上进行这些扩展是没有意义的,因为在这种情况下,HTTP完成的唯一事情是增加遗留语法的开销。HTTP的真正应用将协议用户的动作映射到可以使用HTTP语义表达的东西,从而为服务创建基于网络的API,代理和中介可以在不知道应用程序的情况下理解这些API。
4.5.4 媒体类型的设计
REST对建筑风格来说不同寻常的一个方面是它影响Web架构中数据元素定义的程度。
4.5.4.1 基于网络的系统中的应用状态
REST定义了一个预期的应用程序行为模型,它支持简单而强大的应用程序,这些应用程序在很大程度上不受困扰大多数基于网络的应用程序的部分。 但是,这并不能阻止应用程序开发人员引入违反该模型的功能。最常见的违规是关于应用程序状态和无状态交互的约束。
由于错误放置应用程序状态而导致的架构不匹配不仅限于HTTP cookie。对超文本标记语言(HTML)引入“框架”引起了类似的混淆。框架允许将浏览器窗口划分为子窗口,每个窗口都有自己的导航状态。子窗口中的链接选择与正常转换无法区分,但生成的响应表示在子窗口中呈现,而不是在完整的浏览器应用程序工作空间中呈现。这很好,前提是没有链接退出用于子窗口处理的信息领域,但是当它确实发生时,用户发现自己正在查看一个楔入另一个应用程序的子上下文中的应用程序。
对于帧和cookie,失败的原因是提供了用户代理无法管理或解释的间接应用程序状态。将此信息置于主要表示中的设计,从而通知用户代理如何管理指定的资源域的超媒体工作空间,可以在不违反REST约束的情况下完成相同的任务,同时实现更好的用户界面和减少对缓存的干扰。
4.5.4.2 增量处理
通过将延迟减少作为架构目标,REST可以根据用户感知的性能区分媒体类型(表示的数据格式)。增量渲染的大小,结构和容量都会影响传输,呈现和操作表示媒体类型时遇到的延迟,因此会显着影响系统性能。
HTML是媒体类型的一个示例,在大多数情况下,它具有良好的延迟特性。早期HTML中的信息可以在收到时呈现,因为所有渲染信息都是早期可用的 - 在构成HTML的一小组标记标记的标准化定义中。但是,有些方面的HTML设计不适合延迟。示例包括:在文档的HEAD中放置嵌入的元数据,导致在呈现引擎可以读取显示对用户有用的部分之前需要传输和处理的可选信息[ 93]。嵌入图像没有渲染大小提示,要求在显示周围HTML的其余部分之前接收图像的前几个字节(包含布局大小的部分); 动态调整大小的表列,要求渲染器在开始显示顶部之前读取并确定整个表的大小; 并且,关于解析格式错误的标记语法的惰性规则,通常要求渲染引擎在确定缺少一个键标记字符之前解析整个文件。
4.5.4.3 Java与JavaScript
REST还可用于深入了解为什么某些媒体类型在Web架构中的使用率高于其他媒体类型,即使开发者意见的平衡不利于他们。Java applet与JavaScript的情况就是一个例子。
Java TM 是一种流行的编程语言,最初是为电视机顶盒中的应用程序开发的,但是当它被引入Web作为实现按需代码功能的手段时,它首先获得了声名狼借。尽管该语言得到了其所有者Sun Microsystems,Inc。的大量媒体支持,以及寻求替代C ++语言的软件开发人员的好评如潮,但它未能被应用程序开发人员广泛采用,以满足代码需求。在网络内。
在Java推出后不久,Netscape Communications Corporation的开发人员为嵌入式按需代码创建了一种单独的语言,最初称为LiveScript,但后来因营销原因改为JavaScript名称(这两种语言除此之外几乎没有共同点)。虽然最初被嘲笑嵌入HTML并且与正确的HTML语法不兼容,但自从推出以来,JavaScript的使用率一直在稳步增长。
问题是:为什么JavaScript在Web上比Java更成功?它当然不是因为它的技术质量作为一种语言,因为与Java相比,它的语法和执行环境都被认为是差的。这也不是因为市场营销:Sun在这方面远远超过Netscape,并且还在继续这样做。这并不是因为语言的任何固有特性,因为Java在所有其他编程领域(独立应用程序,servlet等)中比JavaScript更成功。为了更好地理解这种差异的原因,我们需要根据其特性评估Java作为REST中的表示媒体类型。
JavaScript更适合Web技术的部署模型。它的入门门槛要低得多,无论是作为一种语言的整体复杂性还是新手程序员整理第一段工作代码所需的初始工作量。JavaScript对交互的可见性影响也较小。独立组织可以像复制HTML一样阅读,验证和复制JavaScript源代码。相反,Java是作为二进制打包存档下载的 - 因此用户可以信任Java执行环境中的安全限制。同样,Java还有许多在安全环境中被认为有问题的功能,包括将RMI请求发送回原始服务器的能力。RMI不支持中介机构的可见性。
然而,两者之间最重要的区别可能是JavaScript导致较少的用户感知延迟。JavaScript通常作为主要表示的一部分下载,而Java applet则需要单独的请求。Java代码一旦转换为字节代码格式,就比典型的JavaScript大得多。最后,虽然可以在下载HTML页面的其余部分时执行JavaScript,但Java要求在应用程序开始之前下载并安装完整的类文件包。因此,Java不支持增量渲染。
一旦语言的特征与REST约束背后的基本原理相同,就可以更容易地根据现代Web架构中的行为来评估技术。
5 总结
REST是一组协调的体系结构约束,旨在最大限度地减少延迟和网络通信,同时最大限度地提高组件实现的独立性和可伸缩性。这是通过在连接器语义上设置约束来实现的,其中其他样式专注于组件语义。REST支持交互的缓存和重用,组件的动态可替代性以及中介处理操作,从而满足Internet规模分布式超媒体系统的需求。
现代Web是REST风格体系结构的一个实例。虽然基于Web的应用程序可以包括对其他交互方式的访问,但其协议和性能问题的核心焦点是分布式超媒体。REST仅详细说明了体系结构中那些被认为对于Internet规模的分布式超媒体交互至关重要的部分。可以看到Web体系结构的改进领域,其中现有协议无法表达组件交互的所有潜在语义,并且可以在不改变体系结构功能的情况下用更有效的表单替换语法的细节。同样,可以将建议的扩展与REST进行比较,以确定它们是否适合架构。
在理想的世界中,软件系统的实现与其设计完全匹配。现代Web体系结构的某些功能确实与REST中的设计标准完全对应,例如使用URI作为资源标识符以及使用Internet媒体类型来标识表示数据格式。然而,现代Web协议的某些方面尽管存在架构设计,但由于遗留实验失败(但必须保留以便向后兼容)和开发人员在不知道架构风格的情况下部署的扩展。REST提供的模型不仅用于开发和评估新功能,还用于识别和理解损坏的功能。
万维网可以说是世界上最大的分布式应用程序。了解Web底层的关键架构原则可以帮助解释其技术成功,并可能导致其他分布式应用程序的改进,特别是那些适合相同或类似交互方法的应用程序。REST有助于提供现代Web软件体系结构背后的基本原理,以及如何在设计和评估真实软件系统时系统地应用软件工程原理的重要教训。
对于基于网络的应用程序,系统性能主要由网络通信决定。对于分布式超媒体系统,组件交互包括大粒度数据传输而不是计算密集型任务。REST风格是为满足这些需求而开发的。它专注于资源和表示的通用连接器接口,实现了组件的中间处理,缓存和可替代性,这反过来又允许基于Web的应用程序从1994年的100,000个请求扩展到1999年的600,000,000个请求。
REST体系结构样式已通过HTTP / 1.0 和HTTP / 1.1标准的六年开发,URI和相对URL标准的详细阐述以及几十个成功部署的验证在现代Web架构中独立开发的商业级软件系统。它既是设计指导的模型,也是Web协议架构扩展的酸性测试。
未来的工作将侧重于将架构指南扩展到开发HTTP / 1.x协议系列的替代品,使用更高效的标记化语法,但不会丢失REST标识的理想属性。无线设备的需求与REST背后的原理具有许多共同特征,将促进应用程序级协议设计和涉及活动中介的体系结构的进一步增强。还有一些兴趣是扩展REST以考虑可变请求优先级,差异化服务质量以及由连续数据流组成的表示,例如由广播音频和视频源生成的数据流。
参考
[1]Roy Thomas Fielding,Architectural Styles and the Design of Network-based Software Architectures[D], CALIFORNIA,UNIVERSITY OF CALIFORNIA,2000