关于供应链:得物技术供应链库存幂等实战分享

7次阅读

共计 3058 个字符,预计需要花费 8 分钟才能阅读完成。

日常工作中,很多场景须要咱们保证系统操作的幂等性,先来理解下什么是幂等。

引自百度百科:

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

在编程中一个幂等操作的特点是其任意屡次执行所产生的影响均与一次执行的影响雷同。

引自维基百科:

    Idempotence is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application. The concept of idempotence arises in a number of places in abstract algebra (in particular, in the theory of projectors and closure operators) and functional programming (in which it is connected to the property of referential transparency).

幂等了解总结:

咱们提取一下要害信息:一个幂等操作的特点是其任意屡次执行所产生的影响均与一次执行的影响雷同。明确指标,在服务不出错的状况下,尽量去达成这个指标。

实战背景

内部零碎对接库存操作时,所有带业务含意的操作只能失效一次,否则会因为一些流量重放等操作导致库存错乱。因为业务场景的须要,此时就须要保障接口的幂等性。如果针对每个接口独自去做幂等,十分费劲,而且须要思考业务的边边角角。除此之外,每个业务开发同学在针对不同场景写的幂等计划可能也不尽相同,前期保护老本也较高。

在此背景之下,咱们想设计一个公共的幂等组件,想达成以下几个指标,达成的指标越多越好。

指标

  1. 通用性,尽量满足所有幂等场景,大部分幂等场景都反对
  2. 易用性,应用简略,如果学习老本太高,对于开发者来说,还不如本人写一个
  3. 代码无侵入性,如果代码侵入太多,会导致代码不够优雅,好看,拖拖拉拉。且影响代码的可浏览性和前期维护性
  4. 独立性,依赖尽量少,量级要足够轻(否则因为须要引入幂等组件,我的项目中引入一堆无用的内容进来,对本来的业务流程造成较大的影响。例如,一个接口均匀耗时 100ms,引入这个组件后,变成了 200ms,耗时间接 double,这个是相对不能容忍的)
  5. 易拓展性,不便前期迭代保护

设计思路

* 设计流程图

疑难点


DB 选型(mysql/mongo)

业务场景:

要保障一个申请在响应后,之后每次返回后果都一样,须要保留 response 数据,当同一个申请进行反复申请时,间接查 DB 返回后果即可,所以须要保留 response 后果。

相同点不做赘述,次要说下对咱们来说,两者之间对咱们业务影响最大的区别:

抉择

业务特点:

  1. 须要存很多的 response,数据量大。
  2. 不打算引入事务,如果引入了事务,通过事务去做一些 rollback 变得非常简单,但它是包在业务外层的。所以当事务较大时,尽管咱们设计的幂等相干的内容回滚了,但业务流程的事务回滚级别都是业务制订的,无奈与业务放弃同步。

综上所述,最终选型 mongo。

数据落 DB 的机会(同步提交 / 异步提交)

业务场景:

解决方案

这边选型是异步提交。

解决异步提交场景的问题换了个思路,看流程图便知。

详解:

申请刚进来就会查一次 DB,判断是否有此次申请的记录,若有,且此时 mongo 中 response 后果为 null,便认为是往库里落数据的线程还没执行完,这边的线程会 retry 进行期待。直到 response 填充进去(retry 间隔时间 500ms/ 次,次数可配)大多数业务场景无非凡状况,都不须要这么久耗时。如果期待重试后,仍旧无后果。这个时候简略断定服务可能呈现了问题,或是呈现了不太正当的业务场景。

两个雷同申请同一时间戳并发过去,另一个线程如何正确返回

业务场景:

同一时间戳来了两个雷同的申请,会因为惟一键束缚报错。但更好的解决状况是,此时也能正确的返回后果(起因如引言中幂等所述:在编程中一个幂等操作的特点是其任意屡次执行所产生的影响均与一次执行的影响雷同)。

解决方案

这边采纳 spring-retry 在查问发现插入报错后,在 rpc 框架容许的工夫范畴内进行重试。这种场景极少,retry 会短暂的 block 一下线程。

被调用方,办法外部报错,产生异样,然而初始记录曾经存在怎么办

业务场景:

A 服务调用 B 服务,B 服务执行到一半,产生异样,然而库里曾经写入数据,response 后果还没更新进去。上游产生幂等重试,会有限失败,后果也没给出。

解决方案

try-catch 业务流程,一旦产生业务流程执行抛出异样,则删除 mongo 外面记录的初始数据。

如果 DB 选型抉择 mongo,程序跑完,落数据的时刻,是异步执行的,如果此时服务公布,过程忽然没了,而 response 记录没写进去,后续申请会有异样

业务场景:

mongo 落表是异步执行的,如果此时服务公布,过程忽然没了,后续申请再进来会拿不到正确的 response。此时产生幂等反复提交场景,去表里取后果就始终有问题。

解决方案

如果 DB 抉择是 mysql,通过事务 rollback 不须要思考这种场景,此刻只以 mongo 去做探讨:

目前这个场景在设计上是不思考的,曲线救国,通过优雅公布,去防止此场景。

实践上是不会呈现过程执行到一半,过程忽然被 kill 掉这种场景的。如果因为思考此场景而 引入事务回滚等机制,为了这一个很小很轻微,目前不会呈现的点,而引入很多其余技术手段来保障,会影响业务耗时。在进行取舍之后,抉择了目前不肯定是最好,但最适宜咱们的计划,不思考过程忽然被 kill 掉,如果切实是呈现此问题,能够手动修下数据。

是否轻量级,最终引入的第三方依赖

mongoDB

最后的思维是为了保障各种各样边边角角的场景的幂等,过后想引入 mysql,mongo,分布式锁,事务等一系列依赖,想做到尽如人意。

放弃引入少量组件的起因如下:

最初瞄准指标,思量再三,联合目前绝大多数场景具体分析后,打算只引入 mongo 去实现,否则为了解决极少数特地细微末节的问题,引入一大批组件,损耗了很多没必要的性能,解决了简直不可能产生的问题。反而起到了轻重倒置的成果。

表字段

如何应用

学习老本:预计 3 分钟

次要 3 个步骤

  1. 引入 pom
  2. 加个正文
  3. 加个幂等入参

三分钟教你如何保障接口幂

第一步:

第二步:

配置 DB 连贯,波及 DB 连贯敏感数据,故不粘贴具体图片。

第三步:

间接在接口上增加。

第四步:

间接在接口上增加。

注意事项

requestId 反复将会间接返回上一次雷同 requestId 的处理结果, 请确保该接口是否实用幂等场景, 实用幂等的场景应该为:requestId 只能被惟一胜利解决一次, 雷同 requestId 能被胜利解决屡次的场景, 均不实用幂等场景!

将来倒退

目前为了保障业务的疾速倒退,只是做了较为简单的一版,在本人我的项目内进行了应用。

后续想要做的更好,更通用,还有一些改良点:

  1. 比方 DB 反对各种选型,依据服务调用方适配,想选 mysql 就选 mysql,想选其余 DB 就选其余 DB(有啥选啥,做到业务方可配置)。
  2. 目前存储在 DB 里长久化的数据是须要归档的,工夫一长,记录的数据量会特地大。须要业务方应用去限度工夫,比方库存操作对一个月内的操作做到幂等,领取转账等场景同理。保留工夫也做到可配置。
  3. 开源,反对各种场景,包含 MQ 等等。

END

文 | 平川

关注【得物技术】

正文完
 0