spring 多线程 事务的实际应用场景

大家好,我是烤鸭:

今天分享的是spring 多线程 事务的实际应用场景:

上一篇讲了大概的原理,主要是针对事务的开始和执行过程(数据库链接,隔离级别,threadlocal线程绑定)。
https://blog.csdn.net/Angry_Mills/article/details/82502288

下面从实际的角度分析一下。

以下场景的演示代码地址:

https://gitee.com/fireduck_admin/demo-mutithread.git

1.  模拟场景   

    1.1 方法未加 Transactional 注解,new Thread 方式的主子线程 事务提交情况
    主线程和子线程事务分别提交(主线程提交后子线程提交)
    1.2 方法加 Transactional 注解,new Thread 方式的主子线程 事务提交情况
    主线程和子线程事务一起提交

    下面只考虑加注解方式的情况
    1.3 主线程正常,子线程异常 
    无论在new Thread 方式的子线程的run方法加不加 Transactional 注解,子线程都是以无事务方式运行
    1.4 采用springboot的 Async 注解,主线程加事务注解,子线程不加事务注解
    主线程和子线程事务一起提交
    1.5 采用springboot的 Async 注解,主线程加事务注解,子线程不加事务注解,子线程异常
    主线程和子线程事务都未提交
    1.6 采用springboot的 Async 注解,主线程加事务注解,子线程加事务注解 @Transactional(propagation = Propagation.REQUIRES_NEW)
    主线程和子线程事务分别提交(子线程提交后主线程提交)
    1.7 采用springboot的 Async 注解,主线程加事务注解,子线程加事务注解 @Transactional(propagation = Propagation.REQUIRES_NEW),子线程异常
    主线程和子线程事务都未提交
    1.8 采用springboot的 Async 注解,主线程加事务注解,子线程加事务注解 @Transactional(propagation = Propagation.REQUIRES_NEW),主线程异常
    主线程未提交和子线程事务提交

    总结一下。
    采用 异步注解的时候,配置Propagation.REQUIRES_NEW,主线程和子线程确实是两个不同的事务,分别提交。
    但是对于子线程的异常,却两个都回滚了。子线程由于异常回滚,这个是没问题的。同时将异常抛给主线程,主线程也跟着回滚了。
    我们可以认为多线程的事务是嵌套的,任何子线程的异常都会导致整个事务的事务回滚
    但是子线程如果执行完没有异常,事务会直接提交,不管主线程是否异常

2.  实际场景

     之前遇到的场景是这样的,用户和我们发起支付是同步接口,这个时候还要请求第三方的支付接口(如支付宝的接口),这时候就有个问题了。
     由于支付的异步回调如此之快,导致用订单号去数据库查找不到数据(数据还没提交入库)。
     同一个service 做这个肯定不行,就想着把入库的操作放到异步方法里做,主要保证调用第三方接口前数据库已经入库了(子线程事务提交)。
     这时候就可以采用 上面 1.7 的做法,子线程比主线程优先提交,但是即便这样也没法保证子线程的执行一定比回调快
 

扫描二维码关注公众号,回复: 10616144 查看本文章

3.  改进

    一般第三方的回调接口都有超时时间响应,他不希望你做任何的逻辑处理。只要接收到消息返回 success 即可。
    所以将消息的接受和业务逻辑处理分开,接受到回调后返回成功,同时异步处理业务逻辑。
    如果这个时候还没入库,把回调的唯一标识(订单号和回调参数)放到缓存。
    在子线程入库后也做类似的操作,查询缓存中的订单号是否存在(存在说明回调先到了),再获取回调参数,手动调用一次异步通知。
    当然这种还是有可能出现极端情况,就是 回调方法刚查询完,放缓存/子线程刚入库,获取缓存同时进行。导致缓存中还是存在订单号。
    这种极低概率的问题,可以间隔一段时间跑补偿程序(1h 一次)。
 

发布了115 篇原创文章 · 获赞 58 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Angry_Mills/article/details/82620286
今日推荐