基于订单号可重入的交易系统接口设计探讨

在交易系统的设计和实现中,接口的可重入性设计是及其关键的,可重入性也叫接口操作的冥等性保障,那么什么叫冥等性呢?在交易系统中,为什么需要这个特性?

冥等性,顾名思义,就是接口的多次操作和一次操作的效果是一样的,这里的效果指的是对后端系统的状态变更不存在副作用(side-effect)。举一个例子,比如查询类接口,都是具有冥等性的,对同一输入参数的多次查询操作,都没有对后端系统的状态变更产生副作用,或者说,后端系统的状态不会因为该查询接口的调用而发生变化。当然,我们这里所说的冥等性,不是指查询类接口,而是指写入类或者变更类接口,变更类接口一般都会对后端系统的状态进行变更操作(即写操作),那么,在交易系统中,为什么该特性及其重要?这是因为交易系统一般都是资金敏感型系统,每次的系统状态的变更对应了资金或者订单状态等关键业务变量的变更。若服务调用方调用服务方由于网络等原因导致前一次接口调用返回不明确的时候,此时服务调用方是无法确认此处的交易是否已经成功提交的。举个例子,该交易接口是一次往某个朋友转账1000元的接口,每调一次转账1000元,这个时候,调用方单独通过该接口返回(超时了,返回的是系统繁忙类似的信息)无法判断本次是否已经成功转账,那么这个时候,调用方可以怎么做呢?方法可能有三种:1,调用一下查询接口,查一下该笔转账订单的状态是否已经成功;2,继续用同样的参数调用一次该接口,这叫重试;3,等着隔天对账的时候,再确认改笔转账是否成功。而其中第2种方法存在较大的风险,假如前面那次转账其实是已经成功了,只是接口返回包的时候网络超时,使得接口调用方无法获知本次接口调用的成功结果,如果这个时候,接口调用方再次调用一次,服务提供方的接口如果没有做好冥等性的保障,则会出现第二次调用也成功,相当于转账了两笔,即重复支付导致的资金安全问题。

读者可能会问,既然这样,那接口调用方,只用方法1和3就可以了,不重试调用就是了,理论上好像也可以,但仔细琢磨还是有问题的:1,查询接口缺少事务型的保障,其实还是有一定的风险的,即单独依靠查询接口无法彻底解决这个问题,举个例子就明白了:还是上面的转账例子,调用方第一次调用转账接口返回网络超时,此时,考虑到该接口可能不具备冥等性,那么就不敢直接重试,先调用一次查询接口,查询一下状态,如果此时若订单已经支付成功则更新本地订单状态为转账成功后流程结束,若此时查询的是订单状态还是初始状态(即未转账成功),则再调用该转账接口重试一次,问题是这样调用是安全的么?答案是仍然存在重复转账的风险,即查询接口的状态判断只有终态才是可信的,其他状态都是不可信,不能作为资金交易判断的关键依据,因为,存在类似可能的实现:此时后端系统在主db可能是已经转账成功,但备db还没同步到,而基于读写分离,此时查询接口调用的是备db的数据。另外使用方法3,通过对账后再决定是否需要重新转账,这个是可行的,但存在用户体验的问题,一般对账都是隔天T+1进行,也就是说要等到隔天才能确认是否需要进行重新转账。

综上,为了更好的支持交易系统写接口的可重入性,在接口的设计和实现中,需要有一定的方案考虑,重入的关键变量集合很多,不同的业务可能有不同的考虑和设计,本文重点讲解基于订单号的重入设计,一方面是这是最常见的重入判断变量,另外一方面,该方法因为简单,通用性较强,具有很好的适配性。

在交易接口中,一般服务调用方调用某个交易系统的时候,都会传入调用方的唯一识别码,一般就是业务订单号,或者唯一序列号等,它是标明本次交易操作的唯一编码,该编码不同代表不同的交易,相同则表示是同一个交易操作,而服务提供方通过判断该编码进行判断该请求是否已经处理过,从而到达重入的效果。

方案一,基于外部业务订单号的可重入接口的设计

