写在前面

这里总结下 ,Springboot 中异步操作的最佳实践模板,仅供参考,解决包括异步操作中的事务、以及全局线程的监控

一、基本准备

  • 1、springboot web 项目

  • 2、异步线程配置,如下

1.1、如下,配置异步线程,以及相关线程池参数配置

/** * 异步配置 */
@Component
@EnableAsync
public class AsyncConfig {
   

    /** * 异步日志 task * * @return */
    @Bean("logTask")
    public AsyncTaskExecutor getLogAsyncExecutor() {
   
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(100);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.setThreadNamePrefix("Async-logTask-");
        executor.initialize();
        return executor;
    }

    /** * 其他异步 task * * @return */
    @Bean("otherTask")
    public AsyncTaskExecutor getLogAsyncExecutor2() {
   
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(100);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.setThreadNamePrefix("Async-logTask-");
        executor.initialize();
        return executor;
    }
// 其他自定义的 异步线程配置
}

1.2、线程监控配置

@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor{
   
    
    /** * @Title: showThreadPoolInfo * @Description: 展示当前线程池任务:线程池-已提交任务-已完成任务-活跃线程-队列缓存数 * void * @throws */
    private void showThreadPoolInfo(){
   
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if(null==threadPoolExecutor){
   
            return;
        }

        log.info("线程池监控>>>{}, taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }
    
    @Override
    public <T> Future<T> submit(Callable<T> task) {
   
        showThreadPoolInfo();
        return super.submit(task);
    }
    
    @Override
    public void execute(Runnable task) {
   
        showThreadPoolInfo();
        super.execute(task);
    }

}

二、使用方式

2.1、定义 异步操作

定义为 Spring Bean

@Service
@Slf4j
@Async("logTask")  // 这里如果找不到 Bean,会同步操作
public class LogService {
   

    @Autowired
    private ConfigurationChangeLogRepository logRepository;

    /** * @param info * CompletableFuture 也可承载对象,用于某些场景中异步回执检查机制 */
    public CompletableFuture accessorAdd(OperationSystemInfo info) {
   
        final String currentUser = SecurityUtils.getCurrentUserName();
        final LocalDateTime now = LocalDateTime.now();
        log.info("接入方创建日志写入...");

        ConfigurationChangeLog log = new ConfigurationChangeLog();
        log.setChangeUser(currentUser);
        log.setChangeDate(now);
        log.setChangeType(LogEnum.ADD.name());
        log.setChangeInfo(OperateModule.ACCESSOR.getText());
        log.setChangeDetail(JSONUtils.obj2json(info));
        logRepository.save(log);
        return CompletableFuture.completedFuture(null);
    }
}

2.2、调用异步操作

可以在任何操作中 ,注入 异步Bean操作,如下

 @Resource
    private LogService logService;

 @Override
    @Transactional
    public void save(OperationSystemAddRequest dto) {
   

        OperationSystemInfo info = operateStatemMapper.toEntity(dto);
        this.verifyAddIfDuplicate(dto);

        LocalDateTime now = LocalDateTime.now();
        String currentUserName = SecurityUtils.getCurrentUserName();

        // 基础数据封装
        info.setStatus(SystemEnum.SystemStatus.ENABLE.getKey());
        info.setCreateDate(now);
        info.setLastModifuDate(now);
        info.setCreateUser(currentUserName);
        info.setLastModifyUser(currentUserName);
        OperationSystemInfo save = accessorManageDao.save(info);
        
        // 这里的 join 操作,将异步中的回调信息,合并到 主线程中,解决了异步操作中的事务问题
        CompletableFuture completableFuture = logService.accessorAdd(save);
        completableFuture.join();
    }