Apache HttpClient连接池泄露问题排查
问题背景
-
业务系统主要的业务是一个数据聚合管理平台,其中系统有一个功能是同步所有资源(简称 大同步)
-
业务同步数据请求数据工具是适配
Apache HttpClient的Feign,这种请求封装是我当时根据业务适配业务封装请求api -
Feign版本:10.10.1
问题来源
- 在生产环境,大同步功能(20多个任务)发现跑了一半多的任务时候卡住,在测试环境并没有发现这个问题
同步接口
public interface SyncHelper {
Order syncOrder();
void syncAllAccount();
void syncSingleAccount(Long accountId);
default boolean enableSync() {
return true;
}
}
大同步功能实现
@Slf4j
@Component
public class SyncAccountResourceListener {
@Autowired
private final List<SyncHelper> helpers;
// 单线程线程池
private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setDaemon(false)
.setNameFormat("h3c-sync-resource-%d")
.build()
);
public void sync(){
for (SyncHelper helper : helpers) {
if (Thread.currentThread().isInterrupted()) {
log.error("[{}] sync task interrupted,account:[{}]", className, accountId);
continue;
}
Future<?> future = EXECUTOR.submit(() -> helper.syncSingleAccount(accountId));
try {
future.get(helper.getTimeOut(), TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException|TimeoutException e) {
log.error("[{}] sync error,account:[{}]", className, accountId, e);
} finally {
future.cancel(true);
}
}
}
}
排查步骤
本想着以最快速度解决问题,系统上同步进度列表 显示都卡在同一个同步类,然后粗略看了一下相关同步类的代码,发现并没有相关可能导致死循环的代码
尝试复现
- 在测试环境测试大同步,发现没问题(包括请求来回数据日志、数据库sql打印日志),顺利完成所有的同步任务
- 那就针对卡住的同步类做单元测试反复执行多次,结果发现也并没有问题
至此,问题就更加疑惑。并无法在测试环境和本地单元测试复现,生产怎么就会有相关的问题?
死锁
一开始没去排查死锁问题,因为大部分同步都没有用到多线程
可能原因
- 用到多线程在大同步资源使用单线程的线程池跑任务,然后任务超时 TimeOut 没做好任务中断的处理,导致后面任务全部阻塞
- 看到有同事同步数据用了多线程,用的不是很合理,类似以下代码:
List<SysDept> deptList = ......
List<CompletableFuture<CmdbUsageReport>> futureList = new ArrayList<>();
deptList.forEach(t -> futureList.add(
CompletableFuture.supplyAsync(() -> {
// 耗时任务
return report;
}, ioPool)));
CompletableFuture.allOf(futureList.toArray(new CompletableFuture[]{
})).join();
List<CmdbUsageReport> reportList = futureList.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(toList());
// ......
既然排查线程的问题,直接使用相关的分析工具去分析,看一下到底是怎么回事
分析线程状态
使用 阿里 arthas 或者 visualvm 查看同步任务的线程状态
启动 arthas attach 相应进程
java -jar arthas-boot.jar
thread --all 查看所有线程简单信息

使用单线程线程池跑同步任务,执行线程池线程也有自定义名称,名称为 `h3c-sync-resource-0`(进程 ID 为 250 ,线程状态为 `WAITING` )
thread 250 查看同步信息进程的详细信息

"h3c-sync-resource-0" - Thread t@195
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <66bb3d00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:

本文详细记录了Apache HttpClient连接池泄露问题的排查过程,从问题背景到源码分析,揭示了由于未完全消费响应内容导致的死锁现象。通过单元测试和源码追踪,发现问题在于Feign请求处理时未正确释放响应输入流,特别是在处理HTTP Chunked响应时。最终,提出了修复方案,即确保在处理响应时正确释放资源。
最低0.47元/天 解锁文章
695

被折叠的 条评论
为什么被折叠?



