HttpClient 사용 총화

13749 단어 Java
사용 방법
HttpClient 를 사용 하여 요청 을 보 내 고 응답 을 받 는 것 은 간단 합 니 다.보통 다음 과 같은 몇 단계 가 필요 합 니 다.1.HttpClient 대상 을 만 듭 니 다.2.요청 방법의 인 스 턴 스 를 만 들 고 요청 URL 을 지정 합 니 다.GET 요청 을 보 내 려 면 HttpGet 대상 만 들 기;POST 요청 을 보 내 려 면 HttpPost 대상 을 만 듭 니 다.3.요청 인 자 를 보 내 려 면 HttpGet,HttpPost 의 공통 setParams(HetpParams params)방법 으로 요청 인 자 를 추가 할 수 있 습 니 다.HttpPost 대상 에 게 도 setEntity(HttpEntity entity)방법 으로 요청 인 자 를 설정 할 수 있 습 니 다.4.HttpClient 대상 의 execute(HttpUriRequest request)를 호출 하여 요청 을 보 냅 니 다.이 방법 은 HttpResponse 를 되 돌려 줍 니 다.5.HttpResponse 의 getAllHeaders(),getHeaders(String name)등 을 호출 하면 서버 응답 헤드 를 가 져 올 수 있 습 니 다.HttpResponse 의 getEntity()방법 을 호출 하면 HttpEntity 대상 을 가 져 올 수 있 습 니 다.이 대상 은 서버 의 응답 내용 을 포장 합 니 다.프로그램 은 이 대상 을 통 해 서버 의 응답 내용 을 가 져 올 수 있 습 니 다.6.연결 해제.실행 방법 이 성공 하 든 안 하 든 연결 을 풀 어야 합 니 다.
try {
        //        HttpClient
        HttpClient httpclient =new DefaultHttpClient();
        //     GET  
        HttpGet request =new HttpGet("www.google.com");
        //   GET  ,            
        String response = httpclient.execute(request, new BasicResponseHandler());
        Log.v("response text", response);
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

2.다 중 스 레 드 HttpClient
실제 프로젝트 에서 우 리 는 여러 곳 에서 HTTP 통신 을 해 야 할 가능성 이 높 습 니 다.이 럴 때 우 리 는 모든 요청 에 새로운 HttpClient 를 만 들 필요 가 없습니다.현재 우리 프로그램 은 같은 HttpClient 를 사용 하여 모든 Http 요청 을 관리 하고 있 습 니 다.동시 요청 이 발생 하면 다 중 스 레 드 문제 가 발생 할 수 있 습 니 다.이것 은 마치 우리 의 브 라 우 저 에 탭 이 하나 밖 에 없 는데 여러 명의 사용자 가 있 는 것 과 같다.A 는 구 글 에 가 려 고 하고 B 는 baidu 에 가 려 고 하 는데 이때 브 라 우 저 는 바 빠 서 바 쁠 수 없다.다행히 HttpClient 는 스 레 드 보안 대상 을 만 드 는 API 를 제공 합 니 다.
public class CustomerHttpClient {
    private static final String CHARSET = HTTP.UTF_8;
    /**
     *      
     */
    public final static int MAX_TOTAL_CONNECTIONS = 800;
    /**
     *            
     */
    public final static int WAIT_TIMEOUT = 60000;
    /**
     *          
     */
    public final static int MAX_ROUTE_CONNECTIONS = 400;
    /**
     *       
     */
    public final static int CONNECT_TIMEOUT = 10000;
    /**
     *       
     */
    public final static int READ_TIMEOUT = 10000;


    private static HttpClient customerHttpClient;

    private CustomerHttpClient() {
    }

    public static synchronized HttpClient getHttpClient() {
        if (null == customerHttpClient) {
            HttpParams params = new BasicHttpParams();
            //         
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params,
                    CHARSET);
            HttpProtocolParams.setUseExpectContinue(params, true);
            HttpProtocolParams
                    .setUserAgent(
                            params,
                            "Mozilla/5.0(Linux;U;Android 2.2.1;en-us;Nexus One Build.FRG83) "
                                    + "AppleWebKit/553.1(KHTML,like Gecko) Version/4.0 Mobile Safari/533.1");
            //     
            /*               */
            ConnManagerParams.setTimeout(params, WAIT_TIMEOUT);
            /*      */
            HttpConnectionParams.setConnectionTimeout(params, CONNECT_TIMEOUT);
            /*      */
            HttpConnectionParams.setSoTimeout(params, READ_TIMEOUT);


            //          
            ConnManagerParams.setMaxTotalConnections(params, MAX_TOTAL_CONNECTIONS);
            //              
            ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
            ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);

            //      HttpClient  HTTP HTTPS    
            SchemeRegistry schReg = new SchemeRegistry();
            schReg.register(new Scheme("http", PlainSocketFactory
                    .getSocketFactory(), 80));
            schReg.register(new Scheme("https", SSLSocketFactory
                    .getSocketFactory(), 443));

            //               HttpClient
            ClientConnectionManager conMgr = new ThreadSafeClientConnManager(
                    params, schReg);
            customerHttpClient = new DefaultHttpClient(conMgr, params);
        }
        return customerHttpClient;
    }
}

