1. 程式人生 > >new DefaultHttpClient過時處理建議和HTTP呼叫後關閉流處理

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:總結:

  因為每次呼叫的不同,不及時關閉在大請求量下就需要謹慎設計程式碼的安全性了。