天天看點

Springboot中異步操作的最佳實作方式

Springboot中異步操作的最佳實踐

  • ​​寫在前面​​
  • ​​一、基本準備​​
  • ​​1.1、如下,配置異步線程,以及相關線程池參數配置​​
  • ​​1.2、線程監控配置​​
  • ​​二、使用方式​​
  • ​​2.1、定義 異步操作​​
  • ​​2.2、調用異步操作​​

寫在前面

這裡總結下 ,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、定義 異步操作

@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、調用異步操作

@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();
    }      

繼續閱讀