1.시간 초과 설정
위의 코드 는 3 가지 시간 초과 설정 을 언급 하여 헷 갈 리 기 쉽다.HttpClient 의 3 가지 시간 초과 설명
/*               */
ConnManagerParams.setTimeout(params, 1000);
/*      */
HttpConnectionParams.setConnectionTimeout(params, 2000);
/*      */
HttpConnectionParams.setSoTimeout(params, 4000);

첫 번 째 줄 설정 ConnectionPoolTimeout:ConnectionManager 가 관리 하 는 연결 풀 에서 연결 을 꺼 내 는 시간 초과 시간 을 정의 합 니 다.여 기 는 1 초 로 설정 합 니 다.
두 번 째 줄 설정 ConnectionTimeout:네트워크 를 통 해 서버 와 연결 하 는 시간 초과 시간 을 정의 합 니 다.Httpclient 패키지 에 서 는 서버 와 의 socket 연결 을 비동기 스 레 드 로 만 듭 니 다.이것 이 바로 이 socket 연결 의 시간 초과 입 니 다.2 초 로 설정 되 어 있 습 니 다.
세 번 째 줄 은 SocketTimeout 을 설정 합 니 다.이것 은 Socket 에서 데 이 터 를 읽 는 시간 초과 시간 을 정의 합 니 다.즉,서버 에서 응답 데 이 터 를 가 져 오 려 면 기 다 려 야 할 시간 을 4 초 로 설정 합 니 다.
상기 3 가지 초과 시간 은 각각 Connection PoolTimeoutException,Connection TimeoutException 과 SocketTimeoutException 을 던진다. 
2.스 레 드 탱크 설정
ThreadSafeClient ConnManager 는 기본적으로 연결 풀 을 사 용 했 습 니 다.
//       
    ConnManagerParams.setMaxTotalConnections(httpParams, 10);
    //         
    ConnPerRouteBean connPerRoute = new ConnPerRouteBean(10);
    ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);

특히 모든 경로(route)의 최대 연결 수 입 니 다.무엇이 하나의 route 입 니까?이곳 route 의 개념 은 환경 기 계 를 운행 하여 목표 기계 까지 의 선로 로 이해 할 수 있다.예 를 들 어 우 리 는 HttpClient 의 실현 을 이용 하여 www.baidu.com 의 자원 과 www.bing.com 의 자원 을 각각 요청 하면 그 는 두 개의 route 가 생 길 것 이다.여기 서 왜 route 최대 연결 수 라 는 매개 변 수 를 특별히 언급 해 야 합 니까?이 매개 변 수 는 기본 값 이 2 이기 때 문 입 니 다.이 매개 변 수 를 설정 하지 않 으 면 기본 적 인 상황 에서 같은 목표 기기 의 최대 병렬 연결 은 2 개 밖 에 없습니다!이것 은 만약 에 특정한 대상 기 계 를 대상 으로 하 는 캡 처 작업 을 수행 하고 있 을 때 연결 탱크 의 최대 연결 수 를 200 으로 설정 하 더 라 도 실제 적 으로 2 개 만 연결 되 어 작업 을 하고 있 고 나머지 198 개의 연결 은 모두 다른 항목 의 기 계 를 위해 서 비 스 를 제공 하고 있다 는 것 을 의미한다.
3.도구 류
하나의 HttpClient 대상 이 있 으 면 우 리 는 자주 사용 하 는 GET 와 POST 요청 을 보 내 는 코드 도 패키지 하여 우리 의 도구 류 에 쓸 수 있 습 니 다.POST 요청 예시:
private static final String TAG ="CustomerHttpClient";

