相信很多小伙伴在工作学习中会遇到这类问题,调用别人的功能,但是不能批量,故多次feign接口远程调用,导致接口响应时间过长。
这时候就会考虑到我们的多线程来解决这类问题了,使用多线程那么首先肯定是要创建线程池了
下面是自定义线程池方法,根据CPU动态设置核心线程,最大线程数以及队列数量。
@Configuration
@EnableAsync
@AutoConfigureAfter(BifrostProperties.class)
@AllArgsConstructor
public class BFPExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(com.szjz.cloud.bifrost.boot.base.core.thread.ExecutorConfig.class);
private BifrostProperties bifrostProperties;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@Bean("BfpAsyncPool")
public Executor threadPoolTaskExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setKeepAliveSeconds(60);
//配置核心线程数
executor.setCorePoolSize(CPU_COUNT+1);
//配置最大线程数
executor.setMaxPoolSize(CPU_COUNT*2+1);
//配置队列大小
executor.setQueueCapacity((CPU_COUNT*2+1)*100);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(bifrostProperties.getThreadPool().getThreadNamePrefix());
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
// 额外的处理,生成修饰了的对象executorService
// TtlExecutors.getTtlExecutorService((ExecutorService) executor);
return executor;
}
}
自定义线程池之后就来到今天的主题,下面map是执行feign接口需要用到的参数,hashmap用来存储异步方法的返回值,executor是我们自定义的线程池,当我们在使用这个多线程异步方法的时候要注意当我们进入异步方法后子线程无法获取上下文信息,但是主线程不影响,会导致子线程内获取token失败,从而导致接口调用失败。
Map<String, Object> map = new HashMap<>();
map.put("year", t.getYear());
ConcurrentHashMap<String, List<Map<String, Object>>> hashMap = new ConcurrentHashMap<>();
Executor executor = executorConfig.threadPoolTaskExecutor();
List<CompletableFuture> futures = new ArrayList<>();
for (String id : dsIds) {
final String currId = id;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 调用查询方法,返回List<Map<String, Object>>
try {
List<Map<String, Object>> resultList = dataSetService.getListMapByDsId(currId, map);
hashMap.put(currId, resultList);
} catch (Exception e) {
throw new BizException(BaseCode.ERROR, "");
}
}, executor).exceptionally(e -> {
throw new BizException(BaseCode.ERROR, currId);
});
futures.add(future);
}
CompletableFuture<Void> allFutures = null;
allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
allFutures.join();
正确的方式应该是这样:每个地方获取上下文方式不同,小编就用注释标注添加位置了,
最后最最最最重要的一件事:感谢各位老板的观看!
Map<String, Object> map = new HashMap<>();
map.put("year", t.getYear());
// 在for循环外获取上下文信息 并在异步方法内赋值给子线程上下文信息
ConcurrentHashMap<String, List<Map<String, Object>>> hashMap = new ConcurrentHashMap<>();
Executor executor = executorConfig.threadPoolTaskExecutor();
List<CompletableFuture> futures = new ArrayList<>();
for (String id : dsIds) {
final String currId = id;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 调用查询方法,返回List<Map<String, Object>>
try {
// 在这里赋值上下文信息
List<Map<String, Object>> resultList = dataSetService.getListMapByDsId(currId, map);
hashMap.put(currId, resultList);
} catch (Exception e) {
throw new BizException(BaseCode.ERROR, "");
}
}, executor).exceptionally(e -> {
throw new BizException(BaseCode.ERROR, currId);
});
futures.add(future);
}
CompletableFuture<Void> allFutures = null;
allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
allFutures.join();