JAVA HttpClient重發、超時不生效

HttpClient真的是一個神奇的東西,版本之間差異過大,V4.3之前,V4.3以後V4.5以前,V4.5以後都幾本是不兼容。所以JAVA在這一塊的開發真的是十分頭疼。

這位仁兄在深深的被折磨後很好的幫我們總結了HTTP不同版本超時

然而,本人在HttpClient 4.3.6版本下,試了無數次。。還是走不通。雖然已經把RequestConfig加到配置裏面了,而且斷點跟蹤也看到配置已配好,也加在httpPost對象裏面。但是,它就是不生效,我也很無奈。

所以,改用HttpUrlConnection進行發起POST請求。發現可以,代碼如下。

  •  其中,data是一個普通的POJO,可以轉成json然後再轉成String,作爲請求實體。
  •  一定要養好設置編碼的習慣,不管是請求內容還是返回的數據流。
  • 請求結束後一定要把可以關的都關閉
  • 一部分代碼是爲了忽略SSL證書錯誤才寫的。如果沒有這個需求,去掉此段代碼。
        
        HttpsURLConnection httpUrlCoNN = null;
        OutputStream outputStrem = null;
        OutputStreamWriter outWrite = null;
        InputStream inputStream = null;
        InputStreamReader inputReader = null;
        BufferedReader bufferReader = null;
        
        String tmpLineStr = null;
        StringBuffer resultBuffer = new StringBuffer();
        
        try{
        	trustAllHttpCertifications();
        	HostnameVerifier verify = new HostnameVerifier() {
				
				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					// TODO Auto-generated method stub
					return true;
				}
			};
			HttpsURLConnection.setDefaultHostnameVerifier(verify);
        	
        	String dataParam = JSON.toJSONString(data);
        	URL url = new URL(Url);
        	URLConnection urlConn = url.openConnection();
        	httpUrlCoNN = (HttpsURLConnection) urlConn;
        	httpUrlCoNN.setDoInput(true);
        	httpUrlCoNN.setDoOutput(true);
        	
        	//超時
        	int timeoutMilSec = 60000;
        	httpUrlCoNN.setConnectTimeout(timeoutMilSec);
        	httpUrlCoNN.setReadTimeout(timeoutMilSec);
        	
        	httpUrlCoNN.setUseCaches(false);
        	httpUrlCoNN.setRequestMethod("POST");
        	httpUrlCoNN.setRequestProperty("Connection", "Keep-Alive");
        	httpUrlCoNN.setRequestProperty("Accept-Charset", "UTF-8");
        	httpUrlCoNN.setRequestProperty("Content-Type", "application/json;charsert=utf-8");
        	httpUrlCoNN.setRequestProperty("Content-Length", String.valueOf(dataParam.length()));
        	
        	
        	outputStrem = httpUrlCoNN.getOutputStream();
        	outWrite = new OutputStreamWriter(outputStrem);
        	outWrite.write(dataParam.toString());
        	outWrite.flush();
        	if(httpUrlCoNN.getResponseCode() >= 300){
        		//返回錯
        		log.info("httpResultCode = {}",httpUrlCoNN.getResponseCode());
        		return null;
        	}
        	//接收響應流
        	inputStream = httpUrlCoNN.getInputStream();
        	inputReader = new InputStreamReader(inputStream,"UTF-8");
        	bufferReader = new BufferedReader(inputReader);
        	
        	while((tmpLineStr = bufferReader.readLine())!= null){
        		resultBuffer.append(tmpLineStr);
        	}
        }catch(Exception e){
        	log.info("return  = {}",e.getMessage());
        }finally{
        	if(null != outWrite){
        		outWrite.close();
        	}
        	if(null != outputStrem){
        		outputStrem.close();
        	}
        	if(null != bufferReader){
        		bufferReader.close();
        	}
        	if(null != inputReader){
        		inputReader.close();
        	}
        	if(null != inputStream){
        		inputStream.close();
        	}
        	if(null != httpUrlCoNN){
        		httpUrlCoNN.disconnect();
        	}
        }

這部分代碼是爲了忽略SSL證書錯誤才寫的。如果沒有這個需求,去掉此段代碼。

    private void  trustAllHttpCertifications(){
		TrustManager[] trustAllCerts = new TrustManager[1];
		TrustManager tm = new miTM();
		trustAllCerts[0] = tm;
		SSLContext sc = null;
		try {
			sc = SSLContext.getInstance("SSL");
			sc.init(null, trustAllCerts, null);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
	}
	
	class miTM implements TrustManager,X509TrustManager{

		@Override
		public void checkClientTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void checkServerTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub
			
		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			// TODO Auto-generated method stub
			return null;
		}
		
		public boolean isServerTrusted(X509Certificate[] certs){
			return true;
		}
		
		public boolean isClientTrusted(X509Certificate[] certs){
			return true;
		}
		
	}


1)如果新建線程,然後在線程裏發起http請求,一定要做超時控制。要不然,先不說會一直存在http連接,佔用帶寬,如果線程先於http請求斷開,會導致報出SocketException。而且此時,這個報錯是不可以通過IOException捕獲(我也知道SocketException是繼承了IOException,但是無奈人家就在這種情況下不會捕獲)。你只能使用catch (Throwable e){}來做處理。但是,此時,就算捕獲到,但是該線程在線程池裏也不能重新運行了。

2)這是經驗,可以運用到其他地方:因爲HttpClient相對於HttpUrlConnection而言,只是做了一層封裝。如果不同的版本都不能使配置生效的時候,可以直接使用封裝之前的自己來構造。這樣子可以避免版本的差異性

3)HTTPClient對於超時是會自動發起3次自動重連。如果你的http請求是在線程裏發起的。那就需要注意一下這一部分的消耗了。可以手動在代碼裏把重發關閉。黃色部分。(老夫雖然用下面那代碼控制了自動重發,但是無奈,沒有把超時控制住,所以只能棄了這一版。)

        HttpPost httpPost = new HttpPost(Url);
		
        HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
			
			@Override
			public boolean retryRequest(IOException arg0, int arg1, HttpContext arg2) {
				// TODO Auto-generated method stub
				return false;
			}
		};
        int miSec =  60000;//超時時間
        RequestConfig requestConf = RequestConfig.custom().setConnectTimeout(miSec).setConnectionRequestTimeout(miSec)
        		.setSocketTimeout(miSec).build();
        
        
        
        
        CloseableHttpClient client = HttpClients.custom().setRetryHandler(myRetryHandler).build();
        StringEntity entity = new StringEntity(JSON.toJSONString(data), "utf-8");// 解決中文亂碼問題
        entity.setContentEncoding("UTF-8");
        //FIXME
        entity.setContentType(MediaType.APPLICATION_JSON_VALUE);
        entity.setContentEncoding("UTF-8");
        httpPost.setEntity(entity);
        httpPost.setConfig(requestConf);
        httpPost.addHeader("Accept", MediaType.APPLICATION_JSON.toString());
        HttpResponse resp = null;
        try {
            resp = client.execute(httpPost);
            result = JSON.parseObject(resp.getEntity().getContent(),JSONObject.class);
        } catch (Throwable e) {
            //e.printStackTrace();
        	log.info(e.getMessage());
        	return null;
        }finally {
             httpPost.releaseConnection();
            if(null != client){
	        try {
		      client.close();
	        } catch (IOException e) {
		     // TODO Auto-generated catch block
		     e.printStackTrace();
	        }
	}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章