public static String post(String url, NameValuePair... params) {
        try {
            //     
            List formparams = new ArrayList(); //     
            for (NameValuePair p : params) {
                formparams.add(p);
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
                    HTTP.UTF_8);
            //   POST  
            HttpPost request =new HttpPost(url);
            request.setEntity(entity);
            //     
            HttpClient client = getHttpClient();
            HttpResponse response = client.execute(request);
            if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RuntimeException("    ");
            }
            HttpEntity resEntity =  response.getEntity();
            return (resEntity ==null) ?null : EntityUtils.toString(resEntity, CHARSET);
        } catch (UnsupportedEncodingException e) {
            Log.w(TAG, e.getMessage());
            return null;
        } catch (ClientProtocolException e) {
            Log.w(TAG, e.getMessage());
            return null;
        } catch (IOException e) {
            throw new RuntimeException("    ", e);
        }

    } 

4.스 레 드 탱크 기술
4.1 긴 연결 과 짧 은 연결
긴 연결 이란 클 라 이언 트 와 서버 측 이 연결 을 한 후에 여러 번 데이터 전송 을 할 수 있 고 다시 연결 하지 않 아 도 되 며 짧 은 연결 은 매번 데이터 전송 에 클 라 이언 트 와 서버 측 이 연결 을 해 야 한 다 는 것 을 말한다.긴 연결 의 장점 은 매번 데이터 전송 연결 이 구축 되 는 시간 비용 을 줄 이 고 데이터 전송 속 도 를 대폭 높 일 수 있어 P2P 응용 에 매우 적합 하 다 는 것 이다.짧 은 연결 은 매번 데이터 전송 에 연결 이 필요 하 다.우 리 는 HTTP 프로 토 콜 의 전송 층 프로 토 콜 이 TCP 프로 토 콜 이라는 것 을 알 고 있다.TCP 연결 의 구축 과 방출 은 각각 3 번 의 악수 와 4 번 의 악 수 를 해 야 한다.빈번 한 연결 구축 은 시간 비용 을 증가 시 키 는 동시에 Socket 을 자주 만 들 고 없 애 는 것 도 서버 측 자원 에 대한 낭비 이다.웹 사이트 와 같은 B2C 응용 에 대해 동시 다발 요 구 량 이 많 고 모든 사용자 가 빈번 한 조작 을 하지 않 아 도 되 는 상황 에서 대량의 긴 연결 을 유지 하 는 것 은 서버 에 큰 시련 이다.이때 짧 은 연결 이 더 적 용 될 수 있다.HTTP 요청 을 자주 보 내야 하 는 응용 프로그램 은 클 라 이언 트 에서 HTTP 긴 연결 을 사용 해 야 합 니 다.
4.2 연결 탱크
연결 탱크 관리 대상 은 긴 연결 입 니 다.연결 풀 기술 은 연결 을 만 들 고 관리 하 는 버퍼 풀 기술 로 현재 데이터베이스 연결 과 같은 긴 연결 유지 와 관리 에 광범 위 하 게 사용 되 고 있 으 며 시스템 의 응답 시간 을 효과적으로 줄 이 고 서버 자원 의 비용 을 절약 할 수 있다.그 장점 은 주로 두 가지 가 있다.하 나 는 연결 을 만 드 는 자원 비용 을 줄 이 는 것 이 고 다른 하 나 는 자원 의 방문 통제 이다.        HTTP 연결 은 무상 태 입 니 다.이렇게 하면 HTTP 연결 이 짧 은 연결 이라는 착각 을 일 으 키 기 쉽 습 니 다.실제로 HTTP 1.1 은 기본적으로 지속 적 인 연결 입 니 다.HTTP 1.0 도 요청 헤더 에 Connection:keep-alive 를 설정 하여 연결 을 긴 연결 로 할 수 있 습 니 다.
4.3、HttpConnection
연결 탱크 라 는 개념 이 없 으 면 몇 번 요청 하면 몇 개의 IO 를 만 들 수 있 고,방 문 량 이 많은 경우 서버 의 IO 가 소 진 될 수 있다.
4.4、HttpClient3
연결 탱크 의 물건 도 안에 있 습 니 다.MultiThreaded Http Connection Manager 를 사용 합 니 다.대체적으로 다음 과 같 습 니 다.
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();  
HttpClient client = new HttpClient(connectionManager);...//       。  
GetMethod get = new GetMethod("http://jakarta.apache.org/");  
try {  
client.executeMethod(get);// print response to stdout  
System.out.println(get.getResponseBodyAsStream());  
} finally {  
// be sure the connection is released back to the connection   
managerget.releaseConnection();  
}  

