一、问题分析
系统应用使用Httpclient4.5连接池通过PoolingHttpClientConnectionManager实现,系统运行一段时间后就会出现连接池满的情况,导致无法从连接池中获取到可以使用的httpclient链接,导致服务中断。
源代码如下:
public static synchronized CloseableHttpClient getHttpClient(){ if(httpClient == null) { //注册访问协议相关的Socket工厂 Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", SSLConnectionSocketFactory.getSystemSocketFactory()) .build(); //HttpConnection工厂:配置写请求/解析响应处理器 HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory( DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE); //DNS解析器 DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE; //创建池化连接管理器 manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry,connFactory,dnsResolver); //默认为Socket配置 SocketConfig defaultSocketConfig = SocketConfig.custom() .setTcpNoDelay(true).build(); manager.setDefaultSocketConfig(defaultSocketConfig); //设置连接池的最大连接数 manager.setMaxTotal(maxTotal); //每个路由最大连接数 manager.setDefaultMaxPerRoute(maxPerRoute); //在从连接池获取连接时,连接不活跃多长时间后需要进行一次验证,默认为2s manager.setValidateAfterInactivity(validateAfterInactivity * 1000); RequestConfig defaultRequestConfig = RequestConfig.custom() .setConnectTimeout(connectTimeout * 1000) //设置连接超时时间,2s .setSocketTimeout(socketTimeout * 1000) //设置等待数据超时时间,5s .setConnectionRequestTimeout(connectionRequestTimeout) //设置从连接池获取连接的等待超时时间 .build(); //创建HttpClient httpClient = HttpClients.custom() .setConnectionManager(manager) .setConnectionManagerShared(false) //连接池不是共享模式 .evictIdleConnections(60, TimeUnit.SECONDS) //定期回收空闲连接 .evictExpiredConnections() //定期回收过期连接 .setConnectionTimeToLive(60,TimeUnit.SECONDS) //连接存活时间,如果不设置,则根据长连接信息决定 .setDefaultRequestConfig(defaultRequestConfig) //设置默认请求配置 .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) //连接重用策略,即是否能keepAlive .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE) //长连接配置,即获取长连接生产多长时间 .setRetryHandler(new DefaultHttpRequestRetryHandler(0,false)) //设置重试次数,默认是3次;当前是禁用掉 .build(); //JVM停止或重启时,关闭连接池释放掉连接 Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run(){ try{ httpClient.close(); }catch (IOException e){ e.printStackTrace(); } } }); } return httpClient; }
二、解决方案
public static String readNet (String urlPath)
{
StringBuffer sb = new StringBuffer ();
HttpClient client = null;
InputStream in = null;
InputStreamReader isr = null;
HttpGet get = new HttpGet();
try
{
client = HttpConnectionManager.getHttpClient();
get.setURI(new URI(urlPath));
HttpResponse response = client.execute(get);
if (response.getStatusLine ().getStatusCode () != 200) {
get.abort();
return null;
}
HttpEntity entity =response.getEntity();
if( entity != null ){
in = entity.getContent();
......
}
return sb.toString ();
}
catch (Exception e)
{
get.abort();
e.printStackTrace ();
return null;
}
finally
{
if (isr != null){
try
{
isr.close ();
}
catch (IOException e)
{
e.printStackTrace ();
}
}
if (in != null){
try
{
in.close ();
}
catch (IOException e)
{
e.printStackTrace ();
}
}
}
}
显示调用HttpGet的abort,这样就会直接中止这次连接,我们在遇到异常的时候应该显示调用,因为谁能保证异常是在InputStream in赋值之后才抛出的呢。