该方案的特点是,服务调用方每次调用交易系统的接口时候,必须自己确保生成一个唯一的订单号(或者在某个比较长的时间段内唯一也可以),然后作为交易系统接口调用的一个关键参数输入;交易系统收到该请求,为了保证可重入性,会以该外部订单号作为唯一健在db建立跟自身内部订单号的映射关系,大概如下:

图1:基于外部业务订单号的映射表

建立了这个关系后,每次调用该接口,会首先插入该映射关系,若前面已经调用过,则外部业务订单号会因为唯一健冲突导致插入失败,此时,重新检查本次请求和上次请求的关键变量是否一致,若一致则返回接口重入,否则提示接口错误。

图2:基于外部业务订单号的关键调用流程

该方法的关键点是:1,外部服务调用方只需要调用一次接口即可;2,调用方需要自己生成并保障业务订单号的唯一性;3,服务提供方,需要维护以外部业务订单号为唯一健的订单映射表,由于不同调用方的业务订单号规则不统一,使得该映射表的数据不够规范,且无法实现按日期的归档等操作。

总之,该方法的订单映射关系维护的责任在服务提供方,而不是服务的调用方。

方案二,基于内部订单号的可重入接口的设计

方案一虽能够解决接口重入的实现问题,但在实际操作中,仍然不够完美,主要在于随着时间的推进和不同调用方的订单规则不统一,使得订单映射db的维护变成一个问题,比如,累计到一定时段,订单映射表的数据是否可以删除归档?假如把2年前的删除归档,那么2年前的订单调用方不小心这个时候又调用过来,那么是认为是重入还新订单?此外,由于外部业务订单号不一定有时间的信息,因此也无法判断该订单属于什么时候,只能依靠存储的记忆,类似等等的弊端逐步的暴露出来。第2个问题是,这个订单映射的db有全局性的要求,随着云化和分布式的部署,需要对该db进行单独的处理。那么有没有更好的办法呢?方案一的一个问题是外部业务订单号的规则不可控,使得存储映射关系变成一个比较麻烦的事情,那么如果把判断重入的订单号从不可控的外部业务订单号变成自主可控的内部订单号,是不是就更简单呢?内部订单号的规则可以按照自己的规则进行制定,比如位数的统一,埋入时间信息和校验信息,埋入各种业务规则的信息等,然后基于内部订单号进行重入,其实可以去掉独立维护的订单映射关系表了,仅仅根据内部订单号的路由规则在订单表里面设定为UK即可保证,此外,由于埋入了时间信息,可以根据时间维度进行后续的订单db数据的归档,使得维护成本极大的降低。

为了实现这个能力,服务提供方需要单独提供一个内部订单号(交易单号)的申请接口,服务调用方在调用交易接口之前,先调用该订单号申请接口,申请到订单号后跟自己的业务订单号进行绑定(保障自己业务订单号和该交易订单号的一一映射),然后以该新申请的交易订单号作为重入的关键变量调用交易接口。

图3:基于内部订单号的关键调用流程

该方法的关键点是:1,外部服务调用方需要调用两次接口,但由于第一个单号申请接口及其轻量,所以成本比较低;2,调用方需要自己绑定业务单号和新申请的交易单号;3,服务提供方,需要提供两个接口,但可以去掉独立维护的订单映射表。

总之,该方法的订单映射关系维护的责任在服务的调用方,而不是服务的提供方。

