AOP 实现@Async异步事务管理

异步调用

在日常搬砖过程中,有时考虑业务处理性能,会使用@Async异步处理耗时操作

自定义线程池
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {

    /**
     * 自定义线程池
     * ThreadPoolTaskExecutor 是对 ThreadPoolExecutor的封装,是spring提供的。
     */
    @Bean("threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池创建的核心线程数,线程池维护线程的最少数量,即使没有任务需要执行,也会一直存活
        executor.setCorePoolSize(16);

        //如果设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
        //executor.setAllowCoreThreadTimeOut(true);

        //阻塞队列 当核心线程数达到最大时,新任务会放在队列中排队等待执行
        executor.setQueueCapacity(124);
        //最大线程池数量,当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
        //任务队列已满时, 且当线程数=maxPoolSize,,线程池会拒绝处理任务而抛出异常
        executor.setMaxPoolSize(64);

        //当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
        //允许线程空闲时间30秒,当maxPoolSize的线程在空闲时间到达的时候销毁
        //如果allowCoreThreadTimeout=true,则会直到线程数量=0
        executor.setKeepAliveSeconds(30);

        //spring 提供的 ThreadPoolTaskExecutor 线程池,是有setThreadNamePrefix() 方法的。
        //jdk 提供的ThreadPoolExecutor 线程池需要通过设置threadfactory设置线程名
        executor.setThreadNamePrefix("coolThread-");

        // rejection-policy:拒绝策略:当线程数已经达到maxSize的时候,如何处理新任务
        // CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行, (个人推荐)
        // AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
        // DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
        // DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        executor.setAwaitTerminationSeconds(60);

        executor.initialize();

        return executor;
    }
}

业务接口
public interface SourcesService {

    void jtaTest01(String mainThreadName);

    void jtaTest02(String mainThreadName);
}
@Slf4j
@Service
public class SourcesServiceImpl implements SourcesService {
    @Autowired
    TestTxAMapper testTxAMapper;

    @Autowired
    TestTxBMapper testTxBMapper;

    @Async("threadPoolTaskExecutor")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void jtaTest01(String mainThreadName) {
        // DB2 tb1
        TestTxA testTxA = new TestTxA();
        testTxA.setTestName("DB2 tb1  --- tb1");
        testTxAMapper.insert(testTxA);
        try{
            // 其他业务
            // .......
            TimeUnit.SECONDS.sleep(3);
        }catch (Exception e){
            log.error("",e);
        }
        log.info("insert db2 tb1 ...");
        throw new RuntimeException();
    }

    @Async("threadPoolTaskExecutor")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void jtaTest02(String mainThreadName) {
        // DB2 tb2
        TestTxB testTxB = new TestTxB();
        testTxB.setTestCode("DB2 tb2  --- tb2");
        testTxBMapper.insert(testTxB);
        log.info("insert db2 tb2 ...");
    }

}

异步事务

现有另一个业务接口内,会调用以上两个方法,但业务要求原子性。异步调用的话会是spring的声明式事务失效,所以思路是使用编程式事务。这里再加上多数据源,使用JTA实现多源事务

自定义注解

@MainTransaction

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MainTransaction {
    // 子线程数量
    int value();
}

@SonTransaction

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SonTransaction {

}
使用注解

将注解添加到对应的方法上,@MainTransaction 打到主线程方法上,参数为需要调用的异步事务方法数量;@SonTransaction打到异步事务方法上。

    /**
    * 主方法写数据到DB1中
	* sourcesService.jtaTest01 异步写数据到 DB2的tb1中
	* sourcesService.jtaTest02 异步写数据到 DB2的tb2中
	* 改事务一共涉及2个库,三张表
	**/
    @Override
    @MainTransaction(value = 2)
    @Transactional(rollbackFor = Exception.class)
    public void jtaTest() {
        sourcesService.jtaTest01(Thread.currentThread().getName());
        // DB1
        TxJTATest txJTATest = new TxJTATest();
        txJTATest.setName("DB1 --- db1");
        txJTATestMapper.insert(txJTATest);
        log.info("insert db1 ...");
        sourcesService.jtaTest02(Thread.currentThread().getName());
    }

AOP实现编程式事务

// 这里使用JTA事务,如果是spring默认的是 PlatformTransactionManager
@Resource
JtaTransactionManager jtaTransactionManager;

@Slf4j
@Aspect
@Component
public class TransactionAop {

    //用来存储各线程计数器数据(每次执行后会从map中删除)
    private static final Map<String, Object> map = new HashMap<>();

    // 这里使用JTA事务,如果是spring默认的是 PlatformTransactionManager
    @Resource
    JtaTransactionManager jtaTransactionManager;

