对接第三方开发平台的一套模板思想

1 前言 

小明接到上级的需求 需求是做一个对接第三方开放平台(支付宝,微信,飞书开发平台等)的一个审批模块,可以给上游服务调用 并能让上游感知审批的变化 这里就举例是erp服务,于是小明开始进行设计

开始设计第一版

  1 先对审批记录模型做了一个设计

      首先考虑封装成一个starter,因为第三方不支持同一个单号二次发起审批(会存在驳回再发起),需要在单据上加审批单号,没法使用调用方传的单据做为第三方幂等,所以单独一个模型来承载它

  画图水平比较水 将就看下

 文字描述就是总共分为了5步

1 第一步接受到请求开启事务

2 第二步落库审批记录

3 第三步发起审批请求

4 成功或者失败落库状态和第三方实例id

5  将接口返回给调用方

设计完后  因为会存在以下几个问题

1 链路太长 容易超时 

2  在第三步发起审批请求的时候 超时了 但是飞书端发起审批到了审批人手上

     在审批服务中 认为调用失败了 于是整个事务回滚,因为传给第三方的是审批记录的recordNo,而不是调用方传的orderNo,第三方没法做幂等,传orderNo又不满足发起多次,同理支付中,一个订单要是可以支付多次,也不能用订单号直接传给微信或者支付宝做幂等

小明开始想用监听调用方的消息,这时候会存在说下次对接别的服务,再发一个消息,那你又得开发了,消息协议是由调用方制定,那你还得去跟他们扯皮,应该用接口,来定义接口协议,调用方遵从你的协议,你在内部再用消息解耦

于是小明开始进行了第二版设计

核心思想是解耦和将事务剥离在调用第三方之前,保证最终一致

 文字描述就是总共分为了5步

1 第一步接受到请求发消息,成功后返回给调用方成功

2 消费消息,落库审批记录,并提交事务

3 第三步发起审批请求

4 成功或者失败修改状态和第三方实例id

5  将接口结果返回给调用方,分布式就通过消息(或者rpc的spi框架),单机就通过接口来扩展,调用方实现

于是小明就得思考一个问题了 重试下 这整个链路上的5个节点怎么保证都成功,且不会被重复执行

这里就涉及到了一个幂等的问题,小明开始思考,怎么做

于是小明开始想其它办法 发起原因是只考虑是前几步,第四步更新状态成功但是第五步通知单据是可能失败的,导致新的一轮重试,数据库根据单号判断只能幂等第一步的重复创建并且要求调用方一个单号不能调用2次整个链路的节点都要考虑最终被执行和不被反复执行,单单依靠数据库记录状态搞不定了 开始想其它办法,这时候发现公司有一套幂等框架 假设基于redis实现的,aop方式在方法上面打上注解即可使用,思想是在方法执行前根据幂等ID方法签名入参等生成一条记录,在方法执行完后不抛出异常就去更新记录

这时候直接用幂等框架作用在各个节点来实现幂等就能解决上面的问题了

这时候又出现了一个新的问题,调用方是可以根据同一个单据重复发起审批的,被驳回了又可以审批,那这样就又不通用了,后面小明想出来不用单号做幂等,因为这个接口是通用的我来定义幂等规则,那就调用方要遵守我的规则 干嘛不提供一个requestId来调用方,调用方自己保证唯一,这样我这边就不用考虑用什么做幂等key,只用调用方给的做全链路节点的幂等就可以了,这也是一种依赖倒置的思想。我来提供,必须调用方遵循我的规则(同一个单号不能发起二次审批),同时我还得适配生成幂等建的逻辑,举例下次不是erp来调用,是订单服务来调用,可能他是根据订单号+用户id,,但是如果调用方给,那么我逻辑就是通用的,但是如果你这个流程的代码是标准的,是不跟随外面调用发不同而更改 那你就不需要外界给你传幂等键,你自己找到幂等键做幂等就可以

小明又开始想另外一个问题 为啥我每个节点都要做幂等。这是因为该流程下的每个节点都是原子性的,业务逻辑是由N个原子节点组成的链路,并没有包裹在一个大事务里 ,如果是正常的一次增删改,一个事务,里面会有N个节点,但是需要在每个节点做幂等出来吗,答案当然是不需要的,因为原子操作 失败都失败了 再进行重试,都在执行一遍即可

这时候还有一个问题就是为啥要做幂等,如果只请求一次 会需要考虑幂等吗,答案当然是不需要,因为重试和幂等是如影随形的,没有重试,做幂等就是瞎幂等

