1.简述
博主在公司业务开发中遇到一种业务场景,一个主线程调用一个异步线程来实现业务功能,业务线程一共做两个业务,分别是业务1和业务2。业务1只要是用来和用户进行交互,而业务用来在后台运行某些功能。业务2的执行要在业务1完成以后,由业务1调用业务2的方法。当业务1方法执行完成,前端会调用一个接口请求业务1产生的数据。按照博主的设计初衷,那么前端数据只要在业务1完成后,前端就可以通过其他接口拿到这部分数据。
但是功能提测后发现,每次前端拿到的数据都要等到业务2执行完成,执行业务2需要消耗2-5秒左右去轮询查询数据的时间(正常轮询20ms就可以查到)。也就是说用户为了拿到业务1的数据,需要等待至少2秒,这对于用户体验来说是不可接受的。
2.产生原因
@Async
@Transaction(rollBack=Exception.class)
public void workerThread(){
executeService1();
executeService2();
}
上面的代码是博主workerThread的执行的伪代码,我们可以看到我一共使用了两个注解@Async和@Transaction(rollBack=Exception.class).
- @Async是SpringBoot执行异步方法的注解
- @Transaction(rollBack=Exception.class),是表明这个方案要使用事务,如果遇到了异常的话事务会进行回滚。
3.@Transaction的默认传播级别
@Transaction的默认传播级别是REQUIRED
官方文档的解释是,支持当前的事务,如果当前不存在事务就重新创建一个新的事务。
也就是说serviceA和serviceB是在一个大事务里面,serviceA的事务必须等待serviceB执行完成才能提交到数据库。
那么如何处理这种情况呢,如果让serviceA的事务执行完成后,再调用serviceB呢?
4.TransactionSynchronization和TransactionSynchronizationManager
4.1 TransactionSynchronization
可以通过这个接口实现,在事务过程中进行处理规则,我们只需要重写这个事务的afterCommit()方法,指定在当前事务提交完成后自定义处理业务。
new TransactionSynchronization () {
@Override
public void afterCommit() {
serviceB();
}
}
TransactionSynchronization是一个接口我们重写以后需要注册到TransactionSynchronizationManager中。TransactionSynchronizationManager以线程为单位,管理事务资源。
通过使用TransactionSynchronizationManager的registerSynchronization,将自定义的事务同步类注册到管理类中。
根据官方文档的说明public static void registerSynchronization(TransactionSynchronization synchronization) throws IllegalStateException 可以注册一个为当前线程注册一个新的事务同步器.
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronization() {
@Override
public void afterCommit() {
serviceB();
}
通过上述操作,我们就可以实现Spring中线程事务进行自定义提交,并且可以根据业务进行调整顺序的方案。