REST架构风格
李锟 人气:2引子
在移动互联网、云计算迅猛发展的今天,作为一名Web 开发者,如果您还没听说过“REST”这个buzzword,显然已经落伍了。夸张点说,甚至“出了门都不好意思跟别人打招呼”。尽管如此,对于REST 这个泊来品的理解,大多数人(包括一些资深的架构师)仍然停留在“盲人摸象”的阶段。常常听到各种各样关于REST 的说法,例如:有人说:“我们这套新的API 决定不用Web Service(SOAP+WSDL),而是直接使用HTTP+JSON,也就是用RESTful 的方式来开发。” 不用SOAP,甚至也不用XML,就自动变成了RESTful 了。还有人认为:REST 与传统的Web Service 其实没有本质区别,只是对于URI 的构造方式提出了更多要求,而这些要求Web Service 完全都可以实现。潜台词是:既生瑜,何生亮。Web Service 已经足够好了,干嘛还要再折腾什么REST。这些对于REST 的不同说法,果真如此吗?REST 究竟是什么?是一种新的技术、一种新的架构、还是一种新的规范?
对于这些问题笔者先不解答,为了深入理解REST 是什么,我们需要回顾一下Web 发展的最初年代,从源头上讲讲REST 是怎么得来的。
Web 技术发展与 REST 的由来
Web(万维网 World Wide Web 的简称)是个包罗万象的万花筒,不同的人从不同的角度观察,对于 Web 究竟是什么会得出大不相同的观点。作为 Web 开发者,我们需要从技术上来理解 Web。从技术架构层面上看,Web 的技术架构包括了四个基石:
- URI
- HTTP
- HyperText(除了 HTML 外,也可以是带有超链接的 XML 或 JSON)
- MIME
这四个基石相互支撑,促使 Web 这座宏伟的大厦以几何级数的速度发展了起来。在这四个基石之上,Web 开发技术的发展可以粗略划分成以下几个阶段:
- 静态内容阶段:在这个最初的阶段,使用 Web 的主要是一些研究机构。Web 由大量的静态 HTML 文档组成,其中大多是一些学术论文。Web 服务器可以被看作是支持超文本的共享文件服务器。
- CGI 程序阶段:在这个阶段,Web 服务器增加了一些编程 API。通过这些 API 编写的应用程序,可以向客户端提供一些动态变化的内容。Web 服务器与应用程序之间的通信,通过 CGI(Common Gateway Interface)协议完成,应用程序被称作 CGI 程序。
- 脚本语言阶段:在这个阶段,服务器端出现了 ASP、PHP、JSP、ColdFusion 等支持 session 的脚本语言技术,浏览器端出现了 Java Applet、JavaScript 等技术。使用这些技术,可以提供更加丰富的动态内容。
- 瘦客户端应用阶段:在这个阶段,在服务器端出现了独立于 Web 服务器的应用服务器。同时出现了 Web MVC 开发模式,各种 Web MVC 开发框架逐渐流行,并且占据了统治地位。基于这些框架开发的 Web 应用,通常都是瘦客户端应用,因为它们是在服务器端生成全部的动态内容。
- RIA 应用阶段:在这个阶段,出现了多种 RIA(Rich Internet Application)技术,大幅改善了 Web 应用的用户体验。应用最为广泛的 RIA 技术是 DHTML+Ajax。Ajax 技术支持在不刷新页面的情况下动态更新页面中的局部内容。同时诞生了大量的 Web 前端 DHTML 开发库,例如 Prototype、Dojo、ExtJS、jQuery/jQuery UI 等等,很多开发库都支持单页面应用(Single Page Application)的开发。其他的 RIA 技术还有 Adobe 公司的 Flex、微软公司的 Silverlight、Sun 公司的 JavaFX(现在为 Oracle 公司所有)等等。
- 移动 Web 应用阶段:在这个阶段,出现了大量面向移动设备的 Web 应用开发技术。除了 Android、iOS、Windows Phone 等操作系统平台原生的开发技术之外,基于 HTML5 的开发技术也变得非常流行。
从上述 Web 开发技术的发展过程看,Web 从最初其设计者所构思的主要支持静态文档的阶段,逐渐变得越来越动态化。Web 应用的交互模式,变得越来越复杂:从静态文档发展到以内容为主的门户网站、电子商务网站、搜索引擎、社交网站,再到以娱乐为主的大型多人在线游戏、手机游戏。
在互联网行业,实践总是走在理论的前面。Web 发展到了 1995 年,在 CGI、ASP 等技术出现之后,沿用了多年、主要面向静态文档的 HTTP/1.0 协议已经无法满足 Web 应用的开发需求,因此需要设计新版本的 HTTP 协议。在 HTTP/1.0 协议专家组之中,有一位年轻人脱颖而出,显示出了不凡的洞察力,后来他成为了 HTTP/1.1 协议专家组的负责人。这位年轻人就是 Apache HTTP 服务器的核心开发者 Roy Fielding,他还是 Apache 软件基金会的合作创始人。
Roy Fielding 和他的同事们在 HTTP/1.1 协议的设计工作中,对于 Web 之所以取得巨大成功,在技术架构方面的因素做了一番深入的总结。Fielding 将这些总结纳入到了一套理论框架之中,然后使用这套理论框架中的指导原则,来指导 HTTP/1.1 协议的设计方向。HTTP/1.1 协议的第一个草稿是在 1996 年 1 月发布的,经过了三年多时间的修订,于 1999 年 6 月成为了 IETF 的正式规范(包括了 RFC 2616 以及用于对客户端做身份认证的 RFC 2617)。HTTP/1.1 协议设计的极为成功,以至于发布之后整整 10 年时间里,都没有多少人认为有修订的必要。用来指导 HTTP/1.1 协议设计的这套理论框架,最初是以备忘录的形式在专家组成员之间交流,除了 IETF/W3C 的专家圈子,并没有在外界广泛流传。Fielding 在完成 HTTP/1.1 协议的设计工作之后,回到了加州大学欧文分校继续攻读自己的博士学位。第二年(2000 年)在他的博士学位论文 Architectural Styles and the Design of Network-based Software Architectures 中,Fielding 更为系统、严谨地阐述了这套理论框架,并且使用这套理论框架推导出了一种新的架构风格,并且为这种架构风格取了一个令人轻松愉快的名字“REST”——Representational State Transfer(表述性状态转移)的缩写。
在笔者看来,Fielding 这篇博士论文在 Web 发展史上的价值,不亚于 Web 之父 Tim Berners-Lee 关于超文本的那篇经典论文。然而遗憾的是,这篇博士论文在诞生之后的将近 5 年时间里,一直没有得到足够的重视。例如 Web Service 相关规范 SOAP/WSDL 的设计者们,显然不大理解 REST 是什么,HTTP/1.1 究竟是一个什么样的协议、为何要设计成这个样子。
这种情况在 2005 年之后有了很大的改善,随着 Ajax、Ruby on Rails 等新的 Web 开发技术的兴起,在 Web 开发技术社区掀起了一场重归 Web 架构设计本源的运动,REST 架构风格得到了越来越多的关注。在 2007 年 1 月,支持 REST 开发的 Ruby on Rails 1.2 版正式发布,并且将支持 REST 开发作为 Rails 未来发展中的优先内容。Ruby on Rails 的创始人 DHH 做了一个名为“World of Resources”的精彩演讲,DHH 在 Web 开发技术社区中的强大影响力,使得 REST 一下子处在 Web 开发技术舞台的聚光灯之下。
今天,各种流行的 Web 开发框架,几乎没有不支持 REST 开发的了。大多数 Web 开发者都是通过阅读某种 REST 开发框架的文档,以及通过一些例子代码来学习 REST 开发的。然而,通过例子代码来学习 REST 有非常大的局限性。因为 REST 并不是一种具体的技术,也不是一种具体的规范,REST 其实是一种内涵非常丰富的架构风格。通过例子代码来学习 REST,除了学习到一种有趣的 Web 开发技术之外,并不能全面深入的理解 REST 究竟是什么。甚至还会误以为这些简单的例子代码就是 REST 本身,REST 不过是一种简单的 Web 开发技术而已。就像盲人摸象一样,有的人摸到了象鼻子、有的人摸到了象耳朵、有的人摸到了象腿、有的人摸到了象尾巴。他们都坚信自己感觉到的大象,才是最真实的大象,而其他人的感觉都是错误的。
对于不理解 REST 的 Web 开发者,人们习惯于展示一些例子代码来让他们理解 REST,笔者不赞同上述做法。如果 Web 开发者想要深入理解 REST 是什么,就很难避开 Fielding 的这篇博士论文。笔者在本文中对于 REST 是什么的介绍,也是基于 Fielding 的博士论文的。尽管如此,笔者强烈建议本文的读者亲自去通读一下 Fielding 的博士论文,就像想要了解孔子的思想应该直接去读《论语》等著作,而不是首先去读其他人的转述一样。笔者在本文中也仅仅是努力不做一个把经书念错了的歪嘴和尚而已。那么,下面我们言归正传。
在 Fielding 的这篇名为 Architectural Styles and the Design of Network-based Software Architectures 的博士论文(中文版名为《架构风格与基于网络的软件架构设计》)中,提出了一整套基于网络的软件(即所谓的“分布式应用”)的设计方法,值得所有分布式应用的开发者仔细阅读、深入体会。
在论文的前三章中,Fielding 在批判性继承前人研究成果的基础上,建立起来一整套研究和评价软件架构的方法论。这套方法论的核心是“架构风格”这个概念。架构风格是一种研究和评价软件架构设计的方法,它是比架构更加抽象的概念。一种架构风格是由一组相互协作的架构约束来定义的。架构约束是指软件的运行环境施加在架构设计之上的约束。
在论文的第四章中,Fielding 研究了 Web 这样一个分布式系统对于软件架构设计提出了哪些需求。在第五章中,Fielding 将第四章 Web 提出的需求具体化为一些架构约束,通过逐步添加各种架构约束,推导出来了 REST 这种新的架构风格。
REST 架构风格的推导过程
图 1:REST 所继承的架构风格约束
在图1 中,每一个椭圆形里面的缩写词代表了一种架构风格,而每一个箭头边的单词代表了一种架构约束。
REST 架构风格最重要的架构约束有 6 个:
- 客户 - 服务器(Client-Server)
通信只能由客户端单方面发起,表现为请求 - 响应的形式。
- 无状态(Stateless)
通信的会话状态(Session State)应该全部由客户端负责维护。
- 缓存(Cache)
响应内容可以在通信链的某处被缓存,以改善网络效率。
- 统一接口(Uniform Interface)
通信链的组件之间通过统一的接口相互通信,以提高交互的可见性。
- 分层系统(Layered System)
通过限制组件的行为(即,每个组件只能“看到”与其交互的紧邻层),将架构分解为若干等级的层。
- 按需代码(Code-On-Demand,可选)
支持通过下载并执行一些代码(例如 Java Applet、Flash 或 JavaScript),对客户端的功能进行扩展。
在论文中推导出的 REST 架构风格如下图所示:
图 2:REST 架构风格
而HTTP/1.1 协议作为一种REST 架构风格的架构实例,其架构如下图所示:
图3:一个基于REST 的架构的过程视图
用户代理处在三个并行交互(a、b 和c)的中间。用户代理的客户端连接器缓存无法满足请求,因此它根据每个资源标识符的属性和客户端连接器的配置,将每个请求路由到资源的来源。请求(a)被发送到一个本地代理,代理随后访问一个通过DNS 查找发现的缓存网关,该网关将这个请求转发到一个能够满足该请求的来源服务器,服务器的内部资源由一个封装过的对象请求代理(object request broker)架构来定义。请求(b)直接发送到一个来源服务器,它能够通过自己的缓存来满足这个请求。请求(c)被发送到一个代理,它能够直接访问WAIS(一种与Web 架构分离的信息服务),并将WAIS 的响应翻译为一种通用的连接器接口能够识别的格式。每一个组件只知道与它们自己的客户端或服务器连接器的交互;整个过程拓扑是我们的视图的产物。
通过比较图2 和图3,读者不难发现这两张图中的架构是高度一致的。对于HTTP/1.1 协议为何要设计成这个样子,读者想必已经有所领悟。
在论文的第六章中,Fielding 对于到2000 年为止在Web 基础架构协议的设计和开发方面的一些经验教训进行了深入的分析。其中,“HTTP 不是RPC”、“HTTP 不是一种传输协议”两部分值得读者反复阅读。时至13 年之后的今日,对于HTTP 协议的误解仍然广泛存在。
以上简要介绍了Fielding 博士论文中的内容。为了帮助读者仔细阅读Fielding 的博士论文,笔者整理了一套Fielding 博士论文的导读。
REST 详解
REST 究竟是什么?因为 REST 的内涵非常丰富,所以很难用一两句话解释清楚这个问题。
首先,REST 是 Web 自身的架构风格。REST 也是 Web 之所以取得成功的技术架构方面因素的总结。REST 是世界上最成功的分布式应用架构风格(成功案例:Web,还不够吗?)。它是为 运行在互联网环境 的 分布式 超媒体系统量身定制的。互联网环境与企业内网环境有非常大的差别,最主要的差别是两个方面:
- 可伸缩性需求无法控制:并发访问量可能会暴涨,也可能会暴跌。
- 安全性需求无法控制:无法控制客户端发来的请求的格式,很可能会是恶意的请求。
而所谓的“超媒体系统”,即,使用了超文本的系统。可以把“超媒体”理解为超文本 + 媒体内容。
REST 是 HTTP/1.1 协议等 Web 规范的设计指导原则,HTTP/1.1 协议正是为实现 REST 风格的架构而设计的。新的 Web 规范,其设计必须符合 REST 的要求,否则整个 Web 的体系架构会因为引入严重矛盾而崩溃。这句话不是危言耸听,做个类比,假如苏州市政府同意在市区著名园林的附近大型土木,建造大量具有后现代风格的摩天大楼,那么不久之后世界闻名的苏州园林美景将不复存在。
上述这些关于“REST 是什么”的描述,可以总结为一句话:REST 是所有 Web 应用都应该遵守的架构设计指导原则。当然,REST 并不是法律,违反了 REST 的指导原则,仍然能够实现应用的功能。但是违反了 REST 的指导原则,会付出很多代价,特别是对于大流量的网站而言。
要深入理解 REST,需要理解 REST 的五个关键词:
- 资源(Resource)
- 资源的表述(Representation)
- 状态转移(State Transfer)
- 统一接口(Uniform Interface)
- 超文本驱动(Hypertext Driven)
什么是资源?
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个 URI 来标识。URI 既是资源的名称,也是资源在 Web 上的地址。对某个资源感兴趣的客户端应用,可以通过资源的 URI 与其进行交互。
什么是资源的表述?
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端 - 服务器端之间转移(交换)。资源的表述可以有多种格式,例如 HTML/XML/JSON/ 纯文本 / 图片 / 视频 / 音频等等。资源的表述格式可以通过协商机制来确定。请求 - 响应方向的表述通常使用不同的格式。
什么是状态转移?
状态转移(state transfer)与状态机中的状态迁移(state transition)的含义是不同的。状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
什么是统一接口?
REST 要求,必须通过统一的接口来对资源执行各种操作。对于每个资源只能执行一组有限的操作。以 HTTP/1.1 协议为例,HTTP/1.1 协议定义了一个操作资源的统一接口,主要包括以下内容:
- 7 个 HTTP 方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
- HTTP 头信息(可自定义)
- HTTP 响应状态代码(可自定义)
- 一套标准的内容协商机制
- 一套标准的缓存机制
- 一套标准的客户端身份认证机制
REST 还要求,对于资源执行的操作,其操作语义必须由 HTTP 消息体之前的部分完全表达,不能将操作语义封装在 HTTP 消息体内部。这样做是为了提高交互的可见性,以便于通信链的中间组件实现缓存、安全审计等等功能。
什么是超文本驱动?
“超文本驱动”又名“将超媒体作为应用状态的引擎”(Hypermedia As The Engine Of Application State,来自 Fielding 博士论文中的一句话,缩写为 HATEOAS)。将 Web 应用看作是一个由很多状态(应用状态)组成的有限状态机。资源之间通过超链接相互关联,超链接既代表资源之间的关系,也代表可执行的状态迁移。在超媒体之中不仅仅包含数据,还包含了状态迁移的语义。以超媒体作为引擎,驱动 Web 应用的状态迁移。通过超媒体暴露出服务器所提供的资源,服务器提供了哪些资源是在运行时通过解析超媒体发现的,而不是事先定义的。从面向服务的角度看,超媒体定义了服务器所提供服务的协议。客户端应该依赖的是超媒体的状态迁移语义,而不应该对于是否存在某个 URI 或 URI 的某种特殊构造方式作出假设。一切都有可能变化,只有超媒体的状态迁移语义能够长期保持稳定。
一旦读者理解了上述 REST 的五个关键词,就很容易理解 REST 风格的架构所具有的 6 个的主要特征:
- 面向资源(Resource Oriented)
- 可寻址(Addressability)
- 连通性(Connectedness)
- 无状态(Statelessness)
- 统一接口(Uniform Interface)
- 超文本驱动(Hypertext Driven)
这 6 个特征是 REST 架构设计优秀程度的判断标准。其中,面向资源是 REST 最明显的特征,即,REST 架构设计是以资源抽象为核心展开的。可寻址说的是:每一个资源在 Web 之上都有自己的地址。连通性说的是:应该尽量避免设计孤立的资源,除了设计资源本身,还需要设计资源之间的关联关系,并且通过超链接将资源关联起来。无状态、统一接口是 REST 的两种架构约束,超文本驱动是 REST 的一个关键词,在前面都已经解释过,就不再赘述了。
从架构风格的抽象高度来看,常见的分布式应用架构风格有三种:
- 分布式对象(Distributed Objects,简称 DO)
架构实例有 CORBA/RMI/EJB/DCOM/.NET Remoting 等等
- 远程过程调用(Remote Procedure Call,简称 RPC)
架构实例有 SOAP/XML-RPC/Hessian/Flash AMF/DWR 等等
- 表述性状态转移(Representational State Transfer,简称 REST)
架构实例有 HTTP/WebDAV
DO 和 RPC 这两种架构风格在企业应用中非常普遍,而 REST 则是 Web 应用的架构风格,它们之间有非常大的差别。
REST 与 DO 的差别
- REST 支持抽象(即建模)的工具是资源,DO 支持抽象的工具是对象。在不同的编程语言中,对象的定义有很大差别,所以 DO 风格的架构通常都是与某种编程语言绑定的。跨语言交互即使能实现,实现起来也会非常复杂。而 REST 中的资源,则完全中立于开发平台和编程语言,可以使用任何编程语言来实现。
- DO 中没有统一接口的概念。不同的 API,接口设计风格可以完全不同。DO 也不支持操作语义对于中间组件的可见性。
- DO 中没有使用超文本,响应的内容中只包含对象本身。REST 使用了超文本,可以实现更大粒度的交互,交互的效率比 DO 更高。
- REST 支持数据流和管道,DO 不支持数据流和管道。
- DO 风格通常会带来客户端与服务器端的紧耦合。在三种架构风格之中,DO 风格的耦合度是最大的,而 REST 的风格耦合度是最小的。REST 松耦合的源泉来自于统一接口 + 超文本驱动。
REST 与 RPC 的差别
- REST 支持抽象的工具是资源,RPC 支持抽象的工具是过程。REST 风格的架构建模是以名词为核心的,RPC 风格的架构建模是以动词为核心的。简单类比一下,REST 是面向对象编程,RPC 则是面向过程编程。
- RPC 中没有统一接口的概念。不同的 API,接口设计风格可以完全不同。RPC 也不支持操作语义对于中间组件的可见性。
- RPC 中没有使用超文本,响应的内容中只包含消息本身。REST 使用了超文本,可以实现更大粒度的交互,交互的效率比 RPC 更高。
- REST 支持数据流和管道,RPC 不支持数据流和管道。
- 因为使用了平台中立的消息,RPC 风格的耦合度比 DO 风格要小一些,但是 RPC 风格也常常会带来客户端与服务器端的紧耦合。支持统一接口 + 超文本驱动的 REST 风格,可以达到最小的耦合度。
比较了三种架构风格之间的差别之后,从面向实用的角度来看,REST 架构风格可以为 Web 开发者带来三方面的利益:
- 简单性
采用 REST 架构风格,对于开发、测试、运维人员来说,都会更简单。可以充分利用大量 HTTP 服务器端和客户端开发库、Web 功能测试 / 性能测试工具、HTTP 缓存、HTTP 代理服务器、防火墙。这些开发库和基础设施早已成为了日常用品,不需要什么火箭科技(例如神奇昂贵的应用服务器、中间件)就能解决大多数可伸缩性方面的问题。
- 可伸缩性
充分利用好通信链各个位置的 HTTP 缓存组件,可以带来更好的可伸缩性。其实很多时候,在 Web 前端做性能优化,产生的效果不亚于仅仅在服务器端做性能优化,但是 HTTP 协议层面的缓存常常被一些资深的架构师完全忽略掉。
- 松耦合
统一接口 + 超文本驱动,带来了最大限度的松耦合。允许服务器端和客户端程序在很大范围内,相对独立地进化。对于设计面向企业内网的 API 来说,松耦合并不是一个很重要的设计关注点。但是对于设计面向互联网的 API 来说,松耦合变成了一个必选项,不仅在设计时应该关注,而且应该放在最优先位置。
有的读者可能会问:“你说了这么多,REST 难道就没有任何缺点了吗?”当然不是,正如 Fielding 在博士论文中阐述的那样,评价一种软件架构的优劣,不能脱离开软件的具体运行环境。永远不存在适用于任何运行环境的、包治百病的银弹式架构。笔者在前面强调过 REST 是一种为运行在互联网环境中的 Web 应用量身定制的架构风格。REST 在互联网这个运行环境之中已经占据了统治地位,然而,在企业内网运行环境之中,REST 还会面临 DO、RPC 的巨大挑战。特别是一些对实时性要求很高的应用,REST 的表现不如 DO 和 RPC。所以需要针对具体的运行环境来具体问题具体分析。但是,REST 可以带来的上述三方面的利益即使在开发企业应用时,仍然是非常有价值的。所以 REST 在企业应用开发,特别是在 SOA 架构的开发中,已经得到了越来越大的重视。本专栏将有一篇文章专门介绍 REST 在企业级应用中与 SOA 的结合。
到了这里,“REST 究竟是什么”这个问题笔者就解答完了。本文开头那些说法是否正确,笔者还是笑而不语,读者此时应该已经有了自己的判断。在接下来的 REST 系列文章中,我将会为读者澄清一些关于 HTTP 协议和 REST 的常见误解。
参考资料:
加载全部内容