线上使用 CompletableFuture.supplyAsync 来多线程下载影像,使用了默认的ForkJoinPool线程池。
项目运行一段时间后下载影像方法全部报错:
java.util.concurrent.TimeoutException
下载方法如下:
CloseableHttpClient http = HttpClientBuilder.create().build();
public File download(String url, String filename) throws IOException {
File file = new File(parent, filename);
try (FileOutputStream fos = new FileOutputStream(file);
CloseableHttpResponse response = http.execute(new HttpGet(url));) {
int sc = response.getStatusLine().getStatusCode();
if (sc == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
entity.writeTo(fos);
} else {
...
}
}
return file;
}
排查代码发现出现在 CompletableFuture.get() 步骤超时
CompletableFuture<List<File>> sequence = sequence(
imgs.stream().map(p -> CompletableFuture.supplyAsync(() -> {
...
}).exceptionally(e -> {
...
})).collect(toList()));
try {
return sequence.get(300L, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
...
}
考虑到当影像量庞大时候下载速度问题,超时时间设置为5分钟之久。
这么久的超时时间设置,每次方法调用出现都出现TimeoutException问题,猜测是线程堵塞。
项目部署在docker容器,进入bash 。
jps -l // 获取项目进程ID
由于使用了CompletableFuture 异步操作的默认线程池ForkJoinPool
使用jstack 获取项目堆栈日志。
jstack 1(项目进程ID) | grep ForkJoinPool -A 10
获取到的日志部分如下所示。
"ForkJoinPool.commonPool-worker-1" #521 daemon prio=5 os_prio=0 tid=0x00007f4b901be800 nid=0x20e runnable [0x00007f4b5b693000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)