提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
问题描述
一次线上环境,基于netty实现tpc server,大量接受客户端请求,运行几天后出现进程僵死现象,通过jstack工具排查线程栈信息,并未发现线程异常的情况;最终再大量的日志中出现
java.lang.IllegalStateException: Connection pool shut down
at org.apache.http.util.Asserts.check(Asserts.java:34) ~[httpcore-4.4.10.jar!/:4.4.10]
at org.apache.http.pool.AbstractConnPool.lease(AbstractConnPool.java:191) ~[httpcore-4.4.10.jar!/:4.4.10]
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.requestConnection(PoolingHttpClientConnectionManager.java:267) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:176) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.6.jar!/:4.5.6]
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) ~[httpclient-4.5.6.jar!/:4.5.6]
at com.tsn.common.utils.utils.tools.http.AbstractHttpRequest.excuteReturnObj(AbstractHttpRequest.java:82) ~[tsn-common-utils-1.0.0-SNAPSHOT.jar!/:1.0.0-SNAPSHOT]
httpclient连接池分析
public class HttpClientUtils {
private static PoolingHttpClientConnectionManager poolClientConnManager;
public static PoolingHttpClientConnectionManager getPoolClientConnManager() {
return poolClientConnManager;
}
......
}
TotalStats totalStats = poolClientConnManager.getTotalStats()
leased 表示当前正在被使用的连接。正常情况下不会长时间持续增长,如果出现说明连接未释放
pending 表示多少个请求数正在等待连接池空闲连接。
max 表示路由最大可以创建的连接数
available 表示连接池中空闲等待的连接数
初步分析在tcp server中接受大量请求后会同时请求外部接口数据(业务需要),请求客户端使用httpclient,采用的连接池,因此觉得对其进行优化
一、使用步骤
1.调整httpclient并发参数
- 原配置
httpclient.poolMaxTotal=10
httpclient.maxPerRoute=5
- 修改,根据实际情况进行调整
httpclient.poolMaxTotal=500
httpclient.maxPerRoute=250
1.poolMaxTotal 表示总共tcp最大连接数
2.maxPerRoute 表示每个路由最大连接数
3.maxTotal和maxPerRoute请求过大,设置过小,同时客户端调用超时的情况下会导致连接池中没有闲置连接,会使netty异步线程池中的队列数据堆叠的越来越大,最终导致内存oom
4.这里的场景调用的路由为2个,每个分配最大25个连接数,总共设置50,根据实际情况调整即可。
poolMaxTotal 和 maxPerRoute 记得同时设置
2.减少httpclient的连接超时,请求超时时间
RequestConfig.custom().setSocketTimeout(2*1000).setConnectTimeout(2*1000);
同时设置2s,根据实际情况调整,避免延迟时间过长处理缓慢
3.关闭定时清理
httpclient = HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build();
setConnectionManagerShared(true) 关闭定时清理长时间的闲置的连接,在大量频繁请求,使用线程池的情况下,进行关闭,充分利用连接,为false表示开启,true表示不开启
if (!(this.connManagerShared)) {
if (closeablesCopy == null) {
closeablesCopy = new ArrayList(1);
}
HttpClientConnectionManager cm = connManagerCopy;
if ((this.evictExpiredConnections) || (this.evictIdleConnections)) {
IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(
cm, (this.maxIdleTime > 0L) ? this.maxIdleTime : 10L,
(this.maxIdleTimeUnit != null) ? this.maxIdleTimeUnit
: TimeUnit.SECONDS, this.maxIdleTime,
this.maxIdleTimeUnit);
closeablesCopy.add(new Closeable(connectionEvictor) {
public void close() throws IOException {
this.val$connectionEvictor.shutdown();
try {
this.val$connectionEvictor.awaitTermination(1L,
TimeUnit.SECONDS);
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt();
}
}
});
connectionEvictor.start();
}
closeablesCopy.add(new Closeable(cm) {
public void close() throws IOException {
this.val$cm.shutdown();
}
});
}
版权声明:本文为weixin_38548413原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。