概述
下面是一个订单系统向支付系统发送请求的一个场景:
这是一个极具代表性的例子,用户提交一次请求,订单系统向支付系统发出支付请求并处理回执。通常在开发这种订单交易系统的时候,会遇到几种常见的问题,下面是个人对此的一些总结:
1.支付系统API通常需要一个“订单号”作为入参,而实际调用API接口时使用到的往往不是正在意义上的订单号,而是交易流水号。
这个很好理解,像银行或者其他比较强势的公司,他们开出的接口是我们这些调用方无法更改的。实际上他们的设计中,会使用“渠道号+订单号”唯一的标准来设计,这样更加简单。但是对于接口的调用方就需要付出相应的代价来完成这一逻辑。这里就引入了流水表的概念。
一条订单在支付时会创建一条支付流水数据,这笔流水会关联订单数据,并将流水号发给支付系统,根据回执处理流水和订单数据。由于是调用远程接口,接口内部是我们不可控的,所以会出现各种各样的情况,如成功、失败、超时、丢包等等。因此一笔订单可能对应多笔流水,每一笔流水都对应着一次响应结果。
2.对于同步回执的处理(这里只是提供一种处理方式,具体情况具体分析)
上面提到由于远程调用的不可控,可能会在发出请求后收到成功、失败或者无回执的情况。
成功:根据实际业务处理。
失败:需要更新流水状态,通过定时任务进行补单操作。补单时创建新流水,调用支付接口,与同步调用时一致。
无回执:订单状态在没有明确的处理结果前,不做操作。通过补单的定时任务处理一段时间前的处理中订单,比如说5分钟前的订单。在操作前一定要先进行远程查询,确认状态上笔操作状态。
3.事务问题
同一个事务方法中不应该有远程调用接口,因为远程接口的不可控,可能会导致本地资源一直被占用,尤其是数据库连接。一旦数据库连接池用尽,将导致程序僵死。处理方法可以将本地事务与远程调用分开。
4.并发问题
并发问题出现可分为2种,同一个war中多线程情况,分布式系统中多个war的情况。synchronized关键字只能处理同一个war中多线程的情况,在分布式中起不到锁的作用。所以这里需要使用到分布式锁,如redis分布式、zookeeper分布式锁、数据库锁(数据库是没有集群部署,只有主备之分)等。下面会简单介绍一下通过乐观锁如何处理。
5.异步回执问题(这里只是提供一种处理方式,具体情况具体分析)
接收到1个回执:按回执处理结果。如果异步回执与同步回执发生矛盾,已异步处理结果为准,处理业务逻辑。
接收到多个回执:处理方式与4相同
没有接到回执:根据需要,可与同步请求中没有接收回执时处理类似。
下面给出一段伪代码:
同步交易方法(){
@事务一
查询订单;
(修改订单状态为处理中状态:update order set status = 交易中 where id = 订单号 and status = 待支付)
int updateRows=更新订单数量;
if (updateRows==1) {
//无论是并发还是分布式,通过数据库乐观锁能进入到这里的只会有1个线程
流水号 = 新建流水;
}
if (updateRows = =1 && 流水号存在) {
try{
response = 调用远程接口;
@事务二
if (response==成功){doSuccess();}
if (response==失败){doFailed();}
}catch(...){
@事务三
超时处理或者其他处理;
}
}
}
异步回调处理也类似,需要注意的是,为了防止支付系统多次对用一笔订单产生多次调用,也需要通过乐观锁来处理数据。确保一笔交易无论是一次回调还是多次回调,处理结果一致,都只处理一次。否则会发生一些意外状况。
定时任务这里就不写了,反正都是伪代码,根据业务再分析吧。
最后
以上就是结实凉面为你收集整理的分布式交易一致性问题的全部内容,希望文章能够帮你解决分布式交易一致性问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复