Technical Note/SPRING

멀티쓰레드 환경에서 Http Connection 을 사용할 때, 쓰레드안전(Thread Safe)하게 Http Connection을 이용하는 방법을 소개하고자 합니다.

필자는 간단한 웹서버 부하기를 만드는 과정에서, 
부하발생을 위한 쓰레드를 생성해서 HttpClient로 웹서버에 request를 보내는 프로그램을 개발하여
테스트 하는 중에 다음과 같은 에러에 직면합니다.

org.apache.http.impl.client.DefaultRequestDirector tryConnect
정보: I/O exception (java.net.BindException) caught when connecting to the target host: Address already in use: connect
2011. 8. 16 오후 3:05:20 org.apache.http.impl.client.DefaultRequestDirector tryConnect
정보: Retrying connect
 

10초간격으로 웹서버에 request를 보내는 부하용 쓰레드가 120개 되는 지점 부터,
위와 같이 BindException이 발생하였습니다. MultiThreadedHttpConnectionManager를 이용하여
Connection Pool을 사용하니, 5초에 300개 쓰레드까지 증가하였습니다.

[HttpClientFactory.java]

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;

public class HttpClientFactory {
 private static HttpClient client ;
 private static MultiThreadedHttpConnectionManager mgr ;
 
 public synchronized static HttpClient getThreadSafeClient() {
  if(client != null)    return client ;
  
  mgr = new MultiThreadedHttpConnectionManager() ;
  HttpConnectionManagerParams params = new HttpConnectionManagerParams() ;
  params.setMaxTotalConnections( 300 ) ;
  mgr.setParams(params) ;
  client = new HttpClient(mgr) ;

  return client ;
 }
 
 public static int getConnectionsInPool() {
  return mgr.getConnectionsInPool() ;
 }
}


[사용 예]
...

  HttpClient httpClient = HttpClientFactory.getThreadSafeClient() ;

  PostMethod post = new PostMethod(spUrl) ;
  String timestamp = getTimeStamp() ;
  String serialkey = getSerialKey(from, message, timestamp) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_TOKEY__, to)) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_FROMKEY__, from )) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_MESSAGE__, message )) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_TIMESTAMP__, timestamp )) ; 
  post.addParameter( new NameValuePair(__PUSH_PARAM_SERIALKEY__, serialkey )) ; 
  post.addParameter( new NameValuePair(__PUSH_PARAM_APPID__, "0")) ;

  try {
   httpClient.executeMethod(post) ;
  }
  catch(UnsupportedEncodingException e) {e.printStackTrace() ;} 
  catch (IOException e) {e.printStackTrace() ;}
  finally {
   post.releaseConnection() ;
  }

...
 private static final String __PUSH_PARAM_TOKEY__ = "phone" ;
 private static final String __PUSH_PARAM_FROMKEY__ = "from" ;
 private static final String __PUSH_PARAM_MESSAGE__ = "message" ;
 private static final String __PUSH_PARAM_SERIALKEY__ = "serialkey" ;
 private static final String __PUSH_PARAM_TIMESTAMP__ = "timestamp" ;
 private static final String __PUSH_PARAM_APPID__ = "appid" ;