共计 1845 个字符,预计需要花费 5 分钟才能阅读完成。
1. 为什么须要幂等
分布式场景下,多个业务零碎间实现强统一的协定是极其艰难的。一个最简略和可实现的假如就是保障最终一致性,这要求服务端在解决一个反复的申请时须要给出雷同的回应,同时不会对长久化数据产生副作用(即屡次操作与单次操作的后果须要是业务角度统一的)。
一个 API 领有幂等能力的话,调用发起方就能够很平安的进行重试。这合乎咱们广泛的假如。
提供幂等能力是服务提供方须要做的事,所以本文是站在服务提供者的角度来写的,即下文的“咱们”通常指的是服务提供方。
2. 怎么做幂等判断
那么咱们怎么对一次申请做出幂等的判断呢?首先咱们须要有办法来辨别什么是“一次”申请,其次,针对 API 实现逻辑中长久化数据形式的不同,还须要不同的判断办法。
2.1 申请惟一标识
通常咱们的 API 须要减少 bizId 这样的可能标识申请唯一性的属性 ,调用发起方应用它来标识一次业务上认为的独特的业务需要。肯定要留神它是“业务唯一性标识”,而不是技术唯一性标识,这两者是有本质区别的。
事实 1 :不过大多数状况下,调用发起方可能并不太晓得应该如何生成一个无效的申请惟一标识,这须要咱们在业务对接的过程中有更多的交换。
2.2 须要幂等多久
可能幂等代表咱们必定做了某些数据的长久化,然而任何数据都不可能永恒存在,都会要求有一个有效期。
对于大多数的申请, 倡议幂等的有效期是三个月 。一些非凡的场景甚至能够是一年。然而简直不须要更久了。
有效期是一个明确的契约,这代表咱们能够定期对长久化数据做一些数据治理的工作,同时,超过有效期的申请基本上会幂等失败,那么它的结果就是“同样的事,在几个月后又做了一次”。
2.3 写入型
如果咱们的业务逻辑是在长久化存储中写入什么,那么最好的做法是减少一个 unique key,它通常由 user_id, 操作类型, biz_id 组成 。操作类型是咱们零碎设计上自行定义的业务操作类型,加上它是为了防止不同的操作类型之间相互烦扰。加上 user_id 是因为通常状况下咱们都会分库分表。
针对既会写入,还会更新数据的场景,须要把插入放到后面,不然幂等很可能无奈工作。比方如下的库存操作的场景,如果两条 SQL 倒过去写,在库存售磬的场景下就无奈幂等了:
insert 库存扣减流水;
update 库存 set 库存可用数 = 库存可用数 -1 where 库存 id=? and 库存可用数 >0;
在 unique key 抵触时,咱们须要捕捉它,这大概率是幂等了。为什么说是大概率而不是肯定呢?这是因为前述提前的“事实 1”。调用方很可能谬误的重用了申请惟一号,并且可能在重试时就一些外围的参数进行了改变。
所以,咱们须要在产生 unique key 抵触时做如下的事件:
- 依据惟一键,将之前写入的数据查问进去 。
- 进行要害信息的核查 。比方咱们提供的 API 是发放优惠券,那么咱们须要至多校验这些信息:接管用户 Id,券模板,券面额,有效期等。在要害信息不统一时,返回相似于“DUPLICATE_BUT_DIFFERENT_REQUEST”的错误码;在校验通过时,返回发送胜利的券 Id。这十分重要,不进行要害信息的校验就返回幂等胜利是大多数场景下故障的本源。
2.4 更新型
更新型最大的挑战在于咱们没有中央来存储对于一条数据的屡次更新行为,所以大多数状况下须要应用状态机来揣测某个更新行为是否产生过了,更简单的状况可能须要咱们减少专用的幂等表。
2.4.1 应用状态机
业务上的数据处理大多都是有状态的,比方交易订单。这个时候首选的办法是通过状态机的序列来判断某个更新行为是否曾经做过了。不过这并不简略,咱们须要十分小心的去看业务上的各种规定和限度,同时,除了更新状态之外,大多数状况下还会随同着更新一些别的附加信息,咱们还须要去查看这些附加信息是否如申请要求的那样更新过了。
2.4.2 减少幂等表
通过减少幂等表,就把更新型转化为了 2.3 节形容的写入型。不过须要留神幂等表须要和以后更新的表在同一个事务中,不然就是有效的。
幂等表须要减少表明数据写入 / 更新工夫的字段,这便于咱们定期进行过期数据的清理。
2.5 删除型
倡议在存储上应用逻辑删除而不是物理删除,这样咱们就有方法判断删除操作是否曾经做过了。删除往往意味着数据进行终态,所以在幂等上也是最好解决的;如果业务的设计上删除不是数据的终态,那么就须要相当小心,因为这违反了通用的设计准则。
3. 总结
幂等能够说是分布式应用的基石,如你所见,实现它并不像大多数人一开始想像的那么简略。做好它须要咱们站在业务语义的角度来设计与思考。
文 / 苏木
关注得物技术,做最潮技术人