TransactionalEventListener 异步事件+事务

TransactionalEventListener

一、遇到业务问题

在线课业务某一场景:需要创建班级后对接第三方平台,步骤是:

  1. 完成自己方业务数据落库
  2. 同步三方平台数据
  3. 同步成功后维护自己方数据和三方关联关系
    由于链路业务较长决定实现方案是第一步执行完后同步返回web页面结果,默认同步三方数据一定成功(后话:不成功会补偿);异步执行同步数据并维护关联关系。

二、设计方案

使用线程池去处理 2,3 两步。(队列执行一样)
缺陷:在业务实际运行过程中会发现,第一步事务还未提交,第二步已经执行,会出现幻读情况。
临时解决方案:Thread.sleep(3000) (延迟队列,效果一样)。
最终方案:决定第一步事务提交后再去操作 2,3。

三、技术实现

TransactionalEventListener:事务监听器;其实就是 EventListener 事件监听器的一种。
ApplicationEventPublisher:事件发布器。
ApplicationEvent:事件。

  1. 创建一个任务事件
/**
 * @Description
 * @Author wuxb
 * @Date 2022/8/23 17:07
 */
public class TaskEvent extends ApplicationEvent {

    public TaskEvent(TaskRun source) {
        super(source);
    }

    @Override
    public TaskRun getSource() {
        return (TaskRun) super.getSource();
    }


    @FunctionalInterface
    public interface TaskRun {
        void run();
    }
}

说明
@FunctionalInterface
为了下游触发任务便于使用了lambda表达式。
  1. 创建指定事件监听
@Component
public class TaskListener {

    @Async("taskExecutorService")
    @Transactional(propagation = Propagation.REQUIRES_NEW rollbackFor = Exception.class)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void taskHandler(TaskEvent taskEvent) {
        System.out.println("=============> start taskHandler:" + Thread.currentThread().getId() + ", name : " + Thread.currentThread().getName());
        if (Objects.nonNull(taskEvent)) {
            taskEvent.getSource().run();
        }
    }
}

说明:
@Transactional(propagation = Propagation.REQUIRES_NEW rollbackFor = Exception.class)
新开启一个使用用在此监听器上(异步业务也分123链路,3执行溢出,12也需要回滚)

@Async("taskExecutorService")
ApplicationEventPublisher默认是同步的,需要开启异步操作;而我这里使用的是线程池,
所以这边又创建了一个线程池的bean
/**
 * @Description
 * @Author wuxb
 * @Date 2022/8/24 11:36
 */
@Configuration
@EnableAsync
public class TaskExecutorConfig {

    @Bean
    public ExecutorService taskExecutorService() {
        return new ExecutorContextProxy(ThreadPool.newCachedThreadPool("BaseClassInTaskService", 20, 1000, 10000));
    }
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
这个就是重点,监听事务提交后触发;可以指定事务场景:
public enum TransactionPhase {
    BEFORE_COMMIT,
    AFTER_COMMIT,
    AFTER_ROLLBACK,
    AFTER_COMPLETION;

    private TransactionPhase() {
    }
}
  1. 执行
在需要的地方加入一行代码即可

@Autowired
private ApplicationEventPublisher applicationEventPublisher;

// 执行任务
applicationEventPublisher.publishEvent(new TaskEvent(() -> {
    run(param.getMainTaskId());
}));
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值