腾讯云 API 最佳实践: 善用幂等性

有些开发者问我云服务器“创建实例”接口有一个参数“ClientToken”不知道有什么作用。本文作一个简单的解答。

  1. ClientToken 是防止重复创建资源的;
  2. 对于创建资源的接口,你总是应该用到 ClientToken;
  3. 不同的请求用不同的 ClientToken,同一个请求用相同的 ClientToken;
  4. 欢迎对不支持 ClientToken 的发货类接口提出支持的诉求。

注意这里的同一个请求不是说你参数相同就是同一个请求,而是指你的目的性。举例来说,zqfan这个用户现在创建了一台服务器,过会儿又想创建同样的一台服务器,这叫两个不同的请求。虽然参数一样,但目的就是创建规格一样的两台机器,分两次请求发送,这两个请求的 ClientToken 应该指定不同的值。

ClientToken是什么?

如文档所言:

“用于保证请求幂等性的字符串。该字符串由客户生成,需保证不同请求之间唯一,最大值不超过64个ASCII字符。若不指定该参数,则无法保证请求的幂等性。”

ClientToken是需要调用者指定的,通常你直接给一个 UUID就够了 ,例如 a9a90aa6-751a-41b6-aad6-fae360632808 。更好一点的,你可以指定一个前缀以示用途,例如CT-a9a90aa6-751a-41b6-aad6-fae360632808 。

什么是幂等性?

wikipedia上的解释说:

“Idempotence is the property of certain operations in mathematics and computer science that they can be applied multiple times without changing the result beyond the initial application.”

简单地说就是无论操作多少次,结果都应该是一样的。用数学语言来表述: x * x = x

为什么要幂等性?

在查询类的接口里,你基本上是不会想要上一次的结果,而只关心当前的结果,在你不做改变系统状态的操作时,你反复调用查询接口,其返回应该是一样的。同样的,修改操作你也不会关心上一次结果,当你重复调用修改操作时,只要这一次成功了,目的就算达到了,如果失败了,再重试(如果可重试的话)直到成功即可。删除操作同理。

但是对于一些资源创建类的,单纯的重试就有问题了。云服务器的“创建实例”接口可以一次创建一百台实例,包年包月预付费。如果这一次你调用接口失败了,例如在返回结果前网络中断了,死机了,你设置的超时时间太短提前关闭了连接等等,服务器当前的状态你是不知道的。你只能去查询实例列表,还得按时间排序,确定下到底是否创建成功了,哪一批是上一次请求创建的,最终的结果难以保证正确性。如果是在代码里呢,你该怎么办?到底是重试,还是放弃直接抛出异常?重试,你会立刻创建新的一百台实例。抛出异常,可能服务器明明成功了,只是因为你所在环境网络不稳定,你的程序就运行不下去了,你的代码健壮性就太差了。

为了防止创建资源时发生重复下单的问题,引入幂等性的概念。服务器根据唯一标识符, 在腾讯云 API 中是 ClientToken ,判断操作是否曾经发生过。如果找到了同样的标识符,则表示这个操作发生过,直接返回上一次的结果;如果没有发生过,继续执行。

例如,现在你调用“创建实例”接口,同时指定了 ClientToken=CT-a9a90aa6-751a-41b6-aad6-fae360632808 。假设接口发生异常,你立刻再次发送同样的请求,带上同样的 ClientToken ,服务器会返回上一次的结果,而不是再去创建一批新的实例。

幂等性的局限性?

严格的幂等是理想的结果,实际上,现实的系统是难以达到的。古希腊哲学家赫拉克利特说过:"人不能两次踏进同一条河流"。

例如云服务器有一个接口“查看实例列表”,它是个只读的接口,返回的实例里有个实例状态的属性,按理它应该是要幂等的,但是当你对其中某个云服务器关机后,又或者单纯的是服务器正在例行维护自动关机了,那下次再去调用返回的结果,显然和上一次会不一样的。何况每一个请求都有 RequestId 标识符,这个标识符每次都不一样以区别不同的请求。

再比如云服务器的另一个接口“退还实例”,如果退还成功,它会正常返回。当你再次退还,会返回资源未找到的错误信息。这种情况下,虽然返回的不同,但是你的目的达到了。正常删除资源和试图删除不存在的资源,它们表义不同,但实际结果是等价的,都达到了令目标资源不再存在的目的。

又如前文所述,幂等性需要记录每一个带有幂等操作标识符的请求的结果。服务器无意承担一些不必要的存储负担,因此不涉及到资源变动的只读接口,或者涉及到资源变动,但是本身就自带幂等属性的修改、删除接口都是不会有幂等操作标识符的。