    @Around("@annotation(mainTransaction)")
    public void mainIntercept(ProceedingJoinPoint joinPoint, MainTransaction mainTransaction) throws Throwable {
        // 获取当前线程
        Thread thread = Thread.currentThread();
        String threadName = thread.getName();
        log.info("mainTransaction at : {}",threadName);

        //初始化计数器
        CountDownLatch mainDownLatch = new CountDownLatch(1);
        CountDownLatch sonDownLatch = new CountDownLatch(mainTransaction.value());

        // 用来记录子线程的运行状态,只要有一个失败就变为true
        AtomicBoolean rollBackFlag = new AtomicBoolean(false);

        // 用来存每个子线程的异常,把每个线程的自定义异常向vector的首位置插入,其余异常向末位置插入,避免线程不安全,所以使用vector代替list
        Vector<Throwable> exceptionVector = new Vector<>();

        map.put(threadName + "_mainDownLatch", mainDownLatch);
        map.put(threadName + "_sonDownLatch", sonDownLatch);
        map.put(threadName + "_rollBackFlag", rollBackFlag);
        map.put(threadName + "_exceptionVector", exceptionVector);

        try {
            //执行方法
            joinPoint.proceed();
        } catch (Throwable e) {
            // 如果抛异常
            exceptionVector.add(0, e);
            rollBackFlag.set(true);//子线程回滚
            mainDownLatch.countDown();//放行所有子线程
        }
        // 如果未抛出异常
        if (!rollBackFlag.get()) {
            try {
                // sonDownLatch等待,直到所有子线程执行完插入操作,但此时还没有提交事务
                sonDownLatch.await();
                log.info("sonThread is down ...");
                mainDownLatch.countDown();// 根据rollBackFlag状态放行子线程的await处,告知是回滚还是提交
            } catch (Exception e) {
                log.error("mainIntercept error",e);
                rollBackFlag.set(true);
                exceptionVector.add(0, e);
            }
        }
        // 如果有异常
        if (CollectionUtils.isNotEmpty(exceptionVector)) {
            map.remove(threadName + "_mainDownLatch");
            map.remove(threadName + "_sonDownLatch");
            map.remove(threadName + "_rollBackFlag");
            map.remove(threadName + "_exceptionVector");
            throw exceptionVector.get(0);
        }
    }

    @Around("@annotation(com.lcx.more.annotation.SonTransaction)")
    public void sonIntercept(ProceedingJoinPoint joinPoint) throws Throwable {
        Thread thread = Thread.currentThread();
        String threadName = thread.getName();
        log.info("SonTransaction at : {}",threadName);

        // 主线程的线程名,需通过参数传入
        Object[] args = joinPoint.getArgs();
        String mainThread = (String) args[args.length - 1];
        if(StringUtils.isBlank(mainThread)){
            log.error("sonTransaction 未获取到 mainTransaction thread info!");
            throw new RuntimeException();
        }

        CountDownLatch mainDownLatch = (CountDownLatch) map.get(mainThread + "_mainDownLatch");
        CountDownLatch sonDownLatch = (CountDownLatch) map.get(mainThread + "_sonDownLatch");
        AtomicBoolean rollBackFlag = (AtomicBoolean) map.get(mainThread + "_rollBackFlag");
        Vector<Throwable> exceptionVector = (Vector<Throwable>) map.get(mainThread + "_exceptionVector");
        if (mainDownLatch == null) {
            //主事务未加注解时, 直接执行子事务
            joinPoint.proceed();
            sonDownLatch.countDown();
            return;
        }
        //如果这时有一个子线程已经出错,那当前线程不需要执行
        if (rollBackFlag.get()) {
            sonDownLatch.countDown();
            return;
        }

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();// 开启事务
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 设置事务隔离级别
        TransactionStatus status = jtaTransactionManager.getTransaction(def);

        try {
            log.info("{} start",threadName);
            joinPoint.proceed();//执行方法
            log.info("{} countDown",threadName);
            sonDownLatch.countDown();// 对sonDownLatch-1
            mainDownLatch.await();// 如果mainDownLatch不是0,线程会在此阻塞,直到mainDownLatch变为0
            // 如果能执行到这一步说明所有子线程都已经执行完毕判断如果atomicBoolean是true就回滚false就提交
            if (rollBackFlag.get()) {
                log.info("transaction rollback");
                jtaTransactionManager.rollback(status);
            } else {
                log.info("transaction commit");
                jtaTransactionManager.commit(status);
            }
        } catch (Throwable e) {
            log.error("sonIntercept error : ",e.getMessage());
            exceptionVector.add(0, e);
            // 回滚
            jtaTransactionManager.rollback(status);
            // 并把状态设置为true
            rollBackFlag.set(true);
            mainDownLatch.countDown();
            sonDownLatch.countDown();
        }
    }
}

你可能感兴趣的:(SpringBoot,spring,java,JTA,Async)