由于AWS并没有像Google一样公开出一份API Design Guide,所以只能根据 API 的模样去逆向工程最初的设计考量。既然上一篇介绍了很多 REST 的缺陷,那么这里也会介绍一下 AWS是如何处理这类问题的。由于 AWS 在公有云领域无可置疑的领导地位,国内很多的公有云也是跟着AWS样子走的,所以如果你看国内一些公有云的API(如果有)是长得和 AWS很像的。

 NO REST 

上一篇说 Google 的设计是很极限的使用了 REST,而 AWS 作为另一个分支的极限,就一点 REST 的样子都没有了。具体作对比来说:

1.     URL 路径里没有到资源的映射,AWS 甚至做到 URL 里根本就没有路径部分,只有域名,资源映射根本是影子都没有

2.    不区分 GET/POST/PUT/DELETE 等 HTTP 方法,也就是说根本就没有使用到这些 HTTP 语义的部分

3.    也不用 header,一般 REST 都会把鉴权信息放入 header 里,AWS 也没有使用到这部分 HTTP 提供的语义信息

4.     没有 request body,既然路径,方法,header 都不用了,干脆 body 也不要了

5.    返回 XML 格式的信息,现如今的 REST 返回的要么是 JSON 要么是 GRPC 这种XML 的风格有一种浓浓的上世纪的味道。

AWS 这么设计也是有历史原因,毕竟 AWS 诞生的时候 REST 的概念也刚出来,不可能上来就用,而之后为了兼容性和历史统一就一直使用这种风格了。另外听公司里年级大的一些程序员说 AWS 这种风格叫 SOAP,是当年在企业级应用里十分流行的一种设计,然而作为新世纪的程序员半天也没理解什么是 SOAP,就只能照着 AWS API 的模样来猜一下了。

来看一个创建实例的 API,从这里就能明白 AWS 的设计风格了。

https://ec2.amazonaws.com/
?Action=RunInstances

&ImageId=ami-beb0caec

&InstanceType=m1.large

&MaxCount=1

&MinCount=1

&KeyName=my-key-pair

&NetworkInterface.1.DeviceIndex=0

&NetworkInterface.1.
PrivateIpAddresses.1.Primary=true

&NetworkInterface.1.
PrivateIpAddresses.1.PrivateIpAddress=10.0.2.106

&NetworkInterface.1.
PrivateIpAddresses.2.Primary=false

&NetworkInterface.1.
PrivateIpAddresses.2.PrivateIpAddress=10.0.2.107

&NetworkInterface.1.
PrivateIpAddresses.3.Primary=false

&NetworkInterface.1.
PrivateIpAddresses.3.PrivateIpAddress=10.0.2.108

&NetworkInterface.1.SubnetId=subnet-a61dafcf

&AUTHPARAMS

 域名 

域名分为两部分,产品线名称 + 接入点域名。这里的产品线名称基本和控制台里能看到的是对应的,比如 ec2 的 API 里就包含了实例,子网,Volume,EIP 等这些资源的 API 操作。接入点一般就 amazonaws.com 尽管 AWS 在世界范围内有很多 region 但是接入点都是这一个,应该是通过 DNS 来选则离当地最近的服务器,唯二的例外一个是美国国防部另一个是 AWS 北京采用的是独立的域名接入点。可以看出 AWS API 从设计最初就是希望能够一套方法操纵世界范围内的计算资源。

此外通过产品线来划分域名也可以方便的做到流量分离,如果所有产品的 API 接入点都做成同样的,靠程序进行分离显然会比较麻烦,而且域名划分也可以很好的隔离故障域。

 URL参数 

前面说过了 API 的 HTTP 部分没有路径,没有方法,没有 header 和 body,那么就只能靠 URL 参数来传递信息了。所有的 API 参数第一个部分都是 Action=xxx,可以看出来和 REST 围绕资源构造方法的思路不同,AWS API 的设计原则的中心就只有方法。既然不需要围绕资源来构造方法,那么很多 API 的设计就会直白很多。比如之前所说的在 REST 中给实例绑定 eip 这种涉及多个资源操作不好设计 API 的情况,AWS 这里就会简单很多,给实例绑定 eip 的 API 就是Action=AssociateAddress。同样像删除实例和 List 实例这种 REST 里比较简单的设计,这里也很直白,就是Action=TerminateInstances, Action=DescribeInstances.

