springboot中事务的源码分析(四)

185 阅读2分钟

今天说一下TransactionSynchronizationAdapter使用场景

背景:在业务中,经常需要在执行数据库操作后(事务提交完成),发送消息或事件来异步调用其他组件执行相应的业务操作。

比如:用户注册成功后,发送消息干某些事情,如果用户保存后就执行异步操作发送消息事件,但是前面用户保存后发生异常,数据库进行回滚,用户实际没有注册成功,但发送消息事件成功了。此时,我们就迫切要求数据库事务完成后再执行异步操作

public User saveUser(User user) { 
    // 先保存保存用户 
    userDao.save(user); 
    // 发送激活码或激活邮件
    Producer.sendMessage(user.getId());
}

使用TransactionSynchronizationManager 和 TransactionSynchronizationAdapter

public User saveUser(User user) {
// 保存用户 
userDao.save(user);
final int userId = user.getId(); 
// 事务提交后调用 
TransactionSynchronizationManager.registerSynchronization(
    new TransactionSynchronizationAdapter() { 
    @Override 
    public void afterCommit() {
        Producer.sendMessage(user.getId());
   } 
});

注意:上面的代码将在事务提交后执行.如果在非事务context中将抛出java.lang.IllegalStateException: Transaction synchronization is not active。

代码改进后:

public User saveUser(User user) {
// 保存用户 
userDao.save(user);
final int userId = user.getId(); 
// 事务提交后调用 
// 兼容无论是否有事务
if(TransactionSynchronizationManager.isActualTransactionActive()) {
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronizationAdapter() { 
        @Override 
        public void afterCommit() {
            Producer.sendMessage(user.getId());
       } 
    }
});

为了使代码更加稳健而不影响性能,我们可以增加多线程:

@Component("afterCommitExecutor")
public class AfterCommitExecutor extends TransactionSynchronizationAdapter implements Executor {
    private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<List<Runnable>>();
    private ThreadPoolExecutor threadPool;

    @PostConstruct
    public void init() {
        Logs.debug("初始化线程池。。。");
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        if (0 >= availableProcessors) {
            availableProcessors = 1;
        }
        int maxPoolSize = (availableProcessors > 5) ? availableProcessors * 2 : 5;
        Logs.debug("CPU Processors :%s MaxPoolSize:%s", availableProcessors, maxPoolSize);
        threadPool = new ThreadPoolExecutor(
            availableProcessors,
            maxPoolSize,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(maxPoolSize * 2),
            Executors.defaultThreadFactory(),
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    Logs.debug("Task:%s rejected", r.toString());
                    if (!executor.isShutdown()) {
                        executor.getQueue().poll();
                        executor.execute(r);
                    }
                }
            }
        );
    }

    @PreDestroy
    public void destroy() {
        Logs.debug("销毁线程池。。。");
        if (null != threadPool && !threadPool.isShutdown()) {
            threadPool.shutdown();
        }
    }

    @Override
    public void execute(@NotNull Runnable runnable) {
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            runnable.run();
            return;
        }
        List<Runnable> threadRunnables = RUNNABLES.get();
        if (threadRunnables == null) {
            threadRunnables = new ArrayList<Runnable>();
            RUNNABLES.set(threadRunnables);
            TransactionSynchronizationManager.registerSynchronization(this);
        }
        threadRunnables.add(runnable);
    }

    @Override
    public void afterCommit() {
        Logs.debug("事务提交完成处理 ... ");
        List<Runnable> threadRunnables = RUNNABLES.get();
        for (int i = 0; i < threadRunnables.size(); i++) {
            Runnable runnable = threadRunnables.get(i);
            try {
                threadPool.execute(runnable);
            } catch (RuntimeException e) {
                Logs.error("", e);
            }
        }
    }

    @Override
    public void afterCompletion(int status) {
        Logs.debug("事务处理完毕 .... ");
        RUNNABLES.remove();
    }
}