jdbc 연결 탱크 의 사용 방식 과 비슷 하 다 는 것 을 알 수 있 습 니 다.불쾌 한 것 은 releaseConnection 을 수 동 으로 호출 하여 연결 을 풀 어야 한 다 는 것 입 니 다.모든 HttpClient.executeMethod 에 method.releaseConnection()이 일치 해 야 합 니 다.
4.5、HttpClient4
HTTP Client 4.0 의 ThreadSafeClient ConnManager 는 HTTP 연결 의 풀 화 관 리 를 실 현 했 습 니 다.연결 을 관리 하 는 기본 단 위 는 Route(경로)이 고 모든 경로 에서 일정한 수량의 HTTP 연결 을 유지 합 니 다.이곳 의 Route 개념 은 클 라 이언 트 기기 에서 타 겟 기기 로 가 는 노선 으로 이해 할 수 있다.예 를 들 어 HttpClient 의 실현 을 이용 하여 각각 www.163.com 의 자원 과 www.sina.com 의 자원 을 요청 하면 두 개의 route 가 생 긴 다.결 성 된 조건 에서 모든 Route,HttpClient 는 2 개의 연결 만 유지 하고 총 20 개의 연결 을 초과 하지 않 습 니 다.대부분의 응용 프로그램 에 있어 부족 한 것 이 분명 합 니 다.HTTP 매개 변 수 를 설정 하여 조정 할 수 있 습 니 다.
HttpParams params = new BasicHttpParams();
//              200
ConnManagerParams.setMaxTotalConnections(params,200);
//               20
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
//      IP      
HttpHost localhost = new HttpHost("locahost", 80);
connPerRoute.setMaxForRoute(new HttpRoute(localhost), 50);
ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(
new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm, params);

     
설정 가능 한 HTTP 인 자 는 다음 과 같 습 니 다.
1)  http.conn-manager.timeout 한 라인 이 연결 풀 에 스 레 드 분 배 를 요청 할 때 연결 풀 에 할당 할 수 있 는 연결 이 없 으 면 http.conn-manager.timeout 이 시간 을 초과 할 때 까지 연결 풀 타임 아웃 Exception 을 던 집 니 다.
2)  http.conn-manager.max-per-route 모든 경로 의 최대 연결 수;
3)  http.conn-manager.max-total 의 총 연결 수;
4.6 기한 이 지난 긴 연결
연결 의 유효성 검 사 는 모든 연결 탱크 가 직면 하 는 일반적인 문제 입 니 다.대부분의 HTTP 서버 는 자원 비용 을 제어 하기 위해 긴 연결 을 영구적 으로 유지 하지 않 고 한동안 연결 을 닫 습 니 다.연결 탱크 의 연결 을 되 돌려 줍 니 다.서버 에서 닫 혔 다 면 클 라 이언 트 는 이 상태 변 화 를 감지 하지 못 하고 Socket 을 제때에 닫 습 니 다.이 로 인해 연결 탱크 에서 가 져 온 연결 이 반드시 효과 적 인 것 은 아니다.이 문제 의 해결 방법 은 요청 할 때마다 이 연결 이 너무 오래 되 었 는 지,만 료 되 었 는 지 확인 하 는 것 이다.그러나 이 방법 은 매번 요청 할 때마다 추가 비용 을 늘 릴 수 있다.HTTP Client 4.0 의 ThreadSafeClient ConnManager 는 closeExpired Connections()방법 과 closeIdleConnections()방법 을 제공 하여 이 문 제 를 해결 합 니 다.이전 방법 은 연결 풀 에 있 는 모든 만 료 된 연결 을 제거 하 는 것 입 니 다.연결 이 언제 만 료 되 는 지 설정 할 수 있 습 니 다.설정 방법 은 아래 에서 언급 할 것 입 니 다.다음 방법 은 일정 시간 남 은 연결 을 닫 고 단독 스 레 드 로 이 작업 을 완성 할 수 있 습 니 다.
public static class IdleConnectionMonitorThread extends Thread {
private final ClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(ClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
//        
connMgr.closeExpiredConnections();
//         30    
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}

방금 말 했 듯 이 클 라 이언 트 는 연결 의 만 료 시간 을 설정 할 수 있 고 HttpClient 의 setKeepAlive Strategy 방법 으로 연결 의 만 료 시간 을 설정 할 수 있 습 니 다.그러면 closeExpired Connections()방법 과 결합 하여 연결 풀 의 연결 이 실 효 된 것 을 해결 할 수 있 습 니 다.
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
if ("www.163.com".equalsIgnoreCase(target.getHostName())) {
//   163       ,  5 
return 5 * 1000;
} else {
//       30 
return 30 * 1000;
}
}
})

좋은 웹페이지 즐겨찾기