restful架构风格设计准则(六)版本管理
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者!
版本管理
在前面已经提到过,一个REST系统为资源所抽象出的URI实际上是对用户的一种承诺。但反过来说,软件开发人员也很难预知一个资源的各方面特征如何在未来发生变化,从而提供一个永远不变的URI。
在一个REST系统逐渐发展的过程中,新的属性,新的资源将逐渐被添加到该系统中。在这些更改过程中,资源的URI,访问资源的动词,响应中的Status Code将不能发生变化。此时软件开发人员所做的工作就是在现有系统上维护REST API的后向兼容性。
当资源发生了过多的变化,原有的URI设计已经很难兼容现有资源应有的定义时,软件开发人员就需要考虑是否应该提供一个新版本的REST API。那么我们该如何对资源的版本进行管理呢?
首先要考虑的就是,新API的版本信息是否应当包含在资源的URI中。这在各著名论坛中仍然是一个争议较大的话题。一种观点认为在不同版本的API中,一个资源拥有不同的地址在一定程度上违反了HATEOAS:URI只是用来指定一个资源所在的位置,而不是该资源如何被抽象。如果一个资源由不同的URI标示其不同的表现形式,那么用户将无法通过一个响应中所标示的URI得到其它URI所指向的表示形式。而且在URI中添加了有关版本的信息也就标示着其可能会随着时间的推移发生变化。
一种使用独立URI的方法是基于Accept头。在一个请求中,我们常常标明了Accept头,以标示客户端希望得到的表现形式。在该头中,用户可以添加所请求的资源的版本信息:
1 GET /api/categories/1
2 Host: www.egoods.com
3 Authorization: Basic xxxxxxxxxxxxxxxxxxx
4 Accept: application/vnd.ambergarden.egoods-v3+json
而在接收到该请求之后,服务端将返回该资源的第三个版本:
1 HTTP/1.1 200 OK
2 Content-Type: application/vnd.ambergarden.egoods-v3+json
3 Content-Length: xxx
4
5 {
6 "uri" : "/api/categories/1",
7 "label" : "Food",
8 ……
9 }
可以看到,该方法是非常严格地遵守REST系统所提出的约束的。但其也并不是没有缺点:添加一个自定义MIME类型(Custom MIME Type)也是一个很麻烦的流程,而且在很多现有技术中都没有很好地支持它,如HTML5中的Form。因此这种方案的缺点是对REST API用户并不那么友好。
除此之外,另一种基于重定向的解决方案也被提出。该方案允许一个REST系统提供多个版本的API,并在URI中标明版本号:
1 /api/v2/categories
2 /api/v1/categories
这样用户可以选择使用特定版本的REST API来实现客户端功能。由于其使用固定版本的API,因此并不存在着一个资源有多种表示,进而违反了HATEOAS约束的问题。
在REST系统的API随时间逐渐发展出众多版本的时候,系统对API的维护也将成为一个较大的问题。此时就需要逐渐退役一些年代久远的API 版本。对这些版本的退役主要分为两步:首先将其标为过期的,但是还在一段时间内支持。在这种情况下,对这些已经过期的API的访问将得到3XX响应,如301 Moved Permanently,以通知用户该URI所标示的资源需要使用新版本的URI进行访问。而再经过一段时间后,则将过期的REST API标记为废弃的。此时用户在访问这些URI时将返回4XX响应,如410 Gone。
接下来,该REST系统还可以提供一个通用的REST API接口,并与最新版本的API保持一致:
1 /api/categories
这样用户还可以选择一直使用最新版本的API,只是同时也需要一直对其进行维护,以保持与最新版本API的兼容性。在REST系统的API随着时间的推移逐渐发生变化的时候,该客户端也需要逐渐更新自身的功能。
但是该方法有一个问题:由通用URI所辨识出的各个资源需要是稳定的,不能在一定时间之后被废弃,否则会给用户带来非常大的维护性的麻烦。举例来说,假设客户端逻辑添加了一系列操作分类的功能。当REST系统决定不再采用分类作为商品归类的标准,那么客户端逻辑中与分类相关的各个功能都需要进行大幅度地修改。过于频繁的这种改动很容易导致用户对该系统所提供的API失去维护的信心。因此在抽象资源时一定要努力地将各个资源的边界辨识清楚。虽然说这听起来很吓人,但是在经过仔细考虑后这种情况还是较为容易避免的。
但是反过来说,理论常常与实际有些脱钩,更何况REST是在2000年左右提出的,无法做到能够预见到十余年后所使用的各项技术。因此在尽量符合REST所提出的各约束上提供一个最直观的,具有最高易用性的API才是王道。无限制地提供后向兼容性是一个非常困难,成本非常高的事情。因此在版本管理这一方面上来说,我们也需要尽量兼顾项目需求和完全遵从理论这两者之间的平衡。
而在同一个版本之中,我们则需要保证API的后向兼容性。也就是说,在添加新的资源以及为资源添加新的属性的时候,原有的对资源进行操作的API也应该是工作的。
对于一个基于HTTP的REST服务而言,软件开发人员需要遵守如下的守则以保持API的后向兼容性:
- 不能在请求中添加新的必须的参数。
- 不能更改操作资源的动词。
- 不能更改响应的HTTP status。
而前向兼容性则显得没有那么重要了。REST服务的前向兼容性要求现有的服务兼容未来版本服务的客户端。但是由于服务提供商所提供的服务常常是最新版本,因此对前向兼容性有要求的情况很少出现。另外一点是,为一个服务提供前向兼容性其实并不那么容易。因为这要求软件开发人员对产品的未来方向进行非常多的假设,而且这些假设不能有错误。反过来,这种对服务的前向兼容性的要求主要由客户端自身通过保持后向兼容性来完成。
性能
接下来我们就来简单地说说基于HTTP的REST服务中的性能问题。在基于HTTP的REST服务中,性能提升主要分为两个方面:REST架构本身在提高性能方面做出的努力,以及基于HTTP协议的优化。
首先要讨论的就是对登陆性能的优化。在前面我们已经介绍过,在一个基于HTTP的REST服务中,每次都将用户的用户名和密码发送到服务端并由服务端验证这些信息是否合法是一个非常消耗资源的流程。因此我们常常需要在登陆服务中使用一个缓存,或者是使用第三方单点登陆(SSO)类库。
除此之外,软件开发人员还可以通过为同一个资源提供不同的表现形式来减少在网络上传输的数据量,从而提高REST服务的性能。
而在集群内部服务之间,我们则可以不再使用JSON,XML等这种用户可以读懂的负载格式,而是使用二进制格式。这样可以大大地减少内部网络所需要传输的数据量。这在内部网络交换数据频繁并且所传输的数据量巨大时较为有效。
接下来就是REST系统的横向扩展。在REST的无状态约束的支持下,我们可以很容易地向REST系统中添加一个新的服务器。
除了这些和REST架构本身相关的性能提升之外,我们还可以在如何更高效地使用HTTP协议上努力。一个最常见的方法就是使用条件请求(Conditional Request)。简单地说,我们可以使用如下的HTTP头来有条件地存取资源:
- ETag:一个对用户不透明的用来标示资源实例的哈希值
- Data-Modified:资源被更改的时间
- If-Modified-Since:根据资源的更改时间有条件地Get资源。这将允许客户端对未更改的资源使用本地缓存。
- If-None-Match:根据ETag的值有条件地Get资源。
- If-Unmodified-Since:根据资源的更改时间有条件地Put或Delete资源。
- If-Match:根据ETag的值有条件地Put或Delete资源。
当然,这里所提到的一系列性能优化方案实际上仅仅是比较常见的,与基于HTTP的REST服务关联较大的方案。只是顾虑到过多地陈述和REST关联不大的话题一方面显得比较没有效率,另一方面也是因为通过写另一个系列博客可以将问题陈述得更加清楚,因此在这里我们将不再继续讨论性能相关的话题。
相关资源
AtomPub:http://atomenabled.org/。其是最为广泛讨论的并借鉴的RESTful服务。其由众多HTTP和REST专家所编写,甚至包括Roy Fielding本人也参与于其中
Roy Fielding的REST论文:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
Roy Fielding的个人网站:http://roy.gbiv.com/untangled/。
RFC列表:http://www.ietf.org/rfc/
restful架构风格设计准则(六)版本管理的更多相关文章
- restful架构风格设计准则(二)以资源为中心,一个url
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! 1.REST是一种架构风格,其核心是面向资源,简化设计,降低开发的复杂性 ...
- restful架构风格设计准则(五)用户认证和session管理
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! Authentication REST提倡无状态约束,这就要求:用户状态 ...
- restful架构风格设计准则(四)资源表示和资源访问
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! 一.资源表示 1.资源表示:使用 单数 vs. 复数 如果一个URL所对 ...
- restful架构风格设计准则(三)资源识别和资源设计
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! restful风格的设计中,首先要识别系统中的资源,然后用HTTP规范表 ...
- restful架构风格设计准则(一)以资源为中心、自描述的请求响应、资源状态迁移为粒度
读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者! 一.需求描述 当用户在某个电子商务网站购物时,他首先查看要购买的商品分类 ...
- (转载https://segmentfault.com/a/1190000016313947)了解RestFul Api架构风格设计
最近几年REST API越来越流行,特别是随着微服务的概念被广泛接受和应用,很多Web Service都使用了REST API. REST是HTTP规范主要编写者之一的Roy Fielding提出的, ...
- 理解RESTful 架构
REST是所有Web应用都应该遵守的架构设计指导原则. Representational State Transfer,翻译是”表现层状态转化”. 面向资源是REST最明显的特征,对于同一个资源的一组 ...
- 学习RESTFul架构
一.RESTFul介绍 1.一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机 ...
- 理解RESTful架构
越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency).高 ...
随机推荐
- Linux系统默认权限之umask
默认情况下,目录权限值为755, 普通文件权限值为644, 那么这个值是由谁规定的,追究其原因是 umask [root@adminx]# vim /etc/profile 1.假设umask值为:0 ...
- Git常用命令手册
github 的使用教程(非常详细的小白视频)链接如下: http://yun.itheima.com/course/209.html Git 详细使用手册链接如下: https://git-scm. ...
- Django admin 组件 原理分析与扩展使用 之 sites.py (一)
一 . 前言 Django 提供了admin 组件 为项目提供基本的管理后台功能(对数据表的增删改查). 本篇文章通过 admin源码 简单分析admin 内部原理 ,扩展使用方式,为以后进行定制和自 ...
- php notice提示
php页面内添加error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE ); OK.
- 仿QQ发语音、图片选择、表情选择demo
一款仿QQ发语音.图片选择.调用拍照.表情选择的demo git地址:https://github.com/PureLovePeter/pic.git. 喜欢的请 star star star,共 ...
- Genymotion模拟器的安装及常见问题解决方法
Genymotion是很好用很快的一款Android模拟器. 我们使用的时候下载一个免费版的就足够用了. 官网下载地址:http://www.genymotion.net/ 不过这个软件下载之前必须注 ...
- SDP(13): Scala.Future - far from completion,绝不能用来做甩手掌柜
在前面几篇关于数据库引擎的讨论里很多的运算函数都返回了scala.Future类型的结果,因为我以为这样就可以很方便的实现了non-blocking效果.无论任何复杂的数据处理操作,只要把它们包在一个 ...
- cesium 热力图
- c++趣味之shared_ptr额外好处
shared_ptr(sp)额外好处是什么?即使被转为基类,析构函数也可以正常执行. 已知两个类 class foo{}; class bar:foo{public:~bar(){}}; 先来看不用s ...
- 基于hi-nginx的web开发(python篇)——路由装饰器
现在,有了起步的基本认识,现在需要一个可以媲美flask或者bottle的简洁易用的路由功能,可以用装饰器写法任意映射 URLs 到代码. 这个,并不难.首先,来一个叫做hi的模块:hi.py: im ...