基于订单号可重入的交易系统接口设计探讨相关推荐

  1. Redis可重入锁的实现设计

    1 可重入锁的需求 单纯的 Redis 分布式锁仍然有些场景不满⾜的,如⼀个⽅法获取到锁后,可能在⽅法内继续调这个⽅法,就获取不到锁了.这时就要把锁改进成可重⼊锁. 重⼊锁,是以线程为单位,当⼀个线程 ...

  2. 基于ARM处理器的LCD控制及触摸屏接口设计

    作者:menuconfig 转自:http://blog.csdn.net/menuconfig/article/details/2621231 研究了一种基于ARM处理器的嵌入式网络收音机的设计方案 ...

  3. 深入剖析基于并发AQS的(独占锁)重入锁(ReetrantLock)及其Condition实现原理

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/75043422 出自[zejian ...

  4. synchronized可重入锁

    什么是可重入锁?它有什么作用? 可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁. 说白了就是同一个线程再次进入同样代码时,可以再次拿到该锁. 它的作用是 ...

  5. Redis核心数据结构List应用场景-商品列表、缓存击穿、PV阅读量、抢红包、推送帖子、普通分布式锁、Redis可重入锁与红锁

    List应用场景 Redis之List 一. Redis list命令实战 二.商品列表 高并发的淘宝聚划算实现技术方案 SpringBoot+Redis实现商品列表功能 二.缓存击穿 什么是缓存击穿 ...

  6. 老大吩咐的可重入分布式锁,终于完美的实现了~

    重做永远比改造简单 最近在做一个项目,将一个其他公司的实现系统(下文称作旧系统),完整的整合到自己公司的系统(下文称作新系统)中,这其中需要将对方实现的功能完整在自己系统也实现一遍. 旧系统还有一批存 ...

  7. 详述重入锁-ReentrantLock

    什么是重入锁? 锁主要用来控制多线程访问的行为,对于同一个线程,如果连续两次对同一把锁进行lock,那么这个线程会被卡死在那里,这样的特性很不好,在实际的开发中,方法之间的调用方式错综复杂,如果不小心 ...

  8. 可重入锁和不可重入锁

    转自https://www.cnblogs.com/dj3839/p/6580765.html 锁的简单应用 用lock来保证原子性(this.count++这段代码称为临界区) 什么是原子性,就是不 ...

  9. UNIX再学习 -- 可重入函数和 SIGCHLD 语义

    一.可重入函数 参与信号处理的函数必须是可重入函数. 1.何为重入? 假设进程的住控制流程此刻正在调用 foo 函数,就在 foo 函数刚执行到一半的时候,内核向进程递送了信号 a:假设进程对信号 a ...

最新文章

  1. 阿里云安全中心:自动化安全闭环实现全方位默认安全防护
  2. python面向对象和面向过程的区别_Python11-01_面向对象----面向对象和面向过程的区别...
  3. 最近为一个培训公司做的配置
  4. Android 开发笔记___初级控件之实战__计算器
  5. VTK:InfoVis之WordCloud
  6. Sublime Text 3103 Crack 破解 注册码(亲测有效)
  7. docker官方文档中的dns,link,expose,publish
  8. c语言Wndproc未定义,为什么我的老是未定义
  9. java字符串去掉html标签
  10. python 调用 C++ code
  11. 蓝桥杯 ADV-224 算法提高 9-1九宫格
  12. 把C#对象转换为json字符串
  13. potel99se 文件损坏修复
  14. 微信小程序token使用(首页获取不到token)
  15. 凤凰新闻自动评论推荐软件--把自己的评论推荐到最前面,然后通过头像、用户名或者评论内容进行展示
  16. A-LOAM/LOAM/Lego-LOAM/SC_Lego_LOAM实时构建3d点云地图与2d栅格地图(octomap)
  17. 关于ssh登录时卡顿30s左右的问题调试处理
  18. gimp基本操作和抠图操作视频教程
  19. Vue 将毫秒转换为天 小时 分钟 秒 / 毫秒转 小时 分钟
  20. MDT 评测 — 华为 P30 Pro 屏幕素质报告

热门文章

  1. 苹果公司的“多样化”定义:包括加拿大人
  2. matlab学习增强学习,使用 MATLAB 和 Simulink 进行强化学习
  3. K近邻的MATLAB实现
  4. Ludwig Otto Hölder
  5. Java-买飞机票(方法优化版)
  6. 计算机视觉论文-2021-05-28
  7. Linux设备模型-1-主要概念
  8. 利用xpath爬取网名
  9. 伦敦的威斯敏斯特大教堂地下室的墓碑林中,一块震撼全世界的一段碑文。
  10. 下载源码报错Cannot connect to the Maven process. Try again later. If the problem persists, check the Maven