在短短15年间,万维网已从研究人员的实验发展成为现代世界的技术Struts之一。 Web最初是为了使人们可以轻松地发布和链接到信息而发明的,它也已经发展成为软件应用程序的可行平台。 但是随着应用程序通过使用丰富的应用程序模型并生成个性化内容变得更加身临其境 ,其体系结构越来越违反Web的体系结构表示形式状态传输(REST)。 这些冲突往往会降低应用程序可伸缩性并增加系统复杂性。
新兴的Ajax Web客户端体系结构样式使沉浸式Web应用程序与REST体系结构样式保持和谐。 他们可以享受REST的理想属性,同时消除了应用程序违反REST原理时遇到的不良属性。 本文解释了Ajax和REST如何以及为什么在沉浸式Web应用程序中一起成功。
REST:Web的体系结构
尽管万维网是基于数十年的相关研究而建立的,但其有效的诞生日期是1990年12月,当时蒂姆·伯纳斯·李(Tim Berners-Lee)完成了网络主要组件的工作原型:统一资源标识符(URI),HTTP,HTML,浏览器和服务器。 Web的爆炸性采用超出了其开创者的所有希望。 在他著名的论文(见相关信息 ),罗伊菲尔丁描述当时的心情:
“尽管获得了成功,但Internet开发人员社区担心Web的使用量的快速增长以及早期HTTP的某些较差的网络特性会Swift超过Internet基础结构的容量,并导致整体崩溃。”
Fielding和其他人重新审查了Web的体系结构及其是否足以支持大规模扩展和使用。 重新检查的切实结果包括对重要标准(例如URI和HTTP)的更新。 重新审查的无形但有意义的结果是为超媒体应用程序确定了一种新的体系结构样式,菲尔丁将其命名为代表性状态转移(REST)。 菲尔丁断言,在Web上部署并接受REST设计约束并与之保持一致的组件将享受Web的理想特性。 他还警告说,与REST原则不同的Web组件将无法享受这些好处。
在早期,大多数网站和简单的Web应用程序自然都符合REST的原则。 但是随着Web应用程序变得更加身临其境,Web应用程序体系结构与REST的原理背道而驰,并遭受了后果。 沉浸式服务器端Web架构的问题很难解构,因为十年来使用这种架构样式已经树立了这样的信念:这些问题是Web应用程序架构固有的。 他们不是。 而是由服务器端Web应用程序体系结构样式引起的。 为了克服这些偏见,回顾一下导致当前事务状态的体系结构进展会很有帮助。 我将解释为什么既然创建Ajax应用程序在商业上可行,那么我们已经接受的许多假设不再成立。
Web应用程序的简要历史
伯纳斯·李(Berners-Lee)创建了Web,使研究人员可以跨距离共享文档,并在文档之间创建简单的链接,从而加快知识和思想的传播。 但是,URI标准的体系结构特征Swift实现了比静态文档更多的共享。
提供静态文档的网站
Web上最早的内容包括静态HTML文档以及指向其他静态文档的链接,如图1所示:
图1.提供静态文档的网站。
![提供静态文档的网站](https://i-blog.csdnimg.cn/blog_migrate/71bd09f4d43e23d5ae68e0c32bc309f0.png)
REST使检索静态文档的效率和可伸缩性令人难以置信,因为可以根据URI和上次修改日期轻松地对其进行缓存。 开发人员不久将超越静态文档,并支持提供动态内容。
早期的动态Web应用程序
Berners-Lee和其他人设计了URI标准,以支持资源的通用唯一标识,同时允许其表示(HTML,文本等)根据Web客户端(通常是Web浏览器)和Web服务器之间的协商而变化。 。 由于URI区分资源标识和资源的底层存储机制,因此Web开发人员可以创建程序来检查URI的语法并动态生成文档,结合预定义的UI元素和动态检索的数据(通常是从关系数据库中获取数据)(参见图2)。 。 尽管生成了文档,但是它们的缓存特性与静态文件的相同。
图2.提供HTML模板代码中嵌入的数据库记录的网站
![一个提供HTML模板代码中嵌入的数据库记录服务的网站](https://i-blog.csdnimg.cn/blog_migrate/5f99a321180d20cce847b714a8ea6a56.png)
这些早期应用程序的一个简单示例是大学目录Web应用程序。 该应用程序通常如下所示:
- 用户在Web表单中输入名称(例如
Bill Higgins
),然后单击Submit按钮。 - 该表单根据输入的名称创建URI,并从服务器请求该URI的内容(例如
GET http://psu.edu/Directory/Bill+Higgins
)。 - 服务器检查URI并生成一个包含学生的电话号码和地址的网页。
- 服务器将生成的页面发送回用户的浏览器。
这种交互的一个重要属性是等幂的 ,这意味着对于相同的请求,输出将是相同的,除非基础资源已更改(例如,如果Bill更改了他的电话号码)。 这意味着浏览器或代理服务器可以在本地缓存Bill Higgins文档,如果基础资源没有更改,则可以从本地缓存而不是远程服务器检索资源。 这种方法改善了用户感知的响应能力,并提高了整体系统效率和可伸缩性。 这些早期的动态Web应用程序运行良好,并为用户带来了很多信息。
沉浸式Web应用程序
下一代Web应用程序旨在高度身临其境,提供个性化内容和丰富的应用程序模型。 在接下来的十年中,Web开发人员成功创建了这些沉浸式应用程序。 一个典型的例子是Amazon.com电子商务网站。 当用户与Amazon Web应用程序进行交互时,它将创建复杂的自定义页面,以推荐特定产品,显示浏览历史记录并显示放置在用户购物车中的商品成本。
沉浸式服务器端应用程序和REST
沉浸式Web应用程序肯定有用,但是服务器端沉浸式Web应用程序样式与REST体系结构原理根本不协调。 特别是,它违反了REST的关键约束,并且无法利用REST的最重要优势之一,从而产生了一系列新问题。
违反“无状态服务器”约束
REST的“客户端无状态服务器”约束禁止服务器上的会话状态。 在此约束内进行设计可提高系统的可见性,可靠性和可伸缩性。 但是身临其境的服务器端Web应用程序希望为单个用户提供大量个性化设置,因此它们必须在两种设计之间进行选择。 首先是随每个客户端请求发送大量状态信息,以使每个请求都是上下文完整的,并且服务器可以保持无状态。 应用程序开发人员和中间件供应商都喜欢的第二种表面上更简单的解决方案是发送一个简单的用户身份令牌,并将此令牌与服务器端的“用户会话”对象相关联(参见图3)。 第二种设计直接违反了客户端无状态服务器约束。 它当然可以实现所需的用户功能(尤其是个性化),但是却给体系结构带来了巨大压力。
图3.包含大量服务器端会话状态的沉浸式服务器端Web应用程序
![沉浸式服务器端Web应用程序](https://i-blog.csdnimg.cn/blog_migrate/25e1b955e02ec414b8ce967ff7d0f9f3.png)
Java™Servlet的HttpSession API提供了这种压力的示例。 HttpSession使您可以将会话状态与特定用户相关联。 对于新手开发人员来说,此API似乎看似简单。 确实,您似乎可以在HttpSession中存储任何对象并将其拉出,而无需自己编写任何特殊的查找逻辑。 但是,当您开始在HttpSession中放置更多对象时,您开始注意到应用程序服务器使用越来越多的内存和处理资源。 很快,您决定需要在集群环境中部署应用程序,以帮助满足不断增长的资源需求。 然后,您意识到要使HttpSession在集群环境中工作,每个对象必须实现Java的Serializable
接口,以便可以在集群环境中的服务器之间传输会话数据。 然后,您必须确定在关闭/重新启动周期的情况下,应用服务器是否应保留会话数据。 很快,您开始质疑违反客户端无状态服务器约束是否真的是一个好主意。 (实际上,许多开发人员都不了解此约束。)
使分布式缓存成为不可能
身临其境的服务器端Web应用程序的第二个严重后果是,几乎不可能利用REST对资源缓存的一流支持。 引用Fielding的话:“添加缓存约束的优势在于,它们有可能部分或完全消除某些交互,通过减少一系列交互的平均延迟来提高效率,可伸缩性和用户感知的性能。但是,如果高速缓存中的陈旧数据与如果将请求直接发送到服务器时将获得的数据有显着差异,则高速缓存会降低可靠性。”
您可以将沉浸式Web应用程序看作几乎是一个真实的实体,它会根据用户提供的新输入,其他用户提供的新输入以及新的后端数据不断变化。 由于服务器必须基于多个用户与应用程序的交互来生成每个页面,因此几乎不可能两次生成相同的文档。 结果,Web浏览器或代理服务器无法缓存服务器资源。
有几种解决方案可用于解决资源不可缓存的问题。 一种是创建服务器端的细粒度资源缓存,以便服务器可以从预组装的部分而不是从基本元素(HTML和数据)构建粗粒度的页面。 但是问题仍然存在:每个请求都会导致不平凡的服务器处理,损害可伸缩性,并可能损害用户感知的响应时间。
无法提供可缓存资源的另一个结果是,动态性极强的Web应用程序必须明确禁止搜索引擎和其他“机器人”发出请求,因为每个请求都涉及昂贵的处理; 在RESTful应用程序中,您将为每个漫游器提供一次资源,并在随后的漫游器访问中发送一条简单的“未修改”消息。
但是,为什么在理论上可以将处理和内存需求分配给客户端的情况下,将如此多的资源消耗集中在超负荷的服务器上呢? 简单的答案是,鉴于传统的Web浏览器的限制,这是不可行的(请参阅不使用Ajax的客户端处理 )。 但是Ajax的体系结构样式允许开发人员将处理和状态需求分配给客户端。 继续阅读以了解为什么选择Ajax风格的体系结构的沉浸式应用程序可以重新获得与REST的和谐并享受其好处。
Ajax和REST
如您先前所见,传统的服务器端Web应用程序将服务器上的表示形式和动态数据元素结合在一起,并将完整HTML文档返回给浏览器。 Ajax应用程序的不同之处在于,它们的大多数UI和应用程序逻辑都驻留在浏览器中。 基于浏览器的应用程序代码将根据需要获取新的服务器数据,并将这些数据编织到当前页面中(请参阅参考资料 ,了解有关 Jesse James Garrett在Ajax上的开创性文章)。 表示和数据绑定的位置可能看起来像一个实现细节,但是这种差异导致完全不同的体系结构样式。
享受有状态的Web客户端
人们通常将Ajax应用程序描述为不需要每次单击都执行整页刷新的网页。 此描述是准确的,但其根本动机是整页刷新会分散注意力,并有损于令人愉悦的沉浸式用户体验。 从体系结构的角度来看,全页刷新甚至更麻烦,因为它们消除了将应用程序状态存储在客户端中的选项,从而导致设计决策,这些决策阻止了应用程序利用Web的许多最强大的体系结构设计点。
Ajax让您无需完全刷新即可与服务器交互的事实使有状态客户端的选择重新回到桌面上。 这对动态沉浸式Web应用程序的体系结构可能性具有深远的影响:因为应用程序资源和数据资源绑定已转移到客户端,所以这些应用程序可以享受两全其美的体验,即我们期望的沉浸式Web动态,个性化用户体验应用程序以及我们期望RESTful应用程序使用的简单,可扩展的体系结构。
缓存Ajax引擎
想象一下,Amazon.com被完全重新实现为一个纯Ajax应用程序-一个从服务器动态获取其所有数据的网页。 (亚马逊可能出于良好的商业原因而不想这样做,但这是另一篇文章的问题。)由于大量的UI和应用程序逻辑现在在客户端而不是服务器中运行,因此初始页面加载需要下载Amazon的页面和如Garrett所述,Ajax是“引擎”。 该引擎包含一堆应用程序逻辑(实现为JavaScript代码)和一些UI支架,这些支架随后将由从服务器异步获取的业务数据填充(请参见图4):
图4.沉浸式Ajax应用程序
![沉浸式Ajax应用程序](https://i-blog.csdnimg.cn/blog_migrate/840e80c4b234a5b30eae13df44e6ed93.png)
Ajax引擎的一个有趣的特征是,尽管它包含了大量的应用程序逻辑和表示框架元素,但是如果设计得当,它就不会包含业务数据或个性化内容。 应用程序和演示文稿在部署时被冻结。 在典型的Web环境中,应用程序资源可能仅每1-6个月更改一次。 这意味着分离应用程序资源和数据资源的Ajax引擎是高度可缓存的。
Dojo Toolkit是一个很好的示例(请参阅参考资料 )。 Dojo提供了构建时工具来创建包含您的应用程序的所有逻辑,表示形式和样式的压缩JavaScript文件。 因为它最终只是文件,所以Web浏览器可以对其进行缓存,这意味着您第二次访问启用了Dojo的Web应用程序时,很可能是从浏览器的缓存而不是从服务器加载Ajax引擎。 将此与高度沉浸的服务器端Web应用程序进行比较,在该应用程序中,每个请求都会导致大量的服务器处理,因为您的浏览器和网络中介无法缓存不断变化的资源。
因为Ajax应用程序引擎只是一个文件,所以它也是可代理的 。 在大型公司Intranet上,只有一个员工可以下载该应用程序的Ajax引擎的特定版本,而其他所有人都只是从Intranet网关中获取缓存的副本。
因此,就应用程序资源而言,精心设计的Ajax应用程序引擎符合REST原则,与服务器端Web应用程序相比,具有明显的可伸缩性优势。
缓存Ajax数据
好的,用户浏览到Ajax网站并加载Ajax应用程序引擎,最好是从浏览器的缓存中加载,如果不是,则从本地代理服务器加载。 那业务数据呢? 由于应用程序逻辑和状态驻留在浏览器上并在浏览器上执行,因此应用程序与服务器的交互变得与传统Web应用程序非常不同。 无需获取合并的内容页面,它仅需要获取业务数据。
回到Amazon.com示例,假设您单击一个链接以查看有关设计模式的书的信息。 在Amazon.com的当前应用程序中,链接单击操作会发送各种信息,以标识所请求的资源。 它还发送各种会话状态工件,使服务器可以创建一个新页面,其中包括先前的会话状态(例如最近查看的项目),个性化信息(例如“您在1999年购买了这本书”)和实际的业务资源本身。 该应用程序非常动态且高度个性化-但不可缓存且不可自然扩展(尽管如Amazon所示,这些架构问题可通过数百万美元的基础架构工程来解决)。 现在,在应用程序的(虚构)Ajax版本中考虑此操作。 对于“最近查看的项目”,不需要进行任何处理。 这只是页面中已经存在的信息,当您单击链接时这些信息不会消失。 与设计模式书有关的两个请求可能是:
-
/Books/0201633612
(其中0201633612
是设计模式书的ISBN号) -
/PurchaseHistory/0201633612/bhiggins@us.ibm.com
假设的第一个请求返回有关书籍的信息(作者,书名,描述等); 它不包含用户特定的数据。 缺少特定于用户的数据意味着随着更多用户请求相同的资源,他们很可能会从Internet上的中间节点而不是从原始服务器检索其缓存版本。 此特征减少了服务器和整个网络的负载。 另一方面,第二个请求包含特定于用户的信息(Bill Higgins对此书的购买历史)。 因为此数据包括个性化信息,所以只有一个用户应该可以成功地从此URI检索和缓存数据。 尽管此个性化数据不具有非个性化数据的可伸缩性,但重要的是该信息是从不同的URL检索的,因此具有积极的特征,即它不会干扰其他应用程序和数据资源的缓存。可缓存的 。
Ajax和健壮性
Ajax架构样式的另一个好处是能够轻松处理服务器故障。 如前所述,具有沉浸式用户体验的服务器端Web应用程序倾向于在服务器上保留大量用户会话状态。 如果服务器出现故障,会话状态就会消失,并且用户会遇到奇怪的浏览器行为(“为什么我回到首页?购物车中的物品在哪里?”)。 在具有状态客户端和无状态服务的Ajax应用程序中,服务器崩溃/重启对用户是完全透明的,因为服务器崩溃不会影响驻留在用户浏览器中的会话状态。 无状态服务的行为是幂等的,并且仅由用户请求的内容确定。
承诺与问题
对于我称为沉浸式Web应用程序的Web应用程序类别,在用户体验,响应能力和可伸缩性方面,经过精心设计的Ajax / REST应用程序远远优于传统的服务器级Web应用程序。 但是,架构风格的运行时特征并不是软件项目和Web应用程序成功的唯一决定因素。 创建Ajax / REST应用程序存在一些棘手的非运行时问题,包括确定如何为应用程序采用Ajax(如果有的话),大规模JavaScript开发问题,文化问题和打包问题。 在第2部分中,我将讨论不同的采用选项和组织注意事项,以提高您使用Ajax成功的机会。
致谢
我要感谢我的同事Chris Mitchell,Josh Staiger,Pat Mueller,Scott Rich和Simon Archer在本文中提供的有益的技术反馈。 我还要感谢Redmonk分析公司的James Governor和Steve O'Grady,他们最初是挑战我考虑使用REST风格的Web服务的。
翻译自: https://www.ibm.com/developerworks/java/library/wa-ajaxarch/index.html