HttpClient로 빈번히 connection을 맺었다가, 사용이 끝나면 끊고 하다 보면 더 이상 connection을 열 수 없는 경우가 발생할 수 있다.
그 이유는 connection을 닫는다고 호출을 해도, 실제로는 어느 정도 TIME_WAIT 상태에 있다가 끊어지는데 이런 것들이 많이 쌓여 있으면 File Descriptor가 꽉 찼다는 에러(Too Many Open Files)가 나면서 connection을 맺지 못 하게 된다.
이런 현상을 방지하기 위해서는 Connection을 재사용할 수 있도록 HttpClient에서 제공하는 Connection Pool을 사용하는 것이다.


<사용 라이브러리>
httpclient-4.2.3.jar, httpcore-4.2.2.jar

public class ConnectionManager {

private static PoolingClientConnectionManager connectionManager = null;
public static synchronized HttpClient getHttpClient() {
if (connectionManager == null) {
connectionManager = new PoolingClientConnectionManager();
connectionManager.setMaxTotal(50);
connectionManager.setDefaultMaxPerRoute(20);
}

return new DefaultHttpClient(connectionManager);
}

public static void abort(HttpRequestBase httpRequest) {
if (httpRequest != null) {
try {
httpRequest.abort();
} catch (Exception e) {}
}
}

public static void release(HttpResponse response) {
if (response != null && response.getEntity() != null)
EntityUtils.consumeQuietly(response.getEntity());
}
}


요런 식으로, static으로 PoolingClientConnectionManager 를 하나 선언해 두고, 최초로 getHttpClient를 호출할 때 Connection Pool이 지정된 사이즈로 생성된다. 그리고, Connection을 하나 만들어 리턴한다.

- maxTotal : Connection Pool의 수용 가능한 최대 사이즈
- defaultMaxPerRoute : 각 host(IP와 Port의 조합)당 Connection Pool에 생성가능한 Connection 수


protected <T> T get(String url) {
HttpClient httpClient = null;
HttpGet httpGet = null;
HttpResponse response = null;
try {
httpClient = ConnectionManager.getHttpClient();
httpGet = new HttpGet(url);
response = httpClient.execute(httpGet);

StatusLine statusLine = response.getStatusLine();
// 에러 발생
if (statusLine.getStatusCode() < 200 || statusLine.getStatusCode() >= 300) {
throw new Exception(statusLine.getStatusCode(), getReason(response));
}
// 성공
else {
                            // do something
}
} catch (ClientProtocolException e) {
e.printStackTrace();
ConnectionManager.abort(httpGet);
throw e;
} catch (IOException e) {
e.printStackTrace();
ConnectionManager.abort(httpGet);
throw e;
} finally {
ConnectionManager.release(response);
}
}

Connection Pool은 요런 식으로 이용하면 되겠다. 위는 GET 방식 사용시.


Pool을 사용할 때마다 항상 주의할 것은 역시 반환을 꼭 해 줘야 한다는 것!!!
Exception이 발생하면 abort를 호출해서 정리해 주고, finally에선 항상 EntityUtils.consumeQuietly를 호출해서 리소스를 반납하게 해 준다.


AND