前言
线上容器6g内存,然后vm主要配置是-Xms4800m -Xmx4800m
同事需要全量跑数据,需要异步线程池。
于是,我就给对方来了个简单的线程池。
大致代码
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
List<CompletableFuture<Void>> taskList = new ArrayList<>();
for (int i = 0; i < 8000; i++) {
int finalI = i;
CompletableFuture<Void> task = CompletableFuture.runAsync(()->{
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+ finalI +" finish");
},threadPoolExecutor);
taskList.add(task);
}
CompletableFuture.allOf(taskList.toArray(new CompletableFuture[taskList.size()])).join();
在全量跑数据,线上就报错,提示容器发生oom
分析
找到对应机器的gc图标分析
在对应的时间点,我们可以看到,我们在两次全量跑数据的时候,明显老年代的内存有变大。 但是为什么是容器提示oom,而不是应用程序提示oom呢
解决方案
如何处理oom
当前还没有进行深层次的优化。由于是后台项目,使用并不多,所以只有4台机器。 当前解决方案: 8000分批次处理,每次运行32个任务,也不是特别优雅,但是也解决了任务.
ExecutorService executorService = new ThreadPoolExecutor(32,32,60,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
List<CompletableFuture<Void>> taskList = new ArrayList<>();
for (int i = 0;i<200000;i++){
int finalI = i;
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+ finalI +"finish");
return null;
},executorService);
taskList.add(completableFuture);
if(taskList.size()>32){
CompletableFuture.allOf(taskList.toArray(new CompletableFuture[taskList.size()])).join();
taskList.clear();
}
}
if(taskList.size()>0){
CompletableFuture.allOf(taskList.toArray(new CompletableFuture[taskList.size()])).join();
taskList.clear();
}
为什么线上应用程序没有错误日志,而是容器oom
这就是我们在使用CompletableFuture不规范导致的。
我们来测试段简单的代码,vm参数设置小一点 -Xms10m -Xmx10m
// 8000任务
List<Integer> policyList = IntStream.range(0, 8000).boxed().collect(Collectors.toList());
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
List<CompletableFuture<Void>> taskList = new ArrayList<>();
for (Integer policy : policyList) {
CompletableFuture<Void> task = CompletableFuture.supplyAsync(()->{
// 我也不确定,做了什么
// 这边业务逻辑,一部分是http请求,一部分是sql查询插入,等等
byte[] byteArr = new byte[1024 * 1024 * 4];
System.out.println(policy + "处理完成");
return null;
},threadPoolExecutor);
taskList.add(task);
}
运行一段事件,应用程序不继续输出处理完成了,也没有报错。
注意:,不是Exception
解决方案有两种
1.在使用CompletableFuture需要用whenComplete处理,取捕获throwable
2.在执行异步方法的外部用try catch throwable来处理。
后续优化方案
1.通过异步调用http方式,通过负载均衡,将压力到其他机器上 2.mq方式,让集群处理。