如今微服务和分布式架构变的越来越流行,而简单,可靠,高效,跨平台和跨语言的 Web Service 则是这类系统架构的基石。 RESTful Web Service 恰好满足这些特点,被越来越多的系统架构所采用。
本文主要面向对 Web Service 有一定理解,需要进一步了解基于 REST 形式的 Web Service 的 IT 开发人员和架构师。它不是 Web Service 入门介绍,你需要较多相关领域的知识背景才能理解全部内容。
什么是 RESTful Web Service
作为互联网应用开发人员,我们经常能看到 Web Service,REST 和 RESTful Web Service 之类的描述,可我们真的清楚这些概念吗?
Web Service 简单来说是指提供给不同设备通过互联网(一般使用 HTTP 协议)进行通信和交换数据的一种服务。RESTful Web Service 是实现 Web Service 的一种方式。那么到底什么是 RESTful Web Service呢?什么又是 REST 呢?
REST 和 RESTful Web Service
REST (Representational State Transfer) 是由美国计算机科学家 Roy Fielding在2000年的博士论文提出的一种架构方式。Roy Fielding绝对可以称之为业界大牛,他现任 Adobe 首席科学家,是HTTP协议的首要作者之一,也是Apache项目的联合创始人。
REST 是一种架构方式和约定,和具体实现无关,也不一定必须基于Web。我们一般把采用 REST 架构的 Web Service 称之 RESTful Web Service。在实际项目应用中,严格来说,我们应该称这种 Web Service 为具有 REST 风格的 Web Service。原因是我们在处理和解决某些实际问题时,这种 Service 可能并不完全严格遵守 REST 架构的所有必要约定。
RESTful Web Service 和基于SOAP的 Web Services 有着本质的不同。使用 SOAP 的 Web Service 实际上是以协议(protocol)的形式工作的。因为 SOAP Web Service 严格规定了如何发现和描述 API,其传输的消息也有严格统一的格式(例如传输的载体XML有严格的格式规范)。而 RESTful Web Service 并不是协议,它没有规定传输消息的具体格式,它只是一种约定使用 REST 架构实现的 Web Service。RESTful Web Service 相比SOAP Web Service 更加简单和轻量级。现在大部分 RESTful Web Service 都使用类似的形式,例如都使用HTTP传输,使用风格类似的 URL 作为 API 和 使用 JSON 或者 XML 来传输数据等等(目前 JSON 占据主导地位,并且有持续流行的趋势[1])。
如果你之前没有接触过SOAP,你只需记住RESTful Web Service 是一种采用 REST 架构约定,更简单,更轻量级的Web Service
REST 之父大牛 Roy Fielding 在他的论文中,一共从下面6个方面阐述了 REST 架构的约定(REST 应该满足哪些条件,以及这样做会有什么好处)
REST 架构约定
CS结构(Client–server)
客户端是一个相对独立的实现,它不必考虑数据的持久化存储问题。服务端拥有和保存数据,服务端不去关心客户端内部实现,也不用关心客户端请求的上下文。服务端和客户端之间遵守相同的接口规范。在遵守相同接口规范的前提下,二者都可以独立演化,甚至可以被其它的实现替代。无状态(Stateless)
任何时候一个客户端的请求数据都包含能够让服务端完成请求的充分信息,服务端不依赖前后不同请求的顺序和状态信息来完成请求。请求的session信息由客户端持有,并在必要时连同请求数据一起发送。服务端可以使用请求中的session信息去其它外部服务或者数据库获取相关内容进以便对该请求做权限验证等操作。如果所需数据要通过多次请求才能完成,客户端必须自己负责记录状态(因为服务器不跟踪客户端的状态),在下次请求时附带发出。一个典型的例子是分页的实现,客户端需要自己保存当前页数,请求下一页时作为参数一起发给服务端,服务端使用该参数返回正确的下一页数据。有些设计,比如Facebook API,服务端返回数据中包含下一页数据的请求URL,客户端只要记录这个URL即可发起下一页的请求。
服务端不依赖客户端的请求顺序和状态提高了服务器的可扩展性(scalability)。比如在使用Load balancer的情况下,不能因为某个请求被分配到其它服务器而丢失某些信息从而返回不正确的数据。
无状态的约定也提高了系统的健壮性(reliability)。如果集群服务器中的其中一台发生故障也不会对系统的平稳运行造成太大影响。不过无状态的约定也是有缺点的,客户端必须每次都要带上相同重复的信息来确定自己的身份和状态,这就造成了传输数据的冗余性。然而没有一个架构决策是十全十美的,最终的决策往往都是相互妥协的结果,我们只能选择一个相对有优势的决策。
缓存机制(Cacheable)
服务端应该明确规定返回数据的缓存机制,包括是否可缓存,缓存如何失效以及利用缓存获取增量数据而不必每次获取全部数据等。合理的缓存设计可以减少请求次数,进而提高服务器的效率和性能。系统分层(Layered system)
客户端不用知道数据是从服务端直接返回还是通过中转代理返回。这样的设计也同样提高了系统的可扩展性。比如可以使用负责均衡和反向代理等技术来对系统进行水平扩展和缓存处理,把系统划分成不同的层次。使用分层设计也方便我们管理不同资源的权限,有利于提高系统的安全性。可定制代码(可选)(Code on demand)
服务端可选择临时给客户端下发一些功能代码让客户端来执行,从而定制和扩展客户端的某些功能。比如服务端可以返回一些 Javascript 代码让客户端执行,去实现某些特定的功能。一致的接口(Uniform interface)
一致的接口对 REST 服务至关重要。基于统一的接口规则可以简化系统实现,降低子系统之间的耦合度,因为子系统只要关注实现接口即可。在保证接口一致的情况下,不同的实现可以各自独立演化。对于接口的要求有这么四个方面:
一致的数据格式
虽然服务端内部不同数据的存储格式可能千差万别,但返回给客户端的数据一定要有一个统一的表现形式。比如 Web Service 请求返回格式要么是 HTML,要么是 XML,要么是 JSON,不能返回服务端自己内部使用的特殊格式。可以对已有数据进一步操作(Resource Identifiers)
如果客户端拥有一个资源,必要时,客户端应该拥有足有的信息去修改和删除这个资源。通常我们只要在返回的数据中包含一个 UID 即可做到这点。比如从服务端获得了一个订单数据,这个订单数据里应该保证有一个唯一的订单 ID,当我们想对这个订单进行进一步操作时,可以保障操作的是同一个订单。数据具有自我描述性
每项数据应该是可以自我描述的,方便代码去处理和解析其中的内容。比如通过HTTP返回的数据里面有 [MIME type ]信息,我们从MIME type里面可以知道数据的具体格式,是图片,视频还是JSON。应用系统状态变化只依赖超媒体(Hypermedia)
应用系统状态变化只依赖于服务端发来的Hypermedia(如超链接 hyperlinks)。举例来说,假设向一个微博 Web Service 请求一条微博信息,服务端响应信息中应该包含和这条微博相关的其它的URL。客户端可以进一步利用这些URL发起请求来获取感兴趣的信息。前面章节中提到的Facebook API 可以从第一页的返回数据中获取下一页的URL也是基于这个设计理念。
REST 的这些约定为我们设计 RESTful Web Service 提供了一个绝佳的范本。套用这个范本,在设计 Web Service 时,可以在一定程度上避免很多问题和少走弯路。因为这些约定本质上都是对现实系统设计优点的一些提炼和总结,它已经比较周全的为我们考虑了可能出现的问题,并且提供了解决问题的基本原则和方向。
本文第三小节“REST 架构约定”的主要内容来自对参考文档中的部分内容的整理和翻译。计算机科学有很多非常有价值的论文,很多论文是如此的重要以至于能奠定一个时代的技术基础(比如 Google 的 MapReduce,可能需自备梯子)。我们只有充分理解了其中的思想和原则才能作出更好的设计和架构。
下一篇文章我们将结合HTTP的特性,探讨如何设计 RESTful Web Service API
参考文档
这个结论来自Goolge Trends(须自备梯子): https://www.google.com/trends/explore?date=all&q=xml%20api,json%20api
作者:JeffreyLi
链接:https://www.jianshu.com/p/a79a1d01b557