思路 : 使用 CompletableFuture.supplyAsync 异步执行调取另一个service方法 并获取返回结果
主线程等到结果后进行比对 (一般是看是否成功执行 )
结果错误进行主线程异常抛出 配合@Transactional
可解决大部分常用场景
package com.eco.user.service;
import com.eco.common.result.Result;
import com.eco.user.entity.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@Service
public class ThirdService {
@Autowired
ISysUserService sysUserService;
@Autowired
@Qualifier("asyncExecutor")
Executor executor;
@Transactional
public Result<String> npc() throws ExecutionException, InterruptedException {
SysUser sysUser = new SysUser();
sysUser.setId(18L);
sysUser.setStatus(1);
sysUserService.updateById(sysUser);
List<CompletableFuture<Boolean>> futures = new ArrayList<>();
CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
try {
sysUserService.listByParam();
} catch (Exception e) {
e.printStackTrace();
System.out.println("出错了");
return false;
}
return true;
},executor);
// 多任务编排使用
// 使用allOf方法来表示所有的并行任务
// futures.add(future);
// CompletableFuture<Void> allFutures = CompletableFuture.allOf(
// futures.toArray(new CompletableFuture[futures.size()]));
//
// // 下面的方法可以帮助我们获得所有子任务的处理结果
// CompletableFuture<List<Boolean>> finalResults = allFutures.thenApply(v ->
// futures.stream().map(CompletableFuture::join).collect(Collectors.toList())
// );
// List<Boolean> resultList = finalResults.join();
System.out.println(future.get());
if (future.get().equals(false)) {
throw new RuntimeException("结果错误");
}
return Result.success("fff");
}
}
调用另一个service
package com.eco.user.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eco.user.entity.SysPermission;
import com.eco.user.entity.SysUser;
import com.eco.user.mapper.SysUserMapper;
import com.eco.user.service.ISysPermissionService;
import com.eco.user.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.UUID;
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
@Autowired
ISysPermissionService sysPermissionService;
@Override
@Transactional
public SysUser listByParam() {
SysUser sysUser = new SysUser();
sysUser.setId(UUID.randomUUID().getMostSignificantBits());
sysUser.setStatus(0);
save(sysUser);
System.out.println("ddd");
SysPermission sysPermission = new SysPermission();
sysPermission.setId(UUID.randomUUID().getMostSignificantBits());
sysPermission.setCreateTime(new Date());
int t = 1 / 0;
sysPermissionService.save(sysPermission);
return sysUser;
}
}
如果不需要等待线程返回结果 直接在方法添加@Async 配合 @Transactional 即可保证子线程方法内部的事务一致
自定义异步线程池
package com.eco.common.web.config;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import com.eco.common.web.properties.TaskThreadPoolConfig;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义异步线程池
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(TaskThreadPoolConfig.class)
public class AsyncPoolConfig implements AsyncConfigurer {
@Autowired
private TaskThreadPoolConfig config;
@Bean("asyncExecutor")
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
executor.setMaxPoolSize(config.getMaxPoolSize());
executor.setQueueCapacity(config.getQueueCapacity());
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
executor.setThreadNamePrefix("eco_async_");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
/**
* 使用@Async 注解异常处理
*/
@SuppressWarnings("all")
class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
// 1、打印异常堆栈
throwable.printStackTrace();
// 2、日志记录错误信息
log.error("AsyncError:{}, Method:{}, Param:{}", throwable.getMessage(), method.getName(), Arrays.asList(objects));
// 3、TODO 发生异常后通知管理人员(邮件,短信)进一步处理
}
}
}
package com.eco.common.web.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@ConfigurationProperties("task.pool")
public class TaskThreadPoolConfig {
/**
* 核心线程池大小
*/
private int corePoolSize =1;
/**
* 最大线程数
*/
private int maxPoolSize =5;
/**
* 队列容量
*/
private int queueCapacity =1;
/**
* 活跃时间
*/
private int keepAliveSeconds = 5;
}
备注: 本方法缺陷 如果多个子线程执行不同方法 无法对子线程所有方法进行事务统一处理
考虑从数据库层面对事务做统一处理