First Post For Idempotency

  1. 幂等设计
    1. 幂等的定义
    2. 非幂等原因
    3. 幂等解决方案
  2. 唯一ID
    1. UUID
    2. Snowflake
  3. 总结

幂等设计

为何会存在需要服务的幂等?在互联网中由于网络的不稳定和一些业务重复确认设计,对一个接口的调用存在重试的机制,为了确保执行同一个请求执行一次和执行多次的效果是一样的,所以就存在了幂等的设计。

幂等的定义

  • 请求层面

    从请求层面考虑,就是一个接口得保证请求一个和请求多次得到的效果是一致的。

    数学表达式如下:

    f...f(f(x)) = f(x)  x是参数 f是执行函数
  • 业务层面

    从业务角度来看:例如一个用户在一次购买中不能重复下单。

非幂等原因

不幂等的原因是各种Retry机制,那么取消Retry机制,是否能杜绝非幂等呢?答案是肯定的。

但是取消Retry机制是不现实的,下面一些出现Retry机制的场合:

  • 用户进行下订单,调用下单接口超时,调用方又发起一次创建下单接口
  • 用户下单进行扣减库存,调用扣减库存接口超时了,调用方又发起一次扣减库存接口。
  • 下单完毕后,生产者发送一条 MQ,MQ 超时没有及时响应 ACK,生产者又再发送一条 MQ,消费者连续就收到了两条 MQ。

造成问题的原因有几条:

  1. 应用程序对于同步超时的处理逻辑有误
  2. 定时任务重复执行
  3. dubbo,zuul等第三方框架自身的重试机制
  4. HAProxy,Nginx等中间件自身的重试机制
  5. 极限,高并发场景

幂等解决方案

  • MVCC方案, 在更新的过程中利用version来防止,其他操作对对象的并发更新,导致更新丢失。为了避免失败,通常需要一定的重试机制。
  • 去重表,在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。
  • 悲观锁,select for update,整个执行过程中锁定该订单对应的记录。注意:这种在DB读大于写的情况下尽量少用。
  • select + insert,并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行,简单的处理方法是,先查询下一些关键数据,判断是否已经执行过,在进行业务处理,就可以了。注意:核心高并发流程不要用这种方法。
  • 状态机幂等,在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机,就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
  • token机制,防止页面重复提交
  • 对外提供接口的api如何保证幂等

唯一ID

就幂等性接口来说,需要借助一个唯一的ID来标志每次交易。唯一ID的分配可以有几种方式:

  • 由一个统一的ID分配中心来分配。
  • 由上游服务来生成唯一ID,但必须保证不产生冲突的ID。

采用统一的分配中心来分配唯一ID时,业务方每次调用接口都多了一次调用分配中心获取唯一ID的请求。这多了额外的开销。获取唯一ID有一种方式,是借助mysql的自增索引,这其实也是一个ID分配中心。对服务性能有苛刻要求时,可以采用第二种方式,由主调服务本身来生成这个唯一ID。为了保持不会产生重复的ID,可以使用一下几种ID生成方法:

UUID

UUID的全称是Universally Unique Identifier,通用唯一识别码。具体可以看维基百科的介绍:UUID

UUID是一个128bit的数字,用于标志计算机的信息,虽然UUID不能保证绝对不重复,但重复的概率小到可以被忽略。UUID的生成没有什么规律,为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。这也就意味着:

  • 128bit,占据了太多的内存空间
  • 生成的ID不是人可以看懂的
  • 无法保证ID的递增,某些场景需要按前后排序 无法满足。

这是一个在线生成UUID的网站:生成UUID 你可以直观感受一下UUID。

Snowflake

这是Twitter的一个开源项目,它是一个分布式ID的生成算法,它会产生一个long类型的唯一ID,其核心算法是:

  • 时间部分:41bit作为毫秒数,大概可以使用69.7年
  • 机器编号部分:10bit作为机器编号,支持1024个机器实例。
  • 毫秒内的序列号:12bit,一毫米可以生成4096个序列号

网上有各种语言实现的Snowflake算法的实现,有兴趣的阅读一下实现代码。

实际上,redis 或是 mongoDB 的全局ID生成器的算法和Snowflake算法大同小异,这是基于redis的分布式ID生成器实现

它的核心思想是:

  • 使用41 bit来存放时间,精确到毫秒,可以使用41年。
  • 使用12 bit来存放逻辑分片ID,最大分片ID是4095
  • 使用10 bit来存放自增长ID,意味着每个节点,每毫秒最多可以生成1024个ID

总结

幂等性应该是合格程序员的一个基因,在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好 。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1522515386@qq.com

文章标题:First Post For Idempotency

文章字数:1.6k

本文作者:Blitz

发布时间:2019-10-25, 18:33:37

最后更新:2019-10-26, 17:25:41

原始链接:http://yoursite.com/2019/10/25/First-Post-For-Idempotency/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