最近项目上有个跨平台的接口会偶然出现重复传输问题。逻辑是A单据审核后生成B单据,然后将B单据调接口传另一个平台生成相应的后续单据。但现在另一个平台生成了两张后续单据,能追溯到我这边的A单据,但是不能追溯到直接上游单据B单据。一开始以为是重启服务导致的部分单据偶发这种问题,后面发现与重启时间对不上,便着手解决。由于问题是偶发的,不能复现,所以只能通过服务器日志和看代码去分析问题并解决。
一、分析问题
由于该接口所在的服务是部署在两台服务器上做负载均衡的,而且每次出现这种问题的时候业务反馈都是说耗时特别久,一直在卡在审核的界面没有反应。那初步判断是由于审核触发的后续操作以及接口到另一个平台的操作耗时太久,近而导致服务器触发重试,然后这个模块是用了两个服务器去承载服务的,于是重新传输的时候,两个服务器都进行了重传,进而导致了重复的问题。
后续的服务器日志也验证了我的判断,两个服务器分别发现了一个单据的接口传输的日志,即一个单据调了两次接口。同时发现出现这种重复问题时,从审批触发审批后规则进行调接口到完成,耗时共计30秒。而且其中一台服务器日志显示,在调接口时,接口都返回报错提示了。那为什么另一个平台还是能生成单据呢?说明这个服务器单独调用该接口两次,然后后面一次接口调用返回了错误,于是另一个服务器再次重调。
还有个问题是,为什么只有一张能溯源到我这边的单据呢?分析代码发现,原来是B单据保存方法报错了,但报错被塞到一个返回对象里,于是上层方法没有catch到错误,就继续运行了。于是就另一个平台就生成了两张单据,但是我这边却只有一张上游单据的问题。
/**
* A单据审核后规则方法
*/
public void process(Confirm clientEntity) {
ConfirmDto cDto = confirmService.findOne(clientEntity.getId());
ReturnInfo pushExpense = confirmService.pushExpenseBill(cDto);
if (pushExpense.getStatus() == 0) {
throw new BusinessException(pushExpense.getMessage());
}
ReturnInfo sendDtoToNcc = expenseService.sendDtoToNcc((ExpenseDto) pushExpense.getData());
if (sendDtoToNcc.getStatus() == 0) {
throw new BusinessException(sendDtoToNcc.getMessage());
}
}
/**
* A单据审批后转换为B单据方法
*/
public Re