由于所有参数都是通过 HTTP URL 参数传递的,习惯 JSON 传递数据的人可能会有疑问如何传递一个数组或者字典这种带嵌套的复杂数据结构。上面的例子也已经给出了,其实是有方法进行转换的比如上面创建实例 API 例子中的 private_ip 部分换成 JSON 就是:
{    "private_ips": [        {           "primary": true,           "private_ip_address":"127.0.0.1"        },       {            "primary": false,           "private_ip_address":"172.31.1.1"        }    ]}
如果仔细看 API 提供的 Action,可以发现绝大部分 Action 都是复数形式的批量操作 DescribeInstances, TerminateInstances,RunInstances,只有少部分像绑定 IP 这种是单数形式的操作。这样一来 REST 中比较难支持的批量操作,这里在设计最初就考虑支持了。比较跳出 REST 的框架来看,单资源的操作也只是批量操作的一个特殊形式。

另外这样所有信息都放 URL 参数做法额外的一个好处是程序处理起来会容易一些,不必先看路径,再看方法,再看 header 最后看 body 这样从各个地方拼出完整的信息,只需要统一从参数里找就可以。权限相关的处理在这种模式下也会很方便,方法名字直接就出来了,关联的资源 id 也可以从 URL 中直接找到,不用再去解析 body 来拿一堆信息才可以做鉴权。

 Name or ID 

作为定位资源的唯一标识符,可以用资源的 name 也可以用 id,AWS 的设计中 API 定位一个资源必须要用 ID。用 Name 定位的好处就是 API 看起来对人类比较友好,但问题也会很多。比如需要保证名字的全局唯一性,或者保证名字在某个 namespace 下是唯一的,这就需要很多额外的成本,API 和权限的设计和实现也需要考虑 namespace 会多加一层复杂度。另外当用 name 作为唯一标示的时候,变更名称的操作也会变得复杂。

在 AWS 的体系中,名字是可以任意重复的,在 API 中 Name 只能作为过滤条件出现,是无法保证定位到唯一资源的,所有定位资源都需要用 ID。这里面也有少量例外,比如 LoadBalancer 由于会生成域名,名字本身就是全局唯一的,就可以直接用名字来定位了,而且这类资源也是没有 ID 的。一般认为 id 的问题是格式太长了对人类不友好,看起来也别扭。但其实 ID 没必要像 UUID 那么长,在AWS 中一般资源的 ID 格式是资源名简称 + 八位十六进制字母,比如 instance 可能是 i-AB098884, volume 是 vol-FF122542, securityGroup 是 sg-AAA223333 这样的话通过资源名先做个隔离,八位十六进制本身能容纳将近 40 亿个名字,对于很多系统来说这个名字空间已经足够了。(然而最近 AWS 新的资源 id 部分已经到 16 位了,天知道他们有多少资源)

 特殊字段 

1.      RequestID:AWSAPI 返回的 Response 都会带有一个 RequestID 字段,一方面客户如果出了问题可以根据 RequestID 来提工单。另一方面,后端系统也可以通过这个 RequestID 把多个系统串起来,方便定位问题

2.     DryRun:这个字段的意思就是只做参数检查不会真正的区操作资源。有这么个参数的原因大概是因为 AWS API 比较贵…… AWS 上大部分资源都是按小时计费的,比如你在测试代码的时候调用了个创建 instance 的 API,即使你很快就释放也是要交一个小时的使用费用的。如果多做几轮测试,没事再跑个自动化测试,那钱就哗哗的流走了。而像停止删除这类操作虽然不花钱,但是却很危险,万一写代码过程中哪里错了停了所有服务器影响就大了。所以还是用 DryRun 这种来做测试,参数 OK 没啥问题,再真实的跑一下看看实际情况。(www.alauda.cn)