其实还有最后一个问题就是怎么幂等(前提是不能用单据号幂等,因为一个单据可以发起2次审批,但是第三方不支持

1 小明全链路用请求id幂等, 包括插入记录

2  小明是判断是否插入记录用业务逻辑幂等(如果存在关联单据已创建或者创建中)就不插入,后续的用请求id

如果用户提交审批后,调用了审批服务超时了,实际上成功了,于是用户以为提交失败 又提交了一次,于是又再调用了一次,如果插入记录是根据请求id幂等的,因为请求id,调用方不能用单据,所以会给一个新的id表示这个是一次新的请求,代表就会生成2条,发送2次审批,所以当调用方无法确保同样的参数给你同样的请求id的时候,就不能用这个

这时候我们思考下幂等的方式有2种,一种是基于请求id+幂等框架来做的,一种是基于业务逻辑+数据库做判断,什么场景适合这2种方式呢

我觉得是

            1 当业务逻辑是类似的一个提交 但上游无法确保同样的参数给你同样的请求id的时候,就不能请求id,只能业务幂等

            2 当业务逻辑是类似的一个提交,这种是可以用业务逻辑幂等,同时也可以用请求id幂等的时候,这就取决于谁来制定幂等规范

             3 当业务逻辑是一次类似加钱或者是单据的回调 那这种无法使用业务幂等,只能用请求Id,这里插一句,为了防止用户看到报错,又点击了一次加钱按钮(一个新请求id),这种需要事务提交后基于领域事件驱动后续节点来做重试达到最终一致,用户无感知报错

               4  当你的链路有分布式节点,例如mq或写redis或es之类的,他们的幂等只能用requetId,上游不给就需要自己根据业务逻辑造一个

               

  

                        

最后还有一个小小的问题 就是接口需要支持并发 那是先加锁还是先幂等呢,答案是先加锁再幂等

        因为假设有A,B2个请求同时到达,A和B幂等框架校验都校验不住,于是都进到方法里,这时候A抢到了锁,B刚好时间片轮转被夯了100ms,A执行完后刚好花了90ms,将锁释放,于是B也拿到了锁 开始执行业务逻辑,这样就执行了2遍,当然这里的锁是分布式锁,还有锁的范围要大于事务,因为如果是可重复读,读到的都是快照,幂等没啥用,读已提交可以解决但是会存在锁释放了事务未提交,所以依旧幂等没作用,所以锁范围建议大于事务,大于幂等

如果是基于数据库的乐观锁,锁的范围是读到version和事务提交这中间的时间,这里分2种

第一种是version在事务里面读,如果是可重复读 那锁的范围就是整个事务,如果是读已提交(那就是读到version到事务提交这段时间)但这样都是会有问题的 因为锁的范围小于幂等注解范围 除非你设置事务的aop顺序优于幂等的(仅适用可重复读), 所以尽量避免这种写法


public class Arepository{
    
    @事务注解
    @幂等注解
    public int updateA(OrderDo do){
       1 读到version
       2 version控制
       3 并发抛出异常
       4 正常执行
   }
}

 如果非得在事务里面查询version 那要在中间查询下业务是否已经执行,这样才算锁的范围大于幂等的这段代码(查询或者基于注解的)


public class Arepository{
    
    @事务注解
    public int updateA(OrderDo do){
       1 读到version
       //查询该业务逻辑是否已经执行或者aop走幂等注解
       2 version控制
       3 并发抛出异常
       4 正常执行
   }
    
   
}

第二种就是version在事务外面读的传进去的,这种就是锁的范围包括了幂等逻辑,这样就没问题

public class Arepository{

    
    //orderDO的version由上层从数据库查到了传进来的
    @事务注解
    @幂等注解
    public int updateA(OrderDo do){
       1 读到version
       2 version控制
       3 并发抛出异常
       4 正常执行
   }
}

幂等判断,在锁之前,那么并发情况下就会有几率执行2次,在涉及到支付的时候更加要注意,所以就是拿到锁后,第一件事就是判断业务逻辑是否已经执行或者做幂等操作,我们尽量采用第二种方案,就是version是在事务外面拿的,这样第二种写法就没问题

总结

 通过这次小明开发的对接第三方的需求 我们明白大多数是可以直接采用第二版的设计的

核心思想是

1首先通过消息来解耦,对外暴露接口,内部消息自发自接

2 然后事务要先于第三方请求提交之前把事务提交,后续依靠最终一致,同时在整个链路的每个原子节点做好幂等操作,这样就不会导致第三方成功我们本地落库失败而导致大事务回滚,本地任何记录都没有

3 幂等键由调用方提供 因为提供出来的服务是很标准的 不会说不同的调用方要写不同的代码

4 锁的范围要大于幂等的范围,乐观锁就是version建议在事务外面拿完再传进来再做幂等判断

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值