new DefaultHttpClient過時處理建議和HTTP呼叫後關閉流處理
最近寫新的呼叫程式碼時候,發現專案中new DefaultHttpClient()例項過期很久了,翻了翻另一個生產專案呼叫端的程式碼也是如此,於是查閱了些資料,用新版本程式碼替換了手上專案的程式碼並且正常測試完、生產上也正常執行完,算是一次補習,特記錄下替換過程和呼叫完後的處理。
1:來看下原來的呼叫程式碼,也是最常用的(httpclient版本超過4.2.6):(僅限於引用org.apache.httpcomponents的呼叫方式)
專案原先用的4.3.1,過了4.2.6就已經過時了,過時程式碼下面我會標註。
HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost("呼叫地址"); List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("引數佇列頭部", 呼叫引數)); System.out.println("==== 提交引數 ======" +formparams); UrlEncodedFormEntity uefEntity;try { uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8"); httppost.setEntity(uefEntity); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); if(entity!=null){ String results=EntityUtils.toString(entity, "UTF-8"); System.out.println("介面返回值="+results); } EntityUtils.consume(entity); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 關閉連線,釋放資源 httpclient.getConnectionManager().shutdown(); }
從4.2.6版本後,原作用類就出現了過時標識註明,看了maven倉更新時間2013-09-04是4.2.6和4.3同時釋出的。
2:下面看下DefaultHttpClient的原始碼:(追溯原始碼)
* @since 4.0 * * @deprecated (4.3) use {@link HttpClientBuilder}. */ @ThreadSafe @Deprecated public class DefaultHttpClient extends AbstractHttpClient { /** * Creates a new HTTP client from parameters and a connection manager. * * @param params the parameters * @param conman the connection manager */ public DefaultHttpClient( final ClientConnectionManager conman, final HttpParams params) { super(conman, params); }
看得出來為什麼過時了還能用,DefaultHttpClient 繼承了 AbstractHttpClient,AbstractHttpClient 繼承了CloseableHttpClient。
”Creates a new HTTP client from parameters and a connection manager“ ,建立一個HTTP管理連線的一個”動作“類。
”* @deprecated (4.3) use {@link HttpClientBuilder}.“ ,說明從4.3版本後使用httpclientBuilder新的類,類httpclientBuilder的頭部介紹:
”* Please note that some settings used by this class can be mutually exclusive and may not apply when building {@link CloseableHttpClient}“,翻譯過來是和CloseableHttpClient有互斥性,看到有hostname,ssl安全證書載入這些就知道是中後期才會執行到的,都是在外部封裝類執行提交的引數後內部執行的。
這是調式模式下,圖中可以看到引數會傳遞到httpClientBuilder中處理。
綜合資料和httpClientBuilder的作用寫下新的呼叫程式碼(HttpClientBuilder裡CloseableHttpClient的build方法):
private static final CloseableHttpClient httpClient; static { RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build(); httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); } public static void TestHttpPost(String url, String jsonData) { HttpPost httpPost = new HttpPost("呼叫地址"); // 拼接引數 List<NameValuePair> list = new ArrayList<NameValuePair>(); list.add(new BasicNameValuePair("引數佇列頭部", 呼叫引數)); System.out.println("==== 提交引數 ======" + list); CloseableHttpResponse response = null; try { httpPost.setEntity(new UrlEncodedFormEntity(list)); response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { httpPost.abort(); throw new RuntimeException("HttpClient,error status code :" + statusCode); } System.out.println("========HttpResponseProxy:========" + statusCode); HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity, "UTF-8"); System.out.println("========介面返回=======" + result); } EntityUtils.consume(entity); } catch (Exception e) { e.printStackTrace(); } finally { if (response != null) { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } if (httpClient != null) { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
看AbstractHttpClient 繼承了CloseableHttpClient的CloseableHttpClient:
@ThreadSafe //執行緒安全
public abstract class CloseableHttpClient implements HttpClient, Closeable
不難看出實現了httpclient,那麼呼叫方法有了,還實現了關閉流,說明呼叫完畢後會做關閉處理。CloseableHttpResponse也替換了原來的HttpResponse ,使用CloseableHttpClient的新版本程式碼:
CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(”呼叫地址“); //拼接引數 List<NameValuePair> list = new ArrayList<NameValuePair>(); list.add(new BasicNameValuePair("引數佇列頭部", 呼叫引數)); System.out.println("==== 提交引數 ======" +list); CloseableHttpResponse response = null; try { httpPost.setEntity(new UrlEncodedFormEntity(list)); response = httpClient.execute(httpPost); System.out.println("========HttpResponseProxy:========"+response.getStatusLine()); HttpEntity entity = response.getEntity(); if(entity != null){ String result = EntityUtils.toString(entity, "UTF-8"); System.out.println("========介面返回=======" +result); } EntityUtils.consume(entity); //httpClient.getConnectionManager().shutdown(); } catch (Exception e) { e.printStackTrace(); }finally { if(response != null){ try { response.close(); } catch (IOException e) { e.printStackTrace(); } } if(httpClient != null){ try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } }
3:舊版本呼叫完處理和新版本呼叫完處理:
先看舊版本的關閉方法,
httpclient.getConnectionManager().shutdown();
只要httpclient開啟後,BasicClientConnectionManager裡的託管連線connManager初始化,其中shutdown開啟 = false,執行完呼叫shutdown方法變 = true。
Response接收httpEntity返回引數時,EntityUtils.consume(entity);關閉引數流操作。
總結:連線動作關閉,接收引數流關閉。
public static void consume(final HttpEntity entity) throws IOException { if (entity == null) { return; } if (entity.isStreaming()) { final InputStream instream = entity.getContent(); if (instream != null) { instream.close(); } } }
新方法呼叫後關閉方法,
httpClient.close();
新方法是開啟了CloseableHttpClient後,PoolingHttpClientConnectionManager賦值CloseableHttpClient 物件並初始化,shutdown為開啟狀態。
httpClient.getConnectionManager().shutdown(); 和 httpClient.close(); 都是關閉呼叫功能,因為實現類都impl實現了Closeable關閉流操作,所以在client端呼叫哪個方法都是可以關閉的,只是有些方法被註明過時了,用新方法不用擔心出現@Deprecated標記。
4:總結:
因為每次呼叫的不同,不及時關閉在大請求量下就需要謹慎設計程式碼的安全性了。