继上一篇异步操作优化IO 密集型API 接口,这一篇将会加上自定义线程池和对线程池的监控,用到springboot的actuator/health。前面提到用CompletableFuture来优化接口,其实CompletableFuture内部自己维护了一个线程池,这个线程池的个数是电脑核心数-1,显然这个并不适合IO密集型的API。原因是IO密集型的API有很多IO的操作,比如call DB,call third API等等,最大的瓶颈就是IO的等待时间,这是要把尽可能多的请求发出去,然后我们API就等待返回结果,所以我们得自己配置一个线程,来使得我们的API的性能进一步提升。actuator默认大家都会配置哈。太简单了,不会问度娘也行。
1,线程池的配置
@Configuration
@EnableAsync
public class ExecutorConfig {
@Bean(name = "asyncServiceExecutor")
public ThreadPoolTaskExecutor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
executor.setCorePoolSize(25);
executor.setMaxPoolSize(25);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("eb-core-thread-pool-");
//if task is rejected, calling thread will handle this.
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
具体的参数可根据API的情况领过调节。
2,在actuator/health里面打印出线程池的情况
@Component
public class ThreadPoolHealthCheck extends AbstractHealthIndicator {
@Autowired
ThreadPoolTaskExecutor asyncServiceExecutor;
@Override
protected void doHealthCheck(Health.Builder hb) throws Exception {
ThreadPoolExecutor threadPoolExecutor = asyncServiceExecutor.getThreadPoolExecutor();
hb.up().withDetail("activeCount", threadPoolExecutor.getActiveCount());
hb.up().withDetail("queueSize", threadPoolExecutor.getQueue().size());
hb.up().withDetail("poolSize", threadPoolExecutor.getPoolSize());
}
}
activeCount是整在运行的线程数
queueSize是队列中的线程数
poolSize是整个线程池中的线程数
当然我们也可以在每次有新任务线程执行的时候打印出线程池的信息
//哈哈 copy了一段网上大佬的代码,有侵权请通知我改。
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
接口代码不方便贴,大家自己写个简单的demo就可以测试啦,起七个线程,想下面这样。
CompletableFuture<> otxGroupDataFuture = CompletableFuture.supplyAsync(() -> {
//实现逻辑
return null;
}, asyncServiceExecutor);
来run几次接口。看看情况
接着我们访问http://localhost:8082/actuator/health
可以看到,返回了线程池的信息,也方便我们遇到问题的时候调试。
疑问: 不知道大家刚刚注意到一个问题了没
我明明设置的coresize是25,这里activecount才2怎么就往队列里放了?
也就是说还有23个线程没有干活,这时新来的task直接放队列里了,什么操作?