Google Cloud的API 设计,google cloud storageGoogle Cloud的API 设计最近(很久前)在设计API接口的时候发现了一些很难取舍的地方,就看了下业界领先的企业都是怎么设计类似API的,发现了很多之前设计的缺陷和一些新的思路。主要参考了AWS和Google Cloud的API设......
最近(很久前)在设计API接口的时候发现了一些很难取舍的地方,就看了下业界领先的企业都是怎么设计类似API的,发现了很多之前设计的缺陷和一些新的思路。主要参考了AWS和Google Cloud的API设计,两家的设计可以说是把两个不同的风格发挥到了极致,做同样的事情API的各个方面都可以设计的完全不一样。Google有一个自己的API Design规范可以在网上找到,不过真实的GCE API规范略有一些出入。而AWS的API设计是没有什么文档的,只能通过现有的API进行逆向工程来反推设计理念。这篇文章会比较琐碎,更像是一个读书笔记,细节的东西会比较多。
Google Cloud API
由于Google的文档比较详细了,就先照着Google的文档来讲,而Google的API是按照REST风格来的,而REST只是一种纸面的模式,每家的实现可能都不一样,我们就来看下Google眼里的REST是什么样的。然后介绍一下REST API的一些局限和缺点以及Google的解决方式。
最权威的REST当然还是得去看论文了,不过对于普通开发者来讲HTTP的REST就是一个URL指向一个资源,然后这个URL上通过不同的HTTP方法来实现对这个资源的不同操作。简单来说就是指向资源的URL+HTTP方法就构成了常见的REST API。这里面每一个部分都有很多门道。
URL
URL可以分成好几个组成部分
1.域名,不同的域名可以区分这个网站提供的不同API服务。比如一个图书,一个电影,域名就应该是https://library.oilbeater.cn和https://moive.oilbeater.cn
2.版本号,对应不同的历史版本,可以是v1,v2,也可以是alpha,beta这样
3.资源集合,书店里可能会有多种资源的集合,比如图书,报纸,影碟,这就需要用资源集合来区分/books,/newspapers,/cds等等
4.资源名称,这时候才真正到具体的资源,一个图书可能就是https://library.oilbeater.com/v1/books/book
5.子资源,有时候一层的资源模型不能满足现实的需求,比如图书馆的检索是按照书架来的,需要先知道在哪个书架才能找到书,就需要在中间插一层资源。最后的url可能就变成了https://library.oilbeater.com/v1/shelves/shelf/books/book
6.重复34
资源命名
1.资源名必须是合法的C语言变量名,这个规范主要是为了代码和SDK的一致性,不然有个减号这样的字符很多语言的SDK里这个资源对象你就不得不换个名字或者写法了,很容易造成不一致,一些自动生成SDK的工具也会失败
2.资源集合必须是复数
3.不要用缩写避免不必要的歧义
4.不要用过于泛化的资源类型,像type,object,element,resource这样的命名一来不是很清楚,二来很容易和编程语言的关键字撞名字,人为增加编程难度。方法
常用的HTTP方法有GET,POST,PUT,DELETE稍微常见的还有HEAD,PATCH,不太常见的还有COPY,LINK,LOCK,VIEW等等。由于HTTP是纯文本的协议,并没有规定只能用哪几个方法,只要服务端做处理就可以自己再自定义其他方法的,比如Google自己就实现了一个BATCH来做批量处理。
每个HTTP方法和以对应不同的语义,而且这些语义像GET是获取,POST创建PUT更新DELETE删除基本上大家都是形成共识的,API最后的统一性会很好。REST的初衷也是让资源尽可能的多,每个资源的方法尽可能的只有标准的几个方法,这样所有的API看起来长得都很像,学习和实现的成本都会很低。下面说一下具体的每个方法需要注意的一些事情
List
1.需要用GET方法url指向资源集合,并返回资源的列表
2.List返回的内容最好有分页信息,而不是简单的一个资源列表,便于前端的展示并减少每次拿所有数据的性能消耗
3.资源列表需要是有序的,如果两次请求同样资源顺序完全不一样前端再不处理就会有奇奇怪怪的现象
4.List需要提供简单的按照某个字段排序,filter方法,更复杂的过滤查询需要单独的方法
5.可以返回一些metadata,比如资源数量,资源集合的信息等
Create
1.POST方法URL指向资源集合,创建成功需要返回这个资源,而不是只有一个201的状态码
2.需要允许client自己指定resource_id,而不是全部由系统自动生成。这样第三方系统可以根据情况进行重试和重复检查。不然一个create请求中间路径上有重试就会生成多个id不同的资源,后续处理会很麻烦。
Update
1.PUT或者PATCH方法URL指向具体资源,更新成功需要返回资源实体
2.PUT一般用于整个资源实体的更新,而PATCH只更新某个字段。对于一个复杂的有大量字段的资源,最好两种API都提供,而不是只提供一个PUT。只提供一个PUT即使只更新一个字段也需要传递完整的资源实体,很多情况下是没必要的
用户自定义方法
由于HTTP的标准方法是有限的,而很多语义是标准方法不能表示的,比如需要把书从一个书架移动到另一个书架就很难用标准方法表示,这就需要用户自定义方法。定义HTTP方法理论上没问题,但很多第三方库并不支持这种方法,这种API给别人看也会比较奇怪,所以通常会把方法加在url里,通常是加在资源名称后面。比如移动一本书就可以是/books/book:move
1.通常的做法是用『/』来做URL的分隔,但是在Google的规范里用的是用『:』因为/分隔会有歧义,不知道后面的到底是一个子资源还是一个用户自定义方法。听起来很有道理的样子,但是在GCE的API里并没这么做也是用的『/』
2.尽管URL里有了自定义方法,HTTP方法还是尽量用符合语义的,比如更新类的操作都用PUT
3.现实中很多操作都是标准HTTP方法表示起来很苦难的,比如重启机器,发快递邮件,账号登录这些,可以想一下这些操作如何用HTTP标准方法来设计API
标准字段
不同的资源有许多共同的属性字段,由于每个资源对应的API可能是由不同人在不同时间完成的,所以需要有个约定好的公共字段的命名,不然到最后会出现混乱的情况。比如资源需要有个id表示,就会出现不同资源有的叫uuid,有的叫id,有的叫resource_id;创建时间又有created_at,created_datetime,created_time,分页又有page_token,next_page,page_num诸如此类不一致的情况。同样一些字段的类型也需要统一,比如时间使用时间戳还是标准时间,带不带时区。
除了正常的返回就是错误返回也要有统一的格式。错误信息需要包含error_code,error_message和error_detail。其中error_message是用来返回给最终用户的,而error_detail则用于内部人源进行错误排查。
REST的缺陷
尽管REST API的设计原则在现实中使用的很广,但是这种设计也是有很多局限性的。
1.层级设计很容易过多,比如在公有IAAS中需要获得一private_ip的信息,这个ip可能是属于某块网卡的,网卡属于某个机器,机器属于某个subnet,subnet属于某个vpc,vpc属于某个region。设计出来的api就变成了/regions/region_id/vpcs/vpc_id/subnets/subnet_id/instances/instance_id/nics/nics_id/private_ips/private_ip_id这种API直接上就会觉得不合理。而且仔细考虑机器是不是应该属于subnet,private_ip是不是应该直接是subnet的子资源,会有很多需要考虑的地方。
2.标准HTTP方法大多是操纵整个资源,而这个粒度在一些情况下太粗了。比如同样是更新一个主机的操作,更新主机的一个tag和更新主机的name都是PUT一个资源实例url,而更改安全组将主机状态设置成停止也是同样的PUT方法和url就会有些不合理。尽管都是更新操作,但是更新的字段不同,功能不同,重要性也不同,用同样的大PUT就会不合适。而且这些字段可能会对应着不同的权限操作,如果任何更新都是同样的方法,之后细粒度的权限控制也会变得十分痛苦。
3.涉及多资源的操作会很难设计。REST的思想是对资源进行操作,但现实中一个操作可能会对应多个资源,这时候对于多个不同资源关系的操作REST设计起来就比较头疼了。比如给一个主机加一个eip,那么这个操作肯定要用到自定义方法了,接下来的问题就是这个方法到底是属于主机的,还是属于eip的还是两个资源都需要加一个同样的方法?而且API的设计也会影响实现的思路,在设计时API是把主机和eip当成独立的资源,那么实现的时候很可能就忽略了他们俩之间是有关联关系的,很可能到最后就会出问题。
4.批量操作变得困难。由于REST API的更新和删除url路径都是指向具体一个资源的,批量的更新删除就需要额外进行设计。比如批量关闭几个主机,API的样子就会和其他的完全不一样。如果不设计批量操作的API只是组合使用单个资源的API,当需要操作的实例多的时候很容易出现性能问题,而且如果操作时间过长,那么中间各种冲突,不一致的几率就会大幅上升。
Google的应对方案
1.层级多的问题,翻一下google cloud的api可以发现大部分的api url只有三层,前两层还都是project和zone,一个实例的url就是/v1/projects/project/zones/zone/instances/instance换句话说就是真正到了云里面的资源就没有再分层了,所有的资源层级都是扁平的在同等地位,所有的资源都是关联关系而不是层级关系。这样就避免了层级过深和考虑如何设计层级的问题,也保证了将来每个资源操作的灵活性。
2.大量的自定义方法。尽管REST本来的目的是大量的资源少量的标准操作,很显然这个显示环境中是有困难的。Google在instance这个资源的自定义方法就有20多种,最后的情况就是大量的资源加上几个标准方法再加上大量的自定义方法。通过自定义方法,每个方法对应一个操作,权限控制也可以对应到每一个方法上,细粒度的权限控制也就可以实现。
3.HTTP BATCH方法。至于批量操作的问题Google是通过自定义HTTP方法实现的,之前说过了不要用自定义HTTP方法因为大家不支持,可是Google这种能对Web标准起到影响的企业就任性了。BATCH方法其实就是在HTTP的body里再封装多个标准的HTTP方法求情。这样批量操作还是一个个的操作,不过可以一次性打包发过去不用一个个发快递了。API也不用单独再设计一套批量的,还能组合不同类型的操作,不过一般企业就不要学这种作风了。
尽管Google很详细的做了API设计的文档,但是如果你去看国内IaaS厂商的API文档会发现没有一家是这么做的,甚至连一点REST的模样都没有。Google的API设计看上去最符合开发者的常规思维,在这个领域反而显得和一个另类一样,为啥子呢?如果你看过AWS的API就会发现国内这些厂商大概都是从AWS那边借(chao)鉴(xi)过来的。至于AWS是如何应对REST世界中的类似问题以及他到底长什么样子,可能下篇会写。
特别声明:以上文章内容仅代表作者本人观点,不代表ESG跨境电商观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与ESG跨境电商联系。
二维码加载中...
使用微信扫一扫登录
使用账号密码登录
平台顾问
微信扫一扫
马上联系在线顾问
小程序
ESG跨境小程序
手机入驻更便捷
返回顶部