事务遇到第三方API请求如何处理

一、使用场景 :

1.功能实现需多次进行数据库操作

2.方法中包含请求第三方API接口

二、第三方API使用遇到的场景

(1) 第三方API成功或失败不影响主体业务的进行,如消息推送

处理方法: 把消息推送的方法摘出来,进行异步处理,避免请求出错影响主体业务流程;

注意 : 需把异步方法抽取到另一个类中,否则@Async不生效

img

img

(2) 第三方API与主体业务不可分割(如员工调动,员工离职相关API)

处理展示 :

① 请求API,根据API返回结果进行业务处理(进行数据库操作)

② 先进行数据库操作,再请求第三方的API

思考:①和②两种处理方式的区别?


① 请求API,根据API返回结果进行业务处理(进行数据库操作)

问题 : 如果API接口返回错误,则无需往下执行后续业务逻辑处理,直接终止

但是,如果API请求通过, 后续业务逻辑执行一旦出错,进行回滚, API无法回滚 , 导致事务不一致
例如员工离职,已从钉钉架构中移除员工,但是数据库状态回滚为正常状态,造成数据不一致
② 先进行数据库操作,再请求第三方的API

优点:请求第三方API发生错误时,可以回滚数据库数据;

可以得出结论第①种顺序(先API,后修改库)是有致命缺陷的,这种处理方式在代码里禁止使用!

二丶遇到API如何应该如何做?

1、API请求共性

(1)、明确成功

明确成功的意思很明显,这也是我们最常用到的成功情况,当第三方给你返回200状态码或OK时,就可以是明确成功了。

可能存在的问题:请求时间过长

(2)、明确失败

明确成功的意思很明显,这也是我们最常用到的失败情况,当第三方给你返回非200状态码或OK时,就可以是明确失败了,你就可以处理回滚了。

可能存在的问题:请求时间过长

(3)、结果未知

当你请求发过去了,因为第三方处理时间过长(可能是他们服务器有问题,也可能是业务复杂),导致连接直接断开了,谁也不知道他那边是否处理完成了。

可能存在的问题:超时情况

2、API请求存在的问题

(1)、API请求超时无法处理

请求API超时,程序判定修改失败,事务回滚, 但是API方法实际还在执行,导致数据不一致。

(2)、API请求时间过长,长时间占用库问题

虽然API请求成功了,但是20s才处理完成,因为在上面我们对自己库的操作已经在事务里面了,还未提交,所以将导致我们的操作被锁定20s,当堆积的多了,就会产生问题。

3、解决方案:

方案1: 解决超时问题

如果有条件- 调用方和被调用方协调好,可以为调用方提供回调或轮询查询接口

回调:比如超级店长调用千云,千云给超级店长提供一个回调接口,返回处理结果

轮询:超级店长定时去请求千云,获取结果

这样做的好处?

通过被调用方提供的回调或轮询功能,我们能够明确的知道自己调用是否完全成功或完全失败,不会存在模棱两个的情况。保证了第三方API结果的确定性,为事务的一致性构建了前提。

这样做的缺点

这样做没有缺点,正规的程序都会提供,唯一的缺点就是不正规的可能不提供,这也就要求我们在为别人提供业务接口的时候,要根据业务的重要程度,为他人提供回调或轮询方法。

方案2:解决库占用问题

启用编程式事务,包裹住对数据库修改的操作

    // 方法上不加事务
    public SimpleMessage testA() {
    
    
        // 进行校验参数,一些查询
        Select ***

        // 开启事务,进行一些数据库修改操作
        transactionTemplate.execute(status -> {
    
    
            update ***
            update ***

            return null;
        });
        // 进行第三方API请求
        httpUtils.post***;
        return new SimpleMessage(ErrorCodeEnum.OK);
    }

img

使用方法

这样做的好处?

自己库的多个操作数据已经执行完成,事务也已经提交释放,且api请求不在事务中,自己的库不会被锁。

这样做的缺点

虽然库不被占用了,但是第三方请求如果失败了,事务仍然会不一致。而且还是无法解决超时问题,有可能存在超时情况,结果未知导致事务不一致。

方案3:在①和②的基础上改进

在方案②的基础上对失败的情况恢复处理(使用编程式事务)

在方案①的基础上使用 回调/轮询处理

    // 方法上不加事务
    public SimpleMessage testA() {
    
    
        // 进行校验参数,一些查询
        Select ***

        // 开启事务,进行一些数据库修改操作
        transactionTemplate.execute(status -> {
    
    
            update num = 2 where ***
            update ***

            return null;
        });
        // 进行第三方API请求
        httpUtils.post***
        // 根据API请求返回结果, 如果失败则对数据进行回退操作
        // 开启事务,进行数据恢复操作
        transactionTemplate.execute(status -> {
    
    
            update num = 1 where ***
            update ***
            return null;
        });
        return new SimpleMessage(ErrorCodeEnum.OK);
    }

提示

TransactionTemplate执行时,不论发生受检查的异常(Exception),还是不受检查的异常(RuntimeException),都会执行回滚操作,所以无需担心事务不提交问题导致的重大bug。

这样做的好处

1、防止了库被占用,且得到失败的结果后能进行手动回滚。

这样做的缺点

1.写起来比较麻烦

2.如果第三方不提供回调或轮询,还是没办法保证事务一致性,所以第三方很重要。如果第三方提供,搭配①方案使用,就是完美解决方案,如果第三方不提供,将不存在完美解决方案,也间接说明这个业务不是很重要且开发人员很拉跨。

猜你喜欢

转载自blog.csdn.net/qq_43842093/article/details/130632426