逆向 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 条评论
登录 后参与评论

相关文章

来自专栏cmazxiaoma的架构师之路

Redis、Jmeter、MySQL的那些事

1834
来自专栏me的随笔

ASP.NET Core远程调试

关于ASP.NET Core远程调试的具体做法可参考微软文档——Remote Debug ASP.NET Core on a Remote IIS Comput...

693
来自专栏吴老师移动开发

【iOS开发】启动时间优化,runloop的一个小技术点

刚接触项目不久,最近产品说有一个点要优化,App的启动页面显示时间太长了。一直在开发其它的app,还真没注意这个点,去看了一下还真是,有的时候启动页面的显示时间...

772
来自专栏kl的专栏

DevOps自动化组件-RUNDECK介绍、开发、部署、使用

RunDeck 是用 Java开发的自动化部署持续集成的工具应用,项目已开源。runDeck的产品属性和jenkis类似。提供web界面和restapi来给用户...

5469
来自专栏散尽浮华

Linux下快速迁移海量文件的操作记录

有这么一种迁移海量文件的运维场景:由于现有网站服务器配置不够,需要做网站迁移(就是迁移到另一台高配置服务器上跑着),站点目录下有海量的小文件,大概100G左右,...

2017
来自专栏马铖的专栏

npm5 新版功能特性解析及与 yarn 评测对比

前言 前段时间 npm 发布了 5.0 版本,提供了自动记录依赖树,下载使用强校验,重写缓存系统等功能升级和改造,吸引了不少关注。本文将对 npm5 的新功能和...

1.4K7
来自专栏达观数据

达观数据Docker 集群部署实例

1 docker简介 Docker 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本! 使用 Dock...

26710
来自专栏云计算教程系列

在腾讯云CVM上搭建Hadoop集群

本教程将介绍如何在腾讯云CVM上搭建Hadoop集群。Hadoop中是一个Apache的框架,可以让你通过基本的编程处理跨服务器集群的分布式方式的大型数据集。H...

964
来自专栏码农二狗

lumen for sae

853
来自专栏CSDN技术头条

Docker数据容器保护方式利弊

Docker是一个非常成功的Linux开源项目。它在Linux操作系统下无需增加管理器即可虚拟化应用程序。该应用程序常被抽象地误认为是操作系统(具有Linux内...

2037

扫码关注云+社区