逆向 AWS API 设计

由于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 没啥问题,再真实的跑一下看看实际情况。(http://www.alauda.cn)

原文链接:http://geek.csdn.net/news/detail/248618

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java3y

从零开始写项目【总结】

从零开发项目概述 最近这一直在复习数据结构和算法,也就是前面发出去的排序算法八大基础排序总结,Java实现单向链表,栈和队列就是这么简单,十道简单算法题等等… ...

3809
来自专栏Android-JessYan

解决Retrofit多BaseUrl及运行时动态改变BaseUrl?

原文地址: http://www.jianshu.com/p/2919bdb8d09a

1543
来自专栏ImportSource

并发编程-并发的简史

1.1.A(Very)Brief History of Concurrency 并发的简史 在很久以前,计算机没有操作系统;他们只执行一个程序,从头到尾的执行...

3537
来自专栏企鹅号快讯

Spring编程式事务处理不当引起的连接泄露事件

某一日正在孜孜不倦的研究代码,忽然测试童鞋说系统服务挂了,完全不可用。 程序大量抛出如下异常: 对于程序员来说,系统宕机就是军令,更可况是难得一见的连接池泄露问...

2066

内存数据网格主要特性简介

将主存储器用作存储区域而不是使用磁盘是并不是一种全新的尝试。你可以在日常生活中发现许多使用主内存DBMS(数据库管理系统)(MMDB)执行比磁盘快得多的情况。一...

3134
来自专栏Golang语言社区

深入理解Wi-Fi P2P

本章主要内容: 介绍Wi-Fi P2P相关知识; 介绍Android中WifiP2pService、wpa_supplicant的相关代码。 7.1 概述 承...

3733
来自专栏Java社区

一招 | 利用脚本不限速下载百度网盘文件

1965
来自专栏VMCloud

【腾讯云的1001种玩法】在腾讯云上创建您的SQL Server 故障转移集群(3)

在腾讯云上创建您的SQL Cluster(1) 和 腾讯云上创建您的SQL Cluster(2) 的两篇文章中我们介绍了如何在QCloud标准化的搭建一套域环境...

4640
来自专栏Python与爬虫

抓取手机app的数据(摩拜单车)

前几天有人私信我,问能不能帮忙抓取摩拜单车的数据。。。 我想着授人以鱼不如授人以渔,所以本次我们就讲讲如何抓取手机app的内容吧 ? ?  Fiddle的安装...

39012
来自专栏Flutter入门到实战

深入理解什么是RESTful API ?

越来越多的人开始意识到,网站即软件,而且是一种新型的软件。   这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(hi...

1572

扫码关注云+社区