您的位置:首页 > 其它

分布式交易一致性问题

2016-10-19 00:42 246 查看
下面是一个订单系统向支付系统发送请求的一个场景:



这是一个极具代表性的例子,用户提交一次请求,订单系统向支付系统发出支付请求并处理回执。通常在开发这种订单交易系统的时候,会遇到几种常见的问题,下面是个人对此的一些总结:

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(...){

@事务三

超时处理或者其他处理;

}

}

}

异步回调处理也类似,需要注意的是,为了防止支付系统多次对用一笔订单产生多次调用,也需要通过乐观锁来处理数据。确保一笔交易无论是一次回调还是多次回调,处理结果一致,都只处理一次。否则会发生一些意外状况。

定时任务这里就不写了,反正都是伪代码,根据业务再分析吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息