我们还需要考虑高并发的场景。假设两个请求同时发起,都有同样的 ClientToken ,那么总有一个返回先后,后返回的理论上是要和先返回的一致的。但是在分布式高并发系统里,先发出的请求未必先到达服务器,先到达服务器的未必先入库。如果先入库的请求还未处理完毕,另一个请求无法入库(唯一性)又查询不到结果,只能返回一个内部异常(理想情况下应该返回操作正在执行中)。在调用者看来,可能会发生第一个返回是内部异常,第二个返回是正常的预期结果的情况。严格来说,这并不是幂等的,但是只要调用者合理安排,这种情况是可以避免的,例如失败请求后你可以休眠几十毫秒到几秒再去请求。只要某一次请求有确定的结果,后续重复的请求都将返回这个结果。

调用者使用幂等性时,需要注意,不同的请求,它的 ClientToken 必须是不一样的,同一个请求,它的 ClientToken 必须是一样的。也就是说,服务器端完全依靠 ClientToken 来判断是否是同一个请求,而不管其他具体的参数。这就要求调用者得自己确保 ClientToken 不冲突,否则你以为接口返回成功了,其实是之前某一个请求的结果 。此外,为了将来能重复查询,你还必须将其缓存或者持久化。

腾讯云对幂等性支持的情况?

原则上,我们要求所有的资源创建类接口都要支持幂等性。但是每个产品的情况是各有差异的,我们暂时无法一刀切,只能根据实际情况进行判断。有些产品可能刚开始觉得没必要支持幂等性,但是后来随着用户的诉求,或者产品自身的发展,才决定要支持幂等性。这是一个发展的过程,还请用户谅解,并对我们产品的 API 监督和督促,提出意见和建议,共同成长,创造价值。

以下是目前(2018-07-16)我掌握的,支持幂等性的 API 3.0 接口:

总结

看完了这篇短文,你明白了 ClientToken 的作用了吗?这里再回顾一下你在使用腾讯云 API 时应当采取的策略:

  1. ClientToken 是防止重复创建资源的;
  2. 对于创建资源的接口,你总是应该用到 ClientToken;
  3. 不同的请求用不同的 ClientToken,同一个请求用相同的 ClientToken;
  4. 欢迎对不支持 ClientToken 的创建类接口提出诉求。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

4 条评论
登录 后参与评论

相关文章

来自专栏腾讯移动品质中心TMQ的专栏

win32应用程序性能测试-内存篇

本文主要讲述windows平台下应用程序性能测试的内存相关的知识,通过本文了解内存基本原理和分析内存占用问题。 一、内存是什么? 1内存分为物理内存和虚拟内存 ...

2048
来自专栏腾讯NEXT学位

Nodejs探秘:深入理解单线程实现高并发原理

为什么单线程的nodejs可以支持高并发呢?很多人都不明白其原理,自己也在很长一段时间内被这些概念搞的是云里雾里。下面我们就来一步一步揭开其神秘的面纱。

3802
来自专栏小灰灰

RabbitMQ基础教程之使用进阶篇

2224
来自专栏fixzd

redis系列:基于redis的分布式锁

这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁。会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁。

752
来自专栏开发 & 算法杂谈

Unix域协议学习小结

Unix域协议不是一个真正意义上的协议族,只是一个利用socket api在单个主机上进行进程间通信的方法。它不需要走传统网络协议栈,也就不需要计算校验和、维护...

3102
来自专栏程序猿DD

分布式消息队列 RocketMQ 源码分析 —— Message 拉取与消费(上)

本文主要基于 RocketMQ 4.0.x 正式版 1、概述 2、ConsumeQueue 结构 3、ConsumeQueue 存储 DefaultMessag...

2988
来自专栏轻量级微服务

spring-boot-starter-grpc 不同序列化方式性能测试及选型

Github 地址:https://github.com/alipay/sofa-hessian

643
来自专栏向治洪

xmpp即时通讯四

     TLS协商(5节)后,如果需要SASL协商(6节)与资源绑定(7节),XML节可通过流来发送。定义了三种XML节用于 'jabber:client'与...

1805
来自专栏编程一生

linux内核中听过就能记住的概念

942
来自专栏aoho求索

基于redis的分布式锁实现

关于分布式锁 很久之前有讲过并发编程中的锁并发编程的锁机制:synchronized和lock。在单进程的系统中,当存在多个线程可以同时改变某个变量时,就需要...

3508

扫码关注云+社区