文章: REST会是SOA的未来吗?
好像无论我们到哪儿都能听到这样的说法:REST将会是SOA的未来。很多刊物也将REST和SOAP与WS* [1]标准进行比较,但这些比较看起来都太过简单了。近来出现了两种较为主流的方法——本真REST(true REST)以及将REST作为面向服务的技术方法(又称REST Web服务 [2])。本文讨论的重点为:是否其中一种方法能够改进SOA实现。
面向SOA的本真REST
本真REST当然是对面向资源架构的一种实现,而并非一种纯粹的技术决策。所以当讨论本真REST时,真正应该讨论的问题是:其基础支撑——面向资源的架构(ROA)——是否真的适合作为你的SOA实现。
为正确评估该问题,让我们首先回想一下SOA的架构风格,它是基于企业业务架构的功能性分解,并且引入了两个高层次的抽象:企业业务服务和业务流程。企业业务服务代表的是现有IT能力(和企业的业务功能相一致)。业务流程编排业务服务,并定义业务的整体功能。
而REST是一组被称之为面向资源架构(ROA)的架构准则。ROA构建在资源这一概念之上;每个资源都是一个能够直接访问的分布式组件,可通过一个标准的、通用的接口来处理。所以,面向资源的架构(ROA)其最根本的还是一种基于资源的分解 [3]。
为了评估本真REST是否适用于面向SOA的实现,我们真正需要回答的问题是,“服务和资源之间到底是什么关系?”
服务 vs. 资源
何为服务?
在最简单的情况下,服务可以被定义为一个自包含、独立开发、可部署、可管理和可维护的软件实现,它从整体上为企业提供特定的与业务相关的功能,并且在设计上是“可集成的”。“服务”可以通过动词(verb)来定义(例如,“ 验证客户信用积分”,这描述了服务实现的业务功能)。
服务并不是某个编程结构或一组APIs,而是一个用于实现企业解决方案的架构(设计单元、实现以及维护)和部署构件。服务接口(尤其对某个给定的服务而言)定义服务功能,并且可由多种方式实现。存在两种基本的定义服务接口的方法——RPC风格和消息(messaging)风格,RPC风格实现使用服务调用语义并且通过服务接口中的一组参数来定义。而消息风格的服务接口被有效地固定(本质上只需要进行“执行”操作)使用XML文档作为输入和输出(这和GoF设计模式非常相似)。在这种情况下,服务语义是由输入和输出消息的语义来确定 [4]。
过去,服务通常被定义为一组方法的集合,但正如参考文献 [2]中解释的那样,这些方法彼此相互独立 [5],但作为整体它们共享同一个命名空间,这样简化了对服务的管理。
何为资源?
在最简单的情况下,资源可以被定义为一个可直接访问的、独立开发的、可部署的、可管理的和可维护的软件构件,它支持特定的数据。资源可以通过名词(noun)来定义,比如“医生的 预约”就描述了资源提供的数据。某一资源也可以和其他资源相关联并为它们提供引用(链接)。实际上,一个资源就类似于一个对象 [6],不过它是带有预定义(CRUD)接口语义的对象。
REST中的语义基于HTTP操作集,如下所示 [5]:
- createResource——创建一个新的资源(以及相应的唯一标示)– PUT
- getResourceRepresentation——获取资源信息– GET
- deleteResource ——删除资源(可选地包括相关联的资源)– DELETE(只是引用的资源),POST(当需要删除相关联的资源时使用)
- modifyResource——更改资源— POST
- getMetaInforatmion——取得资源元数据信息—HEAD
资源通过两部分定义:资源URL和资源所提供的所有操作上定义的输入/输出参数 [7]。这和服务不同,服务的方法之间是完全独立,并且能够以独立端点(endpoints)的方式部署,而资源上的方法遵循OO语义,这意味着所有的方法(除 createResource以外)都必须依附于底层的某个资源(同一个URL)。
资源和服务之间的根本差异
基于上述对资源和服务的定义,凭直觉它们显然是不同的。我们先继续深究这些差别,然后再讨论它们是如何对最终架构产生影响的。
正如文献 [6]中描述的:
REST不仅不是面向服务的,相反,面向服务和REST风马牛不相及
文献 [7]中进一步阐明了二者之间的区别:
如果把WS-*比作是互联网世界的RPC,那么REST就是互联网世界的数据库管理系统(DBMS)……传统的基于SOA的集成表现了不同软件构件之间通过各种过程或方法进行交互。REST有效地将每个软件构件看作一组数据库表,而这些构件之间使用SELECT, INSERT, UPDATE和DELETE来通信。(或如你所想的使用GET, PUT, POST, DELETE)。那业务逻辑放在哪里呢?在存储过程中?不太对,其实在触发器中。
这里我们用J2EE打个稍微不太恰当的比方。我们把服务想象成无状态会话bean,而资源想象成实体bean。
服务(或会话beans)作为控制器控制执行所需的操作,不管底层是哪个资源。打个比方,某个支出账户服务可能会用到账户ID、支出金额和支出所需账户。这样的服务可以支出任何现有账户。
资源(或实体bean)充当数据访问机制,其面对给定数据类型的某一实例。比如,为了从某一账户支出,需要先找到这一账户相关的信息,然后才能更新它,从而向所需账户进行支出。另外提醒一下,与能实现任意所需的方法的实体bean不同的是,一个REST资源只有一个 更改资源的方法。这意味着真实的业务操作——支出——只能编码成消息请求的一部分。
区别引出的结论
综上所述,不可能使用本真REST来构建SOA系统。构建系统可以,但一定不是SOA。两者都可以从与业务一致的分解入手,但是由于各自使用截然不同的分解方法,它们最终得到的也是基于不同组件和连接器的完全不同的架构风格 [8]。
仅仅因为它们都试图解决同一个问题——业务与IT对齐,并且都基于业务驱动的分解,并不能表明最终的架构风格也是一样的。
另一个问题在于能否可能使用本真REST来构建一个完整的系统。鉴于上述理由,这个问题等价于能否可能只使用数据库或实体bean来构建一个完整的系统。当然你可以了,但是需要以存储过程(重写方法的本意)的方式增加处理代码,或者触发器(完成基于数据变化的后置处理)。这同样适用于本真REST实现—你只有通过改变modifyResource方法的本意(通常使用命令行模式)来实现不止数据更新这个方法。
因此,某个基于REST的实现和本真REST是大相径庭的;一般来说其包含了至少一些REST Web服务的元素。那么REST Web服务是什么呢?
REST Web服务
REST Web服务方法是指单纯使用REST技术作为通信手段来构建SOA的一种方法。在这种情况下,服务由SOA风格的分解来定义,而基于REST的Web服务 [9]作为通信。
虽然一般也被称为REST,这种方法其实和本真REST没有一点关系,倒是和POX(plain old XML over HTTP)很类似,不过与POX不同的是,它不仅支持XML,还支持其他数据类型,比如JSON(JavaScript Object Notation)、ATOM、二进制数据块。而且,它不像POX那样通常只基于GET和PUT,它基于更多的HTTP方法。
归功于Web的优势和Ajax技术的遍地开花,使用JSON逐渐变成主流的方法;大部分流行的浏览器都内置对JSON支持。由于在JavaScript中处理XML(尤其是带有很多命名空间)并不是一件容易的事,所以,Web实现使用基于JSON的REST Web服务要容易的多。面向Web交互的REST Web服务的扩增导致了这些技术的日益流行和广泛传播。
真正的差异是什么?
描述SOAP和REST区别的出版刊物通常会指出如下REST Web服务的优点,比如 [11]:
- 轻量级——无需太多额外的XML标记
- 人工可读的结果集
- 易于构建——无需工具支持
虽然这些区别很重要(我随后会再详细讨论),但是SOAP和REST最主要的区别在于REST是直接实现于HTTP协议之上,而SOAP引人了一个抽象层(SOAP消息传递),这可以在任何传输协议之上实现。标准化SOAP绑定目前存在于HTTP、SMTP和JMS之上,而非标准化绑定已经在其他一些协议解决方案实现了。这层额外的抽象层(提供协议和基于SOAP实现之间的解耦)是造成SOAP和REST Web服务区别的根源。
对于这一抽象层的看法很大程度上取决于不同的人。REST阵营认为它是过度设计的产物,并声称没有提供任何实际价值。他们声称HTTP已经提供了服务交互实现必需的所有特点。而SOAP阵营,从另一方面,争辩道HTTP并不是服务交互(尤其在企业内部)通常所需的唯一协议,而设计一个方便的、可扩展 [10]的抽象层对构建健壮的、功能丰富的服务交互是很有必要的。
虽然两种观点都有其可取之处,但我认为把SOA实现限制到单一协议,即HTTP,实际操作起来不太可行。诚然,HTTP是无处不在,并且其使用方法一般也无需投资额外的基础设施,但是HTTP是不可靠的(HTTP-R没有广泛被采用)、同步的(产生了瞬时的耦合) [11]、而且也没有事务语义等等。
再者,就算认为HTTP是在实现中使用的唯一协议,也可以非常方便的利用SOAP信封把业务信息(SOAP 消息体)和基础设施信息或附加信息(SOAP 消息头)从SOAP消息中隔离。总的来说,如果你本来的实现并不需要任何基础设施或附加数据,整个SOAP信封的开销是很少的——只需两个标签,而且对必要时添加数据提供了明确定义的方法。
所以,从各个方面来看,以数据信封的方式将业务信息和基础设施关注分离是很强大的模式,甚至REST Web服务实现也常常使用这种方法。至于是否使用标准的SOAP还是定制化信封 [12]模式要根据具体实现而定。
其他关键不同点
我们花点时间来讨论一下其他一些常常被发表刊物引用的关于SOAP和REST Web服务的不同点。
简单化
一个普遍的观点是REST要比SOAP简单得多。照这个观点,REST简单的根源基于一个事实:REST不需要WSDL或任何接口定义。至少可以认为这种论调有点天真。无论哪种用于服务消费者和提供者之间通信的技术,都必须在语法和其消息交互(接口) [13]的语义上达成一致。这意味着就REST而言,下面两个方法有一个是可能的:
- 以文本方式定义一个接口,并基于接口文档描述中的通用接口定义来“手工地”编写数据的编码/解码。虽然这种方法常被REST拥趸所推崇,其接口包含的元素很少超过10到15个,但这不是典型的粗粒度REST服务。而且,这种方法很容易出错,所以,大部分可行的REST框架都遗弃该方法而使用下面的方法。
- 在XSD的层次定义接口,基于流行框架(比如,面向XML的JAXB或Castor,面向JSON负载的Jackson)产生数据的编码/解码。这种方法效果上就是WSDL的简约版,并且需要的工作量和基于SOAP实现差不多一样。事实上,完全相同的方法经常被用于基于SOAP的实现,设计一个单独接口和服务执行的命令模式。WSDL2.0 和/或 WADL for REST就是对该方法的扩展。
另一个SOAP常常被抱怨的就是复杂的WS*标准集。虽然不存在一个单独的规范来罗列这些关键的WS*标准集以及其彼此的关系,但对大部分服务交互用例还是存在一个标准的。就算如此,选择一个适当的WS*标准和其用法可能也需要一些额外理解和实现时间,但是:
在REST和SOA之战中争论简单化还是标准化是荒唐的,因为没有标准支持的简单只能有害于成本和应用的可管理性。
所以,除了那些简单到极点的例子之外,如“温度转换器”,REST并不比SOAP简单多少。
轻量级
另一个众多REST拥趸宣扬REST是SOAP的一种取代的原因是,实际上的REST请求和响应消息都较短。这主要基于两个原因:
- SOAP需要一层XML包装器来包装所有的请求和响应消息,这会增加消息的大小。这话没错,但重点不是包装器增加了多少字节,而是它创建在整个负载中的比例。因为包装器的大小是固定的,其所占比例随着消息的不断变大而变小,最终可以忽略不计。考虑到一般服务都是相当粗粒度的,请求和回复消息的大小也是相当大的,所以SOAP信封的负载不太会成为大问题。
- SOAP是基于XML的消息传输,而XML使用冗余的编码。REST,在这方面,提供了更轻量级的消息传输替代方案——JSON [14]。这话也对,但利用消息传输优化机制(MTOM),大部分SOAP框架都支持,可以把消息拆分成多个小的基于XML的SOAP信封/头/体部分,而附加的包含消息内容的部分可以编码为任何MIME类型,包括JSON和二进制流等。
虽然理论上讲,REST要比SOAP轻量级,但实际上,利用一些高级SOAP设计技术,真正使用中的SOAP和REST消息大小的差别是很小的。
易于构建 – 无需工具支持
因为REST基于HTTP,其拥趸认为,我们可以使用熟稔的技术,比如Java servlet API和Java HTTP支持来编写REST服务的实现端和客户端,而无需任何特定工具的帮助。这可以说是对的,但前提是你想要“手工”实现构建输入/输出消息和数据编组。SOAP Web服务也可以实现同样的工作。然而,大家很少希望编写这种样例代码,结果还是会使用工具,SOAP和REST都是。
结论
REST既适用于使用ROA(本真REST方法)的系统设计,也适用于使用REST技术(REST Web服务)的SOA设计实现。虽然两种方法都有其优势,但都没有改变最难的部分——定义和企业业务模型一致的业务服务/资源。有些情况的确两种都适合,但归根到底这完全是两种不同的风格。
关于作者
Boris Lublinsky是NAVTEQ公司的首席架构师,在这家公司中他的工作是为大型数据管理及处理、SOA定义架构愿景,并且实施各种NAVTEQ的项目。他还是InfoQ的SOA编辑, OASIS的SOA RA工作组的参与者。Boris是一位作者,还经常发表演讲,他最新的一本书是《Applied SOA》
致谢
我要谢谢我NAVTEQ的同事,尤其是Jeffrey Herr在我撰写这篇文章时提供的帮助。也要感谢Stefan Tilkov和Kevin T. Smith提供的有趣反馈(多数是负面的),这些反馈有助于文章的改进。
参考
- Cesare Pautasso,Olaf Zimmermann,Frank Leymann著 《RESTful Web Services vs. “Big” Web Services: Making the Right Architectural Decision》。
- Boris Lublinsky著《 Defining SOA as an architectural style》
- 面向资源的架构
- Martin Fowler Richardson著《 Maturity Model: steps toward the glory of REST》
- 面向资源的架构与REST
- Dhananjay Nene的博文“ Service oriented REST architecture is an oxymoron”
- Dhananjay Nene,“ REST is the DBMS of the Internet ”。
- Dhananjay Nene,“ Musings on REST”
- J?rgen Thelin,“ A Comparison of Service-oriented, Resource-oriented, and Object-oriented Architecture Styles”
- Richard Hubert著《Convergent Architecture: Building Model Driven J2EE Systems with UML》,Wiley,2011,ISBN:0471105600
- Arun Gandhi, “ SOAP vs. REST – The Best WebService”。
- 请参见链接
- Lawrence Mandel,“ Describe REST Web services with WSDL 2.0 ”
- Web应用描述语言
- Stefan Tilkov 访谈Sanjiva Weerawarana,“ Debunking REST/WS-* Myths”
- Lori MacVittie,“ SOAP vs REST: The war between simplicity and standards”
- 请参见链接
- Mark Little,“ A Comparison of JAX-RS Implementations”。
[1]看这里,文中有个很好的比喻。
[2]这里我使用的这个词语,从技术上其毫无意义,也不是指REST,但在业界被广泛使用,而有很多人也认为它就是REST。
[3]按照定义,资源可以是任何应该被直接呈现和访问的组件。
[4]这类服务常用的一种实现是基于“命令模式”。一个输入文档定义命令本身和用于执行命令的数据。
[5]方法独立源自于这样一个事实:不同的方法可以执行同一数据——这里指的是无论是否被服务暴露都存在的企业数据,而不是在OO中的针对特定数据的某对象实例。
[6]例如,在文献[4]中对OO和REST做了直接类比。
[7]许多REST倡导者声称后者是没有必要的。我们在文章后面会再回到这个问题上。
[8]架构风格就像面向于不同软件系统之间结构和连接的“设计模式”。文献[10]提供了关于架构风格的一个比较完整的定义,“架构风格是指一组有相同原则和属性的架构”
[9]另一个在业界被普遍乱用的名称——根据定义,Web服务就是SOAP消息。
[10]所有的WS*实现都重度依赖SOAP,尤其是SOAP头。
[11]你总是可以在HTTP上实现异步消息机制,但是需要额外的抽象层,比如SOAP就在其上使用了WS-Addressing。
[12]许多REST倡导者认为HTTP已经有了一组标准的消息头,因此SOAP消息头完全没有必要。这里的问题在于一组预定义好的HTTP头固然有其良好定义的语义,但任何应用特定数据必需一个自定义HTTP消息头,这和自定义SOAP消息头的复杂度相同。
[13]一个有趣例子是,在很多JAX-RS实现中客户端API,其接口往往是一个java接口——许多对多语言的支持。
[14]简单通常意味着高价——所以,要试图通过无需手动编码对象类型而用JSON消息实现多态
查看英文原文: Is REST the future for SOA?
感谢 马国耀对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至 [email protected]。也欢迎大家加入到 InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。