逆向 AWS API 设计的更多相关文章

  1. 移动互联网实战--Web Restful API设计和基础架构

    前言: 在移动互联网的大潮中, Web Restful API逐渐成为Web Server重要的一个分支. 移动端和服务端的交互, 主流的方式还是通过Http协议的形式来进行. 请求以Get/Post ...

  2. 从商业角度探讨API设计

    为Web设计.实现和维护API不仅仅是一项挑战:对很多公司来说,这是一项势在必行的任务.本系列将带领读者走过一段旅程,从为API确定业务用例到设计方法论,解决实现难题,并从长远的角度看待在Web上维护 ...

  3. javascript的api设计原则

    前言 本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块.系卤煮自己总结的一些经验和教训.本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来.很难做到 ...

  4. (转载) RESTful API 设计指南

    作者: 阮一峰 日期: 2014年5月22日 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备......). 因此,必须有一种统一的机制 ...

  5. RESTful API 设计指南

    转自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机. ...

  6. GOTO Berlin: Web API设计原则

    在邮件列表和讨论区中有很多与REST和Web API相关的讨论,下面仅是我个人对这些问题的一些见解,并没有绝对的真理,InnoQ的首席顾问Oliver Wolf在GOTO Berlin大会上开始自己的 ...

  7. RESTful API 设计最佳实践

    背景 目前互联网上充斥着大量的关于RESTful API(为了方便,以后API和RESTful API 一个意思)如何设计的文章,然而却没有一个"万能"的设计标准:如何鉴权?API ...

  8. 我所理解的RESTful Web API [设计篇]

    <我所理解的RESTful Web API [Web标准篇]>Web服务已经成为了异质系统之间的互联与集成的主要手段,在过去一段不短的时间里,Web服务几乎清一水地采用SOAP来构建.构建 ...

  9. 从英文变形规则计算到Restful Api设计

    ➠更多技术干货请戳:听云博客 一天在研究Restful API设计,命名的时候我总是很纠结,我相信大多数人也有这种感觉,不是说想不出来某个单词怎么写的问题,像我这种没事背单词背到13000词量的人也要 ...

随机推荐

  1. linux添加新磁盘和创建分区

    Linux磁盘概念及其管理工具fdisk:http://www.linuxidc.com/Linux/2016-08/134664.htm 一. 进入linux虚拟机 右键 open in termi ...

  2. SpringBoot------整合MyBatis

    1.添加pom.xml需要的依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="htt ...

  3. Python学习笔记(二)

    标识符和关键字 1,邮箱的Python标识符是任意长度的非空字符序列(引导字符+后续字符.) python标识符必须符合两条规则--标识符区分大小写 (1)只要是unicode编码字母都可以充当引导字 ...

  4. svn中status为missing的文件的处理方式

    svn中status为missing的文件在每次commit时都会出现在待提交的列表里,而且它的上级文件夹前面有个红色的点,有碍观瞻,处理方式也很简单: 1.在待提交的列表里,右击该文件->de ...

  5. linux系统关闭指定服务的方式

    1.根据名称称查找并关闭:pgrep -f 名称 | xargs kill -9 2.根据端口称查找并关闭:lsof -i:端口 | grep LISTEN|awk '{print $2}'|xarg ...

  6. Install Local SQL In Mac OS

    extends:http://www.cnblogs.com/maxinliang/p/3583702.html 一.安装 到MySQL官网上http://dev.mysql.com/download ...

  7. 在node环境使用axios发送文件

    yarn add form-data (async () => { const l = console.log; const axios = require("axios") ...

  8. OJ#1002 又是a+b

    题目描述: 给定两个正整数a.b(0 < a,b < =10000),求出a+b的和 输入描述: 多组输入,每组输入为一行,里面有2个数a,b(0 < a,b < =10000 ...

  9. 微信小程序之点赞和取消点赞

    wxml代码 <image wx:if="{{collection}}" src="/images/boy.png" bindtap="toCo ...

  10. mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

    错误情况 解决办法: 首先查看mysql的命令 其次修改root用户的密码 set password for 'root'@'localhost' = password('123456'); 最后退出 ...