踩了异步调用的一个坑

1,236 阅读2分钟

一,背景

有一天,业务反馈部分自动执行流程中断了,我第一反应是不是程序报错,排查半天也没有error错误。 那就开始扒代码呗(代码非本人开发)。

二,抽丝剥茧

直接上简化版的代码

@Transaction(rollbackFor = Exception.class)
publict Result a(){
    //做一些操作
    try{
        CompletableFuture.supplyAsync(()->
               serviceB.b();
        );
    }catch(Exception ex){
        //做一些通知操作,并不回滚a的事务,后续由补偿任务触发b的生成
    }
}
@Transaction(rollbackFor = Exception.class)
publict Result b(){
    //做一些操作
}

意思就是b的一些操作没有落库,那肯定就是回滚了,由于是异步,异常日志都没有打出来,因为捕获异常不应该在completableFuture的外面,应该这样写。

CompletableFuture.supplyAsync(()->
       try{
           serviceB.b();
       }catch(Exception ex){
           //做一些通知操作,并不回滚a的事务,后续由补偿任务触发b的生成
       }
);

这个暂且不提,现在就看b(),里面有没有线索了,好在里面有一些日志,但是日志打着就断了,看不到具体执行到哪一行出错了。结合当前错误是偶发的,也就是说部分数据异常,大部分的数据是正常执行的。测试环境当然也复现不了。

大胆猜测,小心求证,因为该方法是单机执行,假设是由于数据库执行异常了,导致了当前事务回滚,好像可以对得上,那就看下应用有无数据库的error异常,结果没有,想会不会是数据库连接超时了,找日志warn一看,果然有不少超时的连接。 那为啥,应用不报错呢,这个时候注意到数据库连接有个参数,

autoReconnect=true&failOverReadOnly=false

果然,无事务情况下,直接就重连了,也就响应慢一点,有事务的话,肯定先回滚当前事务了,这就和我们的现象对得上了。

三,解决方案

下面就是求证了,把hikari连接池设置只有1个连接,debug进去,设置为超时,执行,果然回滚,至此,原因确认了。 解决也好解决,设置好连接池的如下两个参数:

dataSource.setMaxLifetime(800_000);
dataSource.setIdleTimeout(600_000);