幂等(idempotence)

 

在聊业务的时候,常常听到“我们的业务不能随便重试”之类的话,其实根源就在于接口的幂等性。尤其是钱相关的接口,业务总是有扣钱、送礼等等场景。这些场景都是不能发生诸如“重复扣”、“收两次礼品”等不能对账的错误。本文就来讨论一下接口的幂等吧。...

戳上面的蓝字关注我们哦

幂等主要用于网络接口的特性描述,指的是网络接口的调用可以重试,而不会带来副作用。

分布式系统的调用失败有一种情况是等待返回时超时,对于这种情况,调用方并不知道之前的请求是否成功执行,被调用方可能执行成功也可能没执行。

这时候如果接口是幂等的,调用方就可以继续发起重试请求,来确保请求能最终执行成功。

增删改查的天然幂等性

,如果是数据库自增ID明显就不是幂等的,而如果是基于ID或者KEY的参数来调用的话做存在判断能做到幂等。

,如果是基于ID或者KEY来做天然是幂等的,而如果是删除最后十条这种就天然不是幂等。

,天然不是幂等,主要存在增量累加问题,以及并发覆盖问题。

,天然是幂等的。可以说只读的接口都是幂等的。

不幂等对业务的影响

如果接口非幂等,业务该怎么办?凉拌!正常情况下不会有问题。而一旦发生超时,是没有什么好的办法的。

首先在没有收到回复的情况下是不能重试的,因为重试会导致例如加了两次钱或者扣了两次钱的错误数据。

其次不重试的话,又可能出现不一致状态。例如调用者按照失败来变更相关状态,但实际上数据层已成功。这种不一致状态的伤害,不同的业务也各不相同。展示类的业务可能刷新下就能恢复。而相反,对于交易类的业务,尤其是多步流程中的一环,一般都需要自动对账加修复,多个环节的事务性修复可不是什么容易的事情。

不幂等对容灾的影响

不幂等,要求服务下线是优雅的。也就是必须处理完所有请求才能关停服务。

这也意味这服务突然的中断——网络故障、电力故障、机器故障等是有损的。

同理,运维如果要做流量切换,也不能暴力调度。这相当于对运维也上了一层枷锁。

接口的幂等改造

对原先非幂等的接口,可以改造为幂等。

方法是在接口中添加“操作流水号”或者“KEY版本号”之类的序列号字段。

这样做前后端要付出一定的代价。额外的做一些工作,比如流水号的获取,流水号或版本号的判断。

幂等改造尤其要注意并发问题。在并发的干扰下,如果在重试的请求时,后端返回流水号过号、版本号太旧之类的信息,前端无法分辨自己的请求到底成功与否,也无法决策是否要重新取号重试。

下面我们就来讨论前后端在并发下的几种处理方式,以及相关问题。

版本号

前端操作前读取后端存储的KEY版本号,基于这个版本号发出操作指令。

后端存储KEY版本号,接到请求时判断请求中的版本号是否等于当前版本号,若等于则正常执行操作。否则返回“过号”信息。

对于“过号”信息,前端要保证幂等的话,只能重新使用新的版本号进行重试。

这种办法有问题。比如后端能收到请求和正常处理,但是应答被丢弃的情况下,前端会就会重复操作数据,如果是累加后果不堪设想。

序列号,分布式锁

前端负责对KEY进行分布式加锁,并拿取放号器发放的序列号。本次操作完全结束后再释放锁。

后端只存储最后一次执行成功的序列号,并且在请求执行前进行是否是新请求的鉴别,如果请求等于该序列号,则返回“已成功执行过”信息;如果请求大于该序列号,则正常执行。

PS:如果前端加锁成功,则请求序列号不会小于当前存储的序列号。而一旦不加锁,或者加锁失败,则可能出现这种情况,此时后端只能返回“过号”信息,而前端也不知道该请求是否执行成功,因此无法决策——又出现了不一致状态。

序列号,操作向量

分布式锁显然不是一个高效方案。因此聪明的架构师又想了个操作向量的方法。这也在一些存储集群的系统里面有应用。

前端负责拿取放号器发放的序列号。

后端负责存储前N次成功操作的序列号。只要并发过程中,对该KEY的操作次数小于N,后端均能对重试请求返回成功或“过号且失败”。只有前N+1次操作的序列号,也就是重试时间内这个KEY被写入了N次以上,才会因为没有命中存储,让后端出现“过号且不知道成功与否”的判定,从而使得前端无法决策。



举例如上图,在 N = 4 的操作向量中,存储了 8,7,5,4 四次成功的操作;请求依次的 9,7,6,2 操作就会分别得到9-“成功”,7-“之前已成功”,6-“过号且失败”,2-“过号且不知道成功失败”的返回。

这种方案免去了分布式锁,并且只要N设置得合适,就能满足绝大部分情况。偶然情况可以日志手工恢复。
关注互联网后端技术

微信公众号:后端技术

长按二维码关注


    关注 后端技术


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册