HttpClient 연결 풀 및 재 시도 메커니즘

110925 단어 HttpClient
(1) HttpClient
  • 안내
  • HttpClient 는 Apache Jakarta Common 의 하위 항목 으로 HTTP 프로 토 콜 을 지원 하 는 효율 적 이 고 최신 기능 이 풍부 한 클 라 이언 트 프로 그래 밍 도 구 를 제공 할 수 있 으 며 표준 자바 언어 를 기반 으로 합 니 다.
  • 기능 소개
  • HTTP 와 HTTPS 프로 토 콜 지원
  • HTTP 방법, GET, POST, PUT, DELETE 등 을 실현 했다.
  • 연결 관리 자 는 다 중 스 레 드 응용 을 지원 합 니 다.
  • 접속 시간 초과 설정 가능
  • 사용 방법
  • HttpClient 를 사용 하여 요청 을 보 냅 니 다. 응답 을 받 으 면 몇 단계 로 나 눌 수 있 습 니 다.
  • HttpClient 대상 생 성
  • 요청 방법의 인 스 턴 스 를 만 들 고 URL
  • 을 지정 합 니 다.
  • 요청 파 라 메 터 를 보 내 고 GET 요청 과 POST 요청 으로 파 라 메 터 를 보 내 는 방식 이 다 릅 니 다
  • HttpClient 대상 의 execute 방법 을 호출 하여 HttpResponse 대상
  • 을 되 돌려 줍 니 다.
  • HttpResponse 의 getAllHeaders (), getHeaders (String name) 등 방법 으로 서버 응답 헤드 를 가 져 올 수 있 습 니 다.HttpResponse 의 getEntity () 방법 을 호출 하면 HttpEntity 대상 을 가 져 올 수 있 습 니 다. 이 대상 은 서버 의 응답 내용
  • 을 포장 합 니 다.
  • 연결 해제.성공 여부 와 상 관 없 이 연결 해제
  • (2) HttpClient Util
    2.1 HttpClient 버 전
    필자 가 사용 한 버 전 은 4.5.5 이 고 Maven 프로젝트 이기 때문에 pom 파일 에 해당 하 는 좌 표를 도입 해 야 합 니 다.
    <dependency>  
       <groupId>org.apache.httpcomponents</groupId>  			 	
       <artifactId>httpclient</artifactId>  					 	
       <version>4.5.5</version>
    </dependency>
    

    2.2 프로젝트 에 사용 되 는 도구 류 는 다음 과 같다.
    package cn.htjc.customer.util;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ServiceUnavailableRetryStrategy;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.config.Registry;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.conn.socket.ConnectionSocketFactory;
    import org.apache.http.conn.socket.PlainConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.protocol.HttpContext;
    import org.apache.http.ssl.SSLContextBuilder;
    import org.apache.http.util.EntityUtils;
    
    import java.io.IOException;
    import java.net.SocketTimeoutException;
    import java.net.URI;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    @Slf4j
    public class HttpClientUtil {
    
        // utf-8    
        private static final String CHARSET_UTF_8 = "utf-8";
    
        // HTTP    。   form     ,    
        private static final String CONTENT_TYPE_FORM_URL = "application/x-www-form-urlencoded";
    
        //      
        private static PoolingHttpClientConnectionManager pool;
    
        //     
        private static RequestConfig requestConfig;
    
        static {
    
            try {
                log.info("     HttpClient......  ");
                SSLContextBuilder builder = new SSLContextBuilder();
                builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
                //        HTTP   HTPPS
                Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", sslsf).build();
                //         
                pool = new PoolingHttpClientConnectionManager(
                        socketFactoryRegistry);
                //            
                pool.setMaxTotal(200);
                //                 
                pool.setDefaultMaxPerRoute(20);
                //            requestConfig
    
                //             timeout
                int socketTimeout = 1000;
                //             timeout
                int connectTimeout = 10000;
                //          timeout
                int connectionRequestTimeout = 10000;
                //        
                requestConfig = RequestConfig.custom().setConnectionRequestTimeout(
                        connectionRequestTimeout).setSocketTimeout(socketTimeout).setConnectTimeout(
                        connectTimeout).build();
                log.info("     HttpClient......  ");
            } catch (Exception e) {
                log.error("     HttpClient......  ");
            }
    
    
        }
    
        private HttpClientUtil() {
        }
    
    
        private static CloseableHttpClient getHttpClient() {
    
    		//     503   ,     
            ServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new ServiceUnavailableRetryStrategy() {
                @Override
                public boolean retryRequest(HttpResponse httpResponse, int i, HttpContext httpContext) {
                    if (i < 3) {
                        log.info("ServiceUnavailableRetryStrategy========================"+i);
                        return true;
                    }
                    return false;
                }
    
                @Override
                public long getRetryInterval() {
                    return 2000L;
                }
            };
    
            CloseableHttpClient httpClient = HttpClients.custom()
                    //        
                    .setConnectionManager(pool)
                    //       
                    .setDefaultRequestConfig(requestConfig)
                    //       
                    .setRetryHandler(new DefaultHttpRequestRetryHandler())
                    .setServiceUnavailableRetryStrategy(serviceUnavailableRetryStrategy)
                    .build();
            return httpClient;
        }
    
        public static String doGet(String url, Map<String, String> param) {
    
            //   Httpclient  
            CloseableHttpClient httpClient = getHttpClient();
            String resultString = "";
            CloseableHttpResponse response = null;
            try {
                //   uri
                URIBuilder builder = new URIBuilder(url);
                if (param != null) {
                    for (String key : param.keySet()) {
                        builder.addParameter(key, param.get(key));
                    }
                }
                URI uri = builder.build();
    
                //   http GET  
                HttpGet httpGet = new HttpGet(uri);
    
                //     
                response = httpClient.execute(httpGet);
                //          200
                if (response.getStatusLine().getStatusCode() == 200) {
                    resultString = EntityUtils.toString(response.getEntity(), CHARSET_UTF_8);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (response != null) {
                        response.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return resultString;
        }
    
        public static String doGet(String url) {
            return doGet(url, null);
        }
    
        public static String doPost(String url, Map<String, String> param) {
            //   Httpclient  
            CloseableHttpClient httpClient = getHttpClient();
            CloseableHttpResponse response = null;
            String resultString = "";
            try {
                //   Http Post  
                HttpPost httpPost = new HttpPost(url);
                //       
                if (param != null) {
                    List<NameValuePair> paramList = new ArrayList<>();
                    for (String key : param.keySet()) {
                        paramList.add(new BasicNameValuePair(key, param.get(key)));
                    }
                    //     
                    UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, CHARSET_UTF_8);
                    entity.setContentType(CONTENT_TYPE_FORM_URL);
                    httpPost.setEntity(entity);
                }
                //   http  main
                response = httpClient.execute(httpPost);
                resultString = EntityUtils.toString(response.getEntity(), CHARSET_UTF_8);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (response != null) {
                        response.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return resultString;
        }
    
        public static String doPost(String url) {
            return doPost(url, null);
        }
    
        public static String doPostJson(String url, String json) {
            //   Httpclient  
            CloseableHttpClient httpClient = getHttpClient();
            CloseableHttpResponse response = null;
            String resultString = "";
            try {
                //   Http Post  
                HttpPost httpPost = new HttpPost(url);
                //       
                StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
                httpPost.setEntity(entity);
                //   http  
                response = httpClient.execute(httpPost);
                resultString = EntityUtils.toString(response.getEntity(), CHARSET_UTF_8);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (response != null) {
                        response.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return resultString;
        }
    
    }
    

    코드 에 @ Slf4j 가 나 타 났 습 니 다. log 를 도입 하고 로 그 를 수 동 으로 인쇄 하 는 역할 을 합 니 다.이 주 해 는 lombok 의 주해 입 니 다.Route 가 무엇 인지 설명해 주세요.Route 의 개념 은 클 라 이언 트 기기 에서 타 겟 기기 까지 의 노선 으로 이해 할 수 있다. 예 를 들 어 HttpClient 의 실현 을 이용 하여 각각 www. 163. com 의 자원 과 www. sina. com 의 자원 을 요청 하면 두 개의 route 가 생 긴 다.결 성 된 조건 에서 모든 Route 에 대해 HttpClient 는 2 개의 연결 만 유지 하고 총 20 개의 연결 을 초과 하지 않 습 니 다.
    2. 필 자 는 http 연결 풀 을 다시 말 합 니 다.
  • 1 http 연결 풀 을 왜 사용 합 니까?
  • 지연 이 줄 어 들 고 연결 풀 을 사용 하지 않 으 면 http 요청 을 할 때마다 tcp 연결 (세 번 악수) 을 다시 만 들 고 다 쓰 면 연결 (네 번 악수) 을 닫 으 며 연결 풀 을 사용 하면 시간 대별 소모 가 줄어든다.연결 탱크 관리의 대상 은 모두 긴 연결 이다.
  • 더 큰 병행 을 지원 합 니 다. 연결 풀 은 같은 호스트 (또는 같은 포트 에 자주 접근 하 라 는 요청 에 만 적용 되 기 때문에 연결 풀 은 반복 적 으로 연결 을 구축 하고 포트 자원 을 선점 하 는 상황 을 피 할 수 있 습 니 다. 연결 풀 을 사용 하지 않 으 면 연결 이 구축 되 지 않 을 수 있 습 니 다.

  • 2 시간 초과 설정 은 먼저 세 가지 개념 을 알 아야 한다. socketTimeout, connectTimeout, connection RequestTimeout.socketTimeout: 클 라 이언 트 와 서버 가 데 이 터 를 읽 는 timeout connectTimeout: 클 라 이언 트 와 서버 가 연 결 된 timeout connection RequestTimeout: 연결 풀 에서 연 결 된 timeout
  • 3 설명: 한 번 http 요청 한 번 http 요청 은 반드시 세 단계 가 있 습 니 다. 하 나 는 연결 을 만 드 는 것 입 니 다.2. 데이터 전송;3. 연결 을 끊 습 니 다.연결 이 정 해진 시간 내 에 (Connection TimeOut) 완성 되 지 않 으 면 이번 연결 은 끝난다.후속 소켓 타임 아웃 익 스 셉 션 은 없 을 겁 니 다.연결 이 만들어 진 후에 만 ConnectionTimeOutException 이 발생 하지 않 고 데이터 전송 을 시작 할 수 있 으 며, 데이터 가 정 해진 시간 내 (SocketTimeOut) 전송 이 끝나 면 연결 을 끊 는 다.그렇지 않 으 면 SocketTimeOutException 을 촉발 합 니 다.

  • (3) HttpClient 의 재 시도 메커니즘
    위 에서 이렇게 많은 말 을 한 것 은 바로 아래 의 재시험 문 제 를 끌 어 내기 위해 서 이다.프로젝트 에서 외부 인터페이스 에 접근 하려 고 하기 때문에 인터페이스 에 접근 할 때 가끔 SocketTimeOutException: Read timed out 이 나타 납 니 다. 사실은 클 라 이언 트 가 서버 의 데 이 터 를 읽 는 데 시간 이 초과 되 었 습 니 다.
    3.1. 그럼 문제 가 생 겼 습 니 다. HttpClient 재 시도 전략 이 있 습 니까?
    PoolingHttpClient ConnectionManager 를 사용 하여 얻 은 InternalHttpClient 인 스 턴 스 는 추상 적 인 CloseableHttpClient 의 실현 입 니 다.
    Client ExecChain 인터페이스의 실현 클래스 HttpClient连接池及重试机制_第1张图片 를 살 펴 보고 build () 방법 을 간단하게 살 펴 보 겠 습 니 다.
    public CloseableHttpClient build() {
        //         
        //   MainClientExec
        ClientExecChain execChain = this.createMainExec(requestExecCopy, (HttpClientConnectionManager)connManagerCopy, (ConnectionReuseStrategy)reuseStrategyCopy, (ConnectionKeepAliveStrategy)keepAliveStrategyCopy, new ImmutableHttpProcessor(new HttpRequestInterceptor[]{new RequestTargetHost(), new RequestUserAgent(userAgentCopy)}), (AuthenticationStrategy)targetAuthStrategyCopy, (AuthenticationStrategy)proxyAuthStrategyCopy, (UserTokenHandler)userTokenHandlerCopy);
        execChain = this.decorateMainExec(execChain);
        //   ProtocolExec
        ClientExecChain execChain = new ProtocolExec(execChain, httpprocessorCopy);
        ClientExecChain execChain = this.decorateProtocolExec(execChain);
        // Add request retry executor, if not disabled
        if (!automaticRetriesDisabled) {
                HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
                if (retryHandlerCopy == null) {
                    retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
                }
                execChain = new RetryExec(execChain, retryHandlerCopy);
            } 
        //       
        //      ,  ServiceUnavailableRetryExec
        ServiceUnavailableRetryStrategy serviceUnavailStrategyCopy = this.serviceUnavailStrategy;
            if (serviceUnavailStrategyCopy != null) {
                execChain = new ServiceUnavailableRetryExec((ClientExecChain)execChain, serviceUnavailStrategyCopy);
            }
        
        //   RedirectExec
        if (!this.redirectHandlingDisabled) {
                authSchemeRegistryCopy = this.redirectStrategy;
                if (authSchemeRegistryCopy == null) {
                    authSchemeRegistryCopy = DefaultRedirectStrategy.INSTANCE;
                }
    
                execChain = new RedirectExec((ClientExecChain)execChain, (HttpRoutePlanner)routePlannerCopy, (RedirectStrategy)authSchemeRegistryCopy);
            }
       //        
        return new InternalHttpClient((ClientExecChain)execChain, (HttpClientConnectionManager)connManagerCopy, (HttpRoutePlanner)routePlannerCopy, cookieSpecRegistryCopy, (Lookup)authSchemeRegistryCopy, (CookieStore)defaultCookieStore, (CredentialsProvider)defaultCredentialsProvider, this.defaultRequestConfig != null ? this.defaultRequestConfig : RequestConfig.DEFAULT, closeablesCopy);
    }
    

    위 에서 아래로 서로 다른 Client ExecChain 인 스 턴 스 를 만 들 었 습 니 다.메모: 대상 을 만 드 는 순 서 는 실행 기 체인 의 순서 입 니 다.
    CloseableHttpClient 인 스 턴 스 를 구성 할 때 자동 재 시도 기능 을 닫 았 는 지 여 부 를 판단 합 니 다. automaticRetriesDisabled 는 기본적으로 false 입 니 다.실행 체인 이 지정 되 지 않 으 면 RetryExec 를 사용 합 니 다.기본 재 시도 정책 은 DefaultHttpRequestRetry Handler 입 니 다.
    ServiceUnavailable Retry Strategy 인 터 페 이 스 를 다시 쓰 거나 Default ServiceUnavailable Retry Strategy 를 사용 하면 ServiceUnavailable Retry Exec 도 실행 기 체인 에 가입 합 니 다.
    마찬가지 로 redirectHandling Disabled 는 기본적으로 false 이 고 RedirectExec 도 실행 기 체인 에 가입 하여 가장 먼저 실 행 됩 니 다.
    3.2 실행 절차
    앞에서 우리 가 사용 하 는 Htticlient 는 본질 적 으로 InternalhttpClient 인 것 을 보 았 습 니 다. 여기 서 그의 실행 이 데 이 터 를 보 내 는 방법 을 보 겠 습 니 다.
     @Override
        protected CloseableHttpResponse doExecute(
                final HttpHost target,
                final HttpRequest request,
                final HttpContext context) throws IOException, ClientProtocolException {
                //                  
            return this.execChain.execute(route, wrapper, localcontext, execAware);
            }
        }
    

    먼저 RedirectExec 를 거 쳐 RedirectExec 에서 ServiceUnavailableRetryExec 의 excute () 를 호출 하고 ServiceUnavailableRetryExec 에 들 어간 후 RetryExec 의 excute () 를 호출 하 며 RetryExec 에 들 어간 후 ProtocolExec 의 execute () 를 호출 하고 마지막 으로 MainClient Exec 의 excute () 를 호출 합 니 다.
    실행 기 체인 이 끝 난 후 HttpRequestExecutor 의 excute (), excute () 방법 을 실행 하여 자신의 doSendRequest () 를 호출 하 였 습 니 다.
    이후 한 걸음 한 걸음 돌아 와 이상 을 만 나 처리 했다.
    다음은 Retry Exec 에서 요청 한 부분 입 니 다.
    public CloseableHttpResponse execute(HttpRoute route, 
     									 HttpRequestWrapper request, 
     									 HttpClientContext context, 	 
     									 HttpExecutionAware execAware) throws IOException, HttpException {
            //     
            Args.notNull(route, "HTTP route");
            Args.notNull(request, "HTTP request");
            Args.notNull(context, "HTTP context");
            //           
            Header[] origheaders = request.getAllHeaders();
            //         1
            int execCount = 1;
    
            while(true) {
                try {
                	//     executor  http  
                    return this.requestExecutor.execute(route, request, context, execAware);
                } catch (IOException var9) {
                   //   IO     ,           ,          
                    if (execAware != null && execAware.isAborted()) {
                        this.log.debug("Request has been aborted");
                        throw var9;
                    }
    				//       ,             ,          
                    if (!this.retryHandler.retryRequest(var9, execCount, context)) {
                        if (var9 instanceof NoHttpResponseException) {
                            NoHttpResponseException updatedex = new NoHttpResponseException(route.getTargetHost().toHostString() + " failed to respond");
                            updatedex.setStackTrace(var9.getStackTrace());
                            throw updatedex;
                        }
    
                        throw var9;
                    }
    				//   
                    if (this.log.isInfoEnabled()) {
                        this.log.info("I/O exception (" + var9.getClass().getName() + ") caught when processing request to " + route + ": " + var9.getMessage());
                    }
    				//   
                    if (this.log.isDebugEnabled()) {
                        this.log.debug(var9.getMessage(), var9);
                    }
    				//               
                    if (!RequestEntityProxy.isRepeatable(request)) {
                        this.log.debug("Cannot retry non-repeatable request");
                        throw new NonRepeatableRequestException("Cannot retry request with a non-repeatable request entity", var9);
                    }
    				
    				//      	
                    request.setHeaders(origheaders);
                    //   
                    if (this.log.isInfoEnabled()) {
                        this.log.info("Retrying request to " + route);
                    }
    
                    ++execCount;
                }
            }
        }
    

    IOException 이 발생 하면 다시 시도 할 지 여 부 를 판단 합 니 다.재 시도 하면 해당 횟수 를 기록 하고 재 시도 하지 않 으 면 이상 을 던 지고 물러난다.
    	//     final       ,    
        public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler();
    
        //    
        private final int retryCount;
    
        //           ,         
        private final boolean requestSentRetryEnabled;
    
    	//          
        private final Set<Class<? extends IOException>> nonRetriableClasses;
    
    	//     3 ,      ,    
        public DefaultHttpRequestRetryHandler() {
            this(3, false);
        }
        public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
            this(retryCount, requestSentRetryEnabled, Arrays.asList(
                    InterruptedIOException.class,
                    UnknownHostException.class,
                    ConnectException.class,
                    SSLException.class));
        }
        protected DefaultHttpRequestRetryHandler(
                final int retryCount,
                final boolean requestSentRetryEnabled,
                final Collection<Class<? extends IOException>> clazzes) {
            super();
            this.retryCount = retryCount;
            this.requestSentRetryEnabled = requestSentRetryEnabled;
            this.nonRetriableClasses = new HashSet<Class<? extends IOException>>();
            for (final Class<? extends IOException> clazz: clazzes) {
                this.nonRetriableClasses.add(clazz);
            }
        }
    

    구조 함 수 를 통 해 알 수 있 듯 이:
  • 3 회 재 시도
  • 요청 이 성공 하면 다시 시도 하지 않 습 니 다
  • Interrupted IOException, Unknown HostException, ConnectException, SSLException, 이 4 가지 이상 이 발생 하면 다시 시도 하지 않 습 니 다
  • public boolean retryRequest(IOException exception, 
                                int executionCount, 
                                HttpContext context) {  
         //                              
         Args.notNull(exception, "Exception parameter");    
         Args.notNull(context, "HTTP context");   
         //             ,        
    	 if (executionCount > this.retryCount) {        
         		return false;  
         		//         4   ,        
         } else if (this.nonRetriableClasses.contains(exception.getClass())) {        
         		return false;    
         } else {        
         		Iterator i$ = this.nonRetriableClasses.iterator();        
         		Class rejectException;        
         		do {            
         			if (!i$.hasNext()) {                
         			HttpClientContext clientContext = HttpClientContext.adapt(context);                		   
         			HttpRequest  request = clientContext.getRequest();  
         			//                          
         			if (this.requestIsAborted(request)) {                    
         					return false;                
         			}        
         			//            ,get     ,post/put               
         			if (this.handleAsIdempotent(request)) {                    
         					return true;                
         			}      
         			//              ,                        
         			if (clientContext.isRequestSent() && !this.requestSentRetryEnabled) {                    
         					return false;                
         			}                
         			return true;            
         			}            
         			rejectException = (Class)i$.next();        
         		} while(!rejectException.isInstance(exception));   ///                ,    
         		return false;    
           }
        }
    

    기본 재 시도 정책
  • 세 번 을 넘 으 면 재시험 을 하지 않 는 다
  • 이상 4 중 이상 및 하위 클래스 는 재 시도 하지 않 음
  • 같은 요청 이 비동기 작업 에서 정지 되 었 습 니 다. 재 시도 하지 않 습 니 다
  • 멱 등의 방법 은 재 시험 할 수 있다. 예 를 들 어 get 은 http body 를 포함 하여 모두 비 멱 등 이 라 고 볼 수 있다
  • .
  • 요청 이 전송 되 지 않 았 습 니 다. 재 시도 가능
  • 질문 이 왔 습 니 다. 성공 적 인 요청 을 보 내 는 것 은 어 떻 습 니까?다음 코드 는 HttpCoreContext 에 있 습 니 다. HttpCoreContext 는 HttpContext 의 실현 클래스 입 니 다.
      public static final String HTTP_REQ_SENT    = "http.request_sent";
    
        public boolean isRequestSent() {
            final Boolean b = getAttribute(HTTP_REQ_SENT, Boolean.class);
            return b != null && b.booleanValue();
        }
    

    현재 http Context 의 http. requestsent 를 true 로 설정 하면 발송 에 성공 했다 고 생각 합 니 다.
    HttpRequestExecutor 의 excute () 는 자신의 doSendRequest () 를 호출 합 니 다.
    protected HttpResponse doSendRequest(HttpRequest request, 
    									 HttpClientConnection conn, 
    									 HttpContext context) throws IOException, HttpException {
            //     
            Args.notNull(request, "HTTP request");
            Args.notNull(conn, "Client connection");
            Args.notNull(context, "HTTP context");
            HttpResponse response = null;
            //         
            context.setAttribute("http.connection", conn);
            //        , http.request_sent     context    ,  false
            context.setAttribute("http.request_sent", Boolean.FALSE);
            //  request header     
            conn.sendRequestHeader(request);
            //    post/put   body   ,      
            if (request instanceof HttpEntityEnclosingRequest) {
                boolean sendentity = true;
                //   http     
                ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
                //   100-continue,  http    1.0
                if (((HttpEntityEnclosingRequest)request).expectContinue() && !ver.lessEquals(HttpVersion.HTTP_1_0)) {
                	//       ,    
                    conn.flush();
                    // Checks if response data is available from the connection
                    if (conn.isResponseAvailable(this.waitForContinue)) {
                    	// Receives the request line and headers of the next response available from this connection.
                        response = conn.receiveResponseHeader();
                        //           (   body)
                        if (this.canResponseHaveBody(request, response)) {
                        	// Receives the next response entity available from this connection and attaches it to an existing HttpResponse object.
                            conn.receiveResponseEntity(response);
                        }
    					//        
                        int status = response.getStatusLine().getStatusCode();
                        if (status < 200) {
                            if (status != 100) {
                                throw new ProtocolException("Unexpected response: " + response.getStatusLine());
                            }
                            response = null;
                        } else {
                            sendentity = false;
                        }
                    }
                }
    
                if (sendentity) {
                	//           
                    conn.sendRequestEntity((HttpEntityEnclosingRequest)request);
                }
            }
    		// Writes out all pending buffered data over the open connection.
            conn.flush();
            //  http.request_sent  true
            context.setAttribute("http.request_sent", Boolean.TRUE);
            return response;
        }
    

    실체 소지 여 부 를 판단 하 는 방법
    protected boolean canResponseHaveBody(HttpRequest request, HttpResponse response) {
            //    head  ,  false  HEAD:       
            if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
                return false;
            } else {
                int status = response.getStatusLine().getStatusCode();
                return status >= 200 && status != 204 && status != 304 && status != 205;
            }
        }
    

    주: HttpEntity Enclosing Request 는 인터페이스 입 니 다.
    public interface HttpEntityEnclosingRequest extends HttpRequest {
        //   Server        
        boolean expectContinue();
    
        //    httpEntity  
        void setEntity(HttpEntity entity);
    
    	//   httpEntity
        HttpEntity getEntity();
    }
    

    HttpEntityEnclosing RequestBase 는 HttpEntityEnclosing Request 를 실현 하 는 추상 적 인 클래스 입 니 다.
    public abstract class HttpEntityEnclosingRequestBase extends HttpRequestBase implements HttpEntityEnclosingRequest {
       
        // HttpEntity           ,   http     ,      ,  StringEntity
        private HttpEntity entity;
    
        public HttpEntityEnclosingRequestBase() {
        }
    
        public HttpEntity getEntity() {
            return this.entity;
        }
    
        public void setEntity(HttpEntity entity) {
            this.entity = entity;
        }
    
     	//           expect-continue
        public boolean expectContinue() {
           //       Except   	
            Header expect = this.getFirstHeader("Expect");
            //   except   ,      100-continue   true
            return expect != null && "100-continue".equalsIgnoreCase(expect.getValue());
        }
    
        public Object clone() throws CloneNotSupportedException {
            HttpEntityEnclosingRequestBase clone = (HttpEntityEnclosingRequestBase)super.clone();
            if (this.entity != null) {
                clone.entity = (HttpEntity)CloneUtils.cloneObject(this.entity);
            }
    
            return clone;
        }
    }
    

    아래 그림 에서 알 수 있 듯 이 HttpPost 와 HttpPut 는 HttpEntity Enclosing RequestBase 의 하위 클래스 HttpClient连接池及重试机制_第2张图片 로 상기 조작 과정 을 간략하게 분석 한 것 이다.
  • http. request 시작sent 를 false 로 설정
  • 흐 르 는 flush 데 이 터 를 통 해 클 라 이언 트
  • 그리고 http. requestsent 를 true 로 설정
  • 분명히 conn. flush () 는 이상 이 발생 할 수 있다.메모: conn 은 모두 연결 탱크 에서 가 져 옵 니 다.
    3.3 닫 고 다시 시도 하기
    기본적으로 재 시 도 를 시작 합 니 다. HttpClient Builder 를 만 들 때 아래 방법 으로 닫 을 수 있 습 니 다.
    public final HttpClientBuilder disableAutomaticRetries() {
            this.automaticRetriesDisabled = true;
            return this;
        }
    

    (4) 총화
    4.1 재 시도 발생 조건
  • IOException 이 발생 해 야 재 시도
  • Interrupted IOException, Unknown HostException, ConnectException, SSLException, 이 4 가지 이상 이 발생 하면 다시 시도 하지 않 습 니 다
  • get 방법 은 3 번 다시 시도 할 수 있 습 니 다. post 방법 에 대응 하 는 socket 흐름 이 flush 에 성공 하지 않 았 을 때 3 번 다시 시도 할 수 있 습 니 다
  • 4.2 재 시도 이상 발생 하지 않 음
  • Interrupted IOException, 스 레 드 중단 이상
  • Unknown HostException, 대응 하 는 host 를 찾 을 수 없습니다
  • ConnectException, host 를 찾 았 으 나 연결 을 만 드 는 데 실 패 했 습 니 다.
  • SSLException, https 인증 이상
  • 4.3 실천 과정 에서 발생 하 는 이상
    또한, 우 리 는 두 가지 시간 초과, 연결 시간 초과 와 읽 기 시간 초과: 1. java. net. SocketTimeoutException: Read timed out 2. java. net. SocketTimeoutException: connect timed out 두 가지 시간 초 과 는 모두 SocketTimeoutException 이 며, Interrupted IO Exception 을 계승 하여 스 레 드 중단 이상 에 속 하 며, 재 시도 하지 않 습 니 다.

    좋은 웹페이지 즐겨찾기