为 Web__ 设计、实现和维护 API__ 不仅仅是一项挑战;对很多公司来说,这是一项势在必行的任务。本系列 将带领读者走过一段旅程,从为API__ 确定业务用例到设计方法论,解决实现难题,并从长远的角度看待在Web__ 上维护公共API _。沿途将会有对有影响力的人物的访谈,甚至还有 API__ 及相关主题的推荐阅读清单。_
这篇 InfoQ 文章是 Web API从开始到结束系列文章中的一篇。你可以在这里进行订阅,以便能在有新文章发布时收到通知。
Roy T. Fielding 是来自于 Adobe 的一位高级首席科学家,也是全世界促进网络软件的主要推动力之一。当他还是一位即将毕业于加利福尼亚大学尔湾分校(UCI)的学生时,他在所参与的一个课堂项目中创建了一个对 Web 进行维护的机器人,名为 MOMSpider 。其间,他还抽空创建了 libwww-perl 这一类库,其中的一些底层原则也在之后成为了 WWW 的架构的一部分。当时,Fielding 为这一部分原则命名为 _HTTP__ 对象模型 _(HTTP Object Model)。若干年之后,在他进行博士毕业论文设计的时候,他将这一模型重新命名为_ 具象状态传输_,或简称REST。它为我们展现了“一个设计良好的Web 应用程序的行为的多种特征”。
Fielding 对于开放标准做出了各个方面的巨大贡献,他的名字也总是出现在各种 RFC 规格说明中,包括 HTTP 、 URI 模板等等。Fielding 同时还是 W3C 组织定义的“不要跟踪”请求标准的编辑之一。此外,作为 Apache HTTP 服务器项目的创始人之一,他还协助创建了这一世界上最流行的 Web 服务器软件、编写了 Apache 授权协议、成立了 Apache 软件基金会,并担任了该基金会的第一任主席。
最近,Roy 正辗转于各个有关于标准的会议之间,在此期间,他抽空回答了对于某一个经常引起争论的主题的一系列问题,这一主题就是 Web 的版本化。他也解释了为什么在他所定义的 REST 风格中,超媒体是必不可少的、设计一个能够适应未来变化的网络软件的过程、以及在设计能够在几十年之后继续可用的软件时会遇到的种种挑战等等。
InfoQ**:在 2013年 8月,你在 Adobe Evolve大会上举办了一场演讲,在演讲你对于如何对 Web的 API**** 进行“版本化”提供了一个建议,总结起来就是一句话:“不要版本化。”对于这项建议,你所看到的听众的反应是怎样的?**
Roy:我认为与会的每个人都对此给出了积极的回应,因为大多数与会者都是我们的客户,并且非常熟悉 Adobe Experience Manager 产品的设计理念。当然,我不仅仅是对着幻灯片 _ 照本宣科 _,而是详细地解释了这一结论背后的理论。
在讨论的幻灯片发布到互联网上之后,各种声音就更多一些,某些读者误解了我对版本化的定义,还有些读者误解了更改主机名与商标(branding)的意义。我所说的版本化,是指将客户端可见的接口数量限定在某些名称内,这样一来客户端就可以对每个操作进行标记,将这些操作归于该 API 的某一版本。
不幸的是,对接口名称进行版本化,只是从 API 作者的角度而言实现了对变更的管理。这一点是对接口设计哲学的短视行为:作者渴望对 API 进行控制,而忽略了客户对于良好的持续性的需求。
InfoQ:那么,如果你为某个 API实现了版本化,会发生什么事呢?
Roy:有以下两种可能,(a) 版本最终产生了变更,所有为之前版本所设计的组件不得不重启、重新部署、甚至于被完全抛弃,因为这些组件无法适应新系统所带来的好处。(b) 版本永远不会产生变化,就像一个永远不动的吊锤一样,它导致的结果是每个 API 调用都显得低效。
许多开发者对此举手表达他们的厌恶之情,并且坚持声称我不理解他们所遇到的问题:他们的系统非常重要、他们要对系统进行变更、要加入新的特性、数据也要进行重新组织。他们需要某种方式,让旧版本的客户端能够和新版本的客户端共存。
一般来说,遇到这种问题,我总需要解释一下,为什么“超媒体即应用状态引擎”是 REST 中不可缺少的一环。它不是可选的,也不是“最好能有”的,而且必须的。换句话说,如果你放弃这一点,就等于放弃了 REST。如果客户端将 API 的可控性寄托于部署方案的设计,那你是不可能让 API 保持逐步演化的。对于 API 的可控性必须在运行过程中进行学习,这一点正是超媒体所实现的功能。
但是,仅仅通过使用超媒体还是不足以实现逐步演化的目标。超媒体允许应用程序按需提供控制行为,但我们还需要能够让客户端理解它的表现形式的能力(对媒体类型及其所预期的处理过程的理解)。这一点是按需实现代码的闪光点。
InfoQ:如此说来,超媒体作为 REST风格不可缺少的一部分的原因之一,就在于它能够随着时间的推移对变更进行处理,是这样吗?
Roy:对于变更的预期是 REST 的核心思想之一。有经验的开发者考虑到了 API 在未来可能发生的各种变化,随之想到,对接口进行版本化正是应对这些变更的正确方法,这也不是不能理解。但随之而来的问题是,在何处、以及怎样对 API 进行版本化,这方面的争论永远也不会停止。
某些开发者在内部软件的管理过程中学到了一些经验,他们相信能够控制客户端及服务器的部署,这是可以理解的。但对于那些志在跨越多个组织的界限的互联网软件来说,对于客户端及服务器的部署的完全控制则是不可能的。而这正是 REST 试图解决的问题:如何在不破坏或替换已部署的组件的情况下,对系统进行无痛升级。
因此,我的幻灯片试图将人们的注意力转回正确的方向:逐步演化的能力。换句话说,不要总想着将 API 设计为 RESTful 的,而是要想着设计一套有着你所需要的各种特性的 API。REST 的作用在于它能够引导出这些对于为多个组织服务的系统具有重要意义的特性,例如演化能力。演化能力意味着系统在适应新的变更时,无需进行重启或重新部署。
InfoQ:这是不是意味着,只要我使用 REST风格,我就不会遇到版本化所带来的问题?
Roy:并非如此。总是会有某些原因,会促进你设计一套完全不同的 API,尤其是当接口的语义产生变化、或是由于安全性的原因会要求你抛弃之前所部署的软件。我的观点是,不要用版本 ID 来定义一次天翻地覆的大改动,而是用主机名来处理。因为你所创建的不是一个 API 的新版本,而是一个带有新商标的新系统。
在 Web 上,这种新系统就是指一个全新的网站。网站上不会存在任何版本号,因为它们根本不需要。RESTful API 同样也不需要版本号,一个(正确设计的)RESTful API 对于客户端来说就是一个网站,只是它的受众相对有限而已。
InfoQ**:在你的讲座中,当你讲到你所定义的 REST架构风格时,你提到 REST的设计能够支持“几十后的软件工程的规模”。从实质性的角度来说,你所说的“几十年后的规模”是指什么?**
Roy:我在最初创建 REST 的时候,是为了解决我当时所遇到的一个问题:如何在不破坏 Web 的情况下改善 HTTP。在 1994-95 年那段时间,我正在重新 HTTP 协议,这是我遇到的一个重要的问题。当时我还是一位软件工程方面的博士后学生,我试图不要破坏在那个年代中已成为标准的一些东西,这就意味着我必须设计出一个能够应对今后几十年中全世界的人们所进行的各种变更。想象一下,在 1994 年创建的软件系统,有多少个延用至今的?我的意思正如字面所说:让系统在几十年后依然能够发挥作用,同时保证系统在独立且正交的方向进行演化,而不需要对系统进行关机或是重新部署。目前为止,已经过了两十年了。
InfoQ:你自己也承认,这种水平的软件工程是大多数架构师、设计师和开发者无法做到的,那么为什么还要讨论这种级别的软件工程规模呢?
Roy:我之所以谈到这一点,是因为人们对于使用 REST 进行机器与机器之间通信这一方式的第一反应往往是,“既然客户端知道要发送的内容,为什么还要使用超媒体呢?它只会降低交互的速度。”这套系统设计的理念是通过解耦实现演化能力,而对于那些将“让系统在下周之内运行”,或是“我们会在下次发布中解决问题”视为金科玉律的开发者来说,他们显然无法理解这一理念。
如果开发者们期望让他们的系统运行得更长久,他们就能够从“为什么要让系统随着时间推移进行变更”的偏见中跳出来。这时我们可以将时间跨度从几十年缩短到几年(在这段时间内,你的用户群已经截然不同了),甚至缩短到几个月(在这段时间内,你还能够控制客户端的部署吗?)。
InfoQ**:HTTP这一应用层的协议通常被视为一个能够适应几十年变化的软件工程的成功案例。如今,HTTP已经发布了若干版本,而较早的 HTTP版本中存在着大量的 **错误 **,例如 Host头,绝对时间的缓存指令等等问题。这一点与你对于 Web API**所建议的“不要版本化”是否存在矛盾。
Roy:不,HTTP 不会对接口名称进行版本化,在接口方法或 URI 中也不存在版本号。但不意味着通迅的其它方面不存在版本化。我们需要变更,否则我们将无法随着时间推移而改进。我们所看到的变更包括描述数据的定义语言。我们只是不欢迎破坏性的变更,因此,版本化更多地用于信息的变更,而不是用于契约的变更。
随便说一句。正确的说,HTTP 的设计几乎是完全没有什么错误的,只是整个世界对它的应用(也是由 HTTP 所产生的应用)发生了变化。Host 这一头信息在 1992 年看来是个愚蠢的设计,因为没有人需要为某个 IP 地址指定多个域名,但业务上的需求促成了它的应用。而如果 Mosaic 没有为 HTML 加入内嵌图片的功能,那么持久性连接也会成为一个糟糕的主意。绝对过期时间对于镜像托管的意义要大于缓存,而且通常来说过期时间应该是几个星期,而不是几秒种之后。
InfoQ**:那么,从 HTTP以及 HTML随着时间推移而产生了变更的这一事实中,我们又能学到些什么呢?**
Roy:从 HTTP 及 HTML 中,我们所学到的是必须定义协议或语言该如何随着时间推移进行变更,以及在他们接收到某种他们还不理解的变更时应当如何进行应对。HTTP 能够随着时间进行改善的原因,是因为我们需要新的语法必须能够被忽略,并且在语义进行变更时,必须要等到新的版本能够理解新的语义才接受这种变更。
InfoQ:如今的 Web开发者面对变更的压力似乎比从前更大了。我们是否遇到了新的问题,还是说某些问题如今变得更为常见了?
Roy:我认为原因只是现在的开发者遇到这种问题的机会比以前更大了。现如今,要创建一个拥有大量访问者的网站已经变得十分容易,而在从前,要让某个公司在内网之外部署一台服务器通常都要花上几年时间。在多数情况下,这是种幸福的烦恼。
软件开发者总是要与短视进行搏斗的。
InfoQ**:最后一个问题了,除了“不要版本化”之外,你对 Web API的设计者、架构师和开发者还有什么建议,能够帮助他们解决 API随时间产生变更这个问题吗?**
Roy:呵呵,我可没说不要随着时间进行变更,我只是说不要在 API 中使用破坏性的名称变更。
要想给出一些通用性的建议看起来是不可能的,因为我所说的东西是特定于所创建系统的环境与类型的。我依然建议使用 REST 创建 Web 应用程序,因为它保证系统能够在未来依然运行良好,并且会带来更多的 Web 访问(更多的可访问资源)。
关于受访者
Roy T. Fielding是来自于 Adobe 的一位高级首席科学家,也是全世界促进网络软件的主要推动力之一。当他还是一位即将毕业于加利福尼亚大学尔湾分校(UCI)的学生时,他在所参与的一个课堂项目中创建了一个对 Web 进行维护的机器人,名为 MOMSpider 。其间,他还抽空创建了 libwww-perl 这一类库,其中的一些底层原则也在之后成为了 WWW 的架构的一部分。当时,Fielding 为这一部分原则命名为 HTTP 对象模型(Object Model)。若干年之后,在他进行博士毕业论文设计的时候,他将这一模型重新命名为_ 具象状态传输_,或简称REST。它为我们展现了“一个设计良好的Web 应用程序的行为的多种特征”。
为Web__ 设计、实现和维护API__ 不仅仅是一项挑战;对很多公司来说,这是一项势在必行的任务。本系列 将带领读者走过一段旅程,从为API__ 确定业务用例到设计方法论,解决实现难题,并从长远的角度看待在Web__ 上维护公共API _。沿途将会有对有影响力的人物的访谈,甚至还有 API__ 及相关主题的推荐阅读清单。_
这篇 InfoQ 文章是 Web API从开始到结束系列文章中的一篇。你可以在这里进行订阅,以便能在有新文章发布时收到通知。
查看英文原文: Article: Roy Fielding on Versioning, Hypermedia, and REST
评论