RESTful Web Service 架构剖析

如今微服务和分布式架构变的越来越流行,而简单可靠高效跨平台跨语言的 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 架构约定

  1. CS结构(Client–server)
    客户端是一个相对独立的实现,它不必考虑数据的持久化存储问题。服务端拥有和保存数据,服务端不去关心客户端内部实现,也不用关心客户端请求的上下文。服务端和客户端之间遵守相同的接口规范。在遵守相同接口规范的前提下,二者都可以独立演化,甚至可以被其它的实现替代。

  2. 无状态(Stateless)
    任何时候一个客户端的请求数据都包含能够让服务端完成请求的充分信息,服务端不依赖前后不同请求的顺序和状态信息来完成请求。请求的session信息由客户端持有,并在必要时连同请求数据一起发送。服务端可以使用请求中的session信息去其它外部服务或者数据库获取相关内容进以便对该请求做权限验证等操作。

    如果所需数据要通过多次请求才能完成,客户端必须自己负责记录状态(因为服务器不跟踪客户端的状态),在下次请求时附带发出。一个典型的例子是分页的实现,客户端需要自己保存当前页数,请求下一页时作为参数一起发给服务端,服务端使用该参数返回正确的下一页数据。有些设计,比如Facebook API,服务端返回数据中包含下一页数据的请求URL,客户端只要记录这个URL即可发起下一页的请求。

    服务端不依赖客户端的请求顺序和状态提高了服务器的可扩展性(scalability)。比如在使用Load balancer的情况下,不能因为某个请求被分配到其它服务器而丢失某些信息从而返回不正确的数据。
    无状态的约定也提高了系统的健壮性(reliability)。如果集群服务器中的其中一台发生故障也不会对系统的平稳运行造成太大影响。

    不过无状态的约定也是有缺点的,客户端必须每次都要带上相同重复的信息来确定自己的身份和状态,这就造成了传输数据的冗余性。然而没有一个架构决策是十全十美的,最终的决策往往都是相互妥协的结果,我们只能选择一个相对有优势的决策。

  3. 缓存机制(Cacheable)
    服务端应该明确规定返回数据的缓存机制,包括是否可缓存,缓存如何失效以及利用缓存获取增量数据而不必每次获取全部数据等。合理的缓存设计可以减少请求次数,进而提高服务器的效率和性能

  4. 系统分层(Layered system)
    客户端不用知道数据是从服务端直接返回还是通过中转代理返回。这样的设计也同样提高了系统的可扩展性。比如可以使用负责均衡和反向代理等技术来对系统进行水平扩展和缓存处理,把系统划分成不同的层次。使用分层设计也方便我们管理不同资源的权限,有利于提高系统的安全性

  5. 可定制代码(可选)(Code on demand)
    服务端可选择临时给客户端下发一些功能代码让客户端来执行,从而定制和扩展客户端的某些功能。比如服务端可以返回一些 Javascript 代码让客户端执行,去实现某些特定的功能。

  6. 一致的接口(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

参考文档

  1. CH5 - Representational State Transfer (REST)
  2. Representational state transfer

  1. 这个结论来自Goolge Trends(须自备梯子): https://www.google.com/trends/explore?date=all&q=xml%20api,json%20api

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,417评论 0 19
  • 渲染引擎 渲染引擎的工作主要是。。渲染,用来在屏幕上显示的内容。主流的浏览器,Chrome和Safari用的是开源...
    ShawnRong阅读 323评论 0 0
  • 收获 导航ul不要给宽度,给li设置间距,这样页面在缩小的时候就可以吧内容往里里面收缩 响应式页面完成,目前只兼容...
    毛毛i阅读 186评论 0 0
  • 《忘了去懂你》算是一部标准的文艺片,没有波澜起伏的剧情没有爱恨情仇的交织,叙述方式也是不温不火的细腻、冷静,这对于...
    娟子的书房阅读 305评论 0 0