java處理http請求之Apache httpClient入門教程


說明

本文示例代碼基於 4.5.13 版本

轉載請注明出處:https://www.cnblogs.com/qnlcy/p/15378446.html

一、項目介紹

Apache 提供用來做http請求的項目有兩個,3.x 版本的項目叫做 The Commons HttpClient

它一開始是 Apache Jakarta Common 下的子項目,后來獨立出去了,現在這個項目已經結束了它的生命周期,不再開發和維護。

取而代之的是 4.x 版本的 Apache Httpcomponents 項目,它包括 HttpClientHttpCore 兩大模塊,能提供更好的性能和更大的靈活性。

二、項目模塊

Apache Httpcomponents 項目包括 HttpClientHttpCore 兩大模塊,其中,HttpCore 是一套HTTP協議實現包。而 HttpClient 是基於HttpCore的一套客戶端。

三、使用方式

使用 Httpclient 需要經過如下步驟

  1. 創建 HttpClient
  2. 創建 http 請求,如 HttpGetHttpPost
  3. 添加請求參數
  4. 添加請求設置,如超時等
  5. 使用 HttpClient 執行 http 請求
  6. 讀取返回內容並釋放連接

3.1 創建 HttpClient

3.1.1 創建默認客戶端:

    CloseableHttpClient httpclient = HttpClients.createDefault();

一些重要的默認配置:

  • 默認連接池大小10,每域名最大連接5
  • 連接池中連接存活時間 connTimeToLive = -1 ,默認單位為毫秒,默認連接不失效
  • 域名驗證器為 DefaultHostnameVerifier , 會驗證域名
  • SSL 上下文為 SSLContext.getInstance("TLS"),沒有使用密鑰管理器(KeyManager)和信任管理器(TrustManager)

3.1.2 自定義客戶端

  • 失敗不重試
    CloseableHttpClient client = HttpClients.custom().setRetryHandler((e, i, c) -> false).build();
  • 自定義連接池
 //設置自定義連接池
    @Test
    public void customConnectionPool() throws Exception {
        //1.創建 https 需要的 SslContext 相關內容
        //1.1 創建 SslContext
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream("證書文件"), "密碼".toCharArray());
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(TrustAllStrategy.INSTANCE)
                .loadKeyMaterial(ks, "證書密碼".toCharArray()).build();
        //1.2 創建 SSLConnectionSocketFactory
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv3", "TLSv1.1", "TLSv1.2"}, null,
                NoopHostnameVerifier.INSTANCE);

        //2.創建連接池
        //2.1 構建協議 registry
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory)
                .build();
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
        //3.連接池針對所有連接、每域名的連接的數量設置
        poolingHttpClientConnectionManager.setMaxTotal(100);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);

        //4.創建client
        CloseableHttpClient client =
        HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).build();
    }

3.2 創建 Http 請求

創建 HttpGetHttpPost 請求

    @Test
    public void getAndPost(){
        //1.創建get請求
        HttpGet get = new HttpGet("https://www.baidu.com");

        //2.創建post請求
        HttpPost post = new HttpPost("https://www.baidu.com");

        //3.其他如 HttpPut、HttpOptions、HttpTrace、HttpDelete、HttpPatch
    }

3.3 添加請求參數

    @Test
    public void addParams() throws IOException {
        HttpPost post = new HttpPost("https://www.baidu.com");
        //1.底層流,基礎參數
        BasicHttpEntity basicHttpEntity = new BasicHttpEntity();
        //1.1添加參數內容
        InputStream bis = new ByteArrayInputStream("參數".getBytes());
        basicHttpEntity.setContent(bis);
        //1.2設置內容長度
        basicHttpEntity.setContentLength(bis.available());
        //1.3取消分塊發送
        basicHttpEntity.setChunked(false);
        post.setEntity(basicHttpEntity);

        //2.字節碼類型參數
        HttpEntity entity = new ByteArrayEntity("name=zhangsan&age=100".getBytes());
        post.setEntity(entity);

        //3.字符串類型參數
        entity = new StringEntity("name=zhangsan&age=100");
        post.setEntity(entity);

        //4.流式參數,用法與BasicHttpEntity類似,內容和長度嚴格匹配
        entity = new InputStreamEntity(bis,bis.available());
        post.setEntity(entity);

        //5.文件類型參數
        entity = new FileEntity(new File("上傳文件"));
        post.setEntity(entity);

        //6.添加請求頭
        post.addHeader("Content-Type","text/html;charset=UTF-8");

        Header contentType = new BasicHeader("Content-Type","text/html;charset=UTF-8");
        post.addHeader(contentType);

        Header host = new BasicHeader("Host","www.baidu.com");
        post.setHeaders(new Header[]{contentType,host});

    }

3.4 添加請求設置

    @Test
    public void requestConfig(){
        //1.配置RequestConfig
        RequestConfig requestConfig = RequestConfig.custom()
        .setConnectionRequestTimeout(10000) //從連接池獲取可用連接的超時時間,單位毫秒
        .setSocketTimeout(5000) //請求獲取數據的超時時間
        .setConnectTimeout(4000) //連接超時時間
        .build();
        HttpPost post = new HttpPost("https://www.baidu.com");
        //2.設置到post請求當中
        post.setConfig(requestConfig);

        //也可以當作默認值,設置到client當中,此client都會按這個超時處理
        CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
    }

3.4.1 超時時間說明

超時類型 說明
connectionTimeout 連接建立時間,即3次握手時間,默認值-1
socketTimeout 連接后,數據傳輸過程中數據包之間間隔的最大時間,默認值-1
connectionRequestTimeout 從連接池獲取連接的超時時間,默認值-1

去哪里吃魚 https://www.cnblogs.com/qnlcy/

注意:

socketTimeoutconnectionRequestTimeout 如果不設置,請求會阻塞。

但是 connectionTimeout 的情況有所不同,它依賴於各平台的 socket 超時時間設置。

windows 10 實測為 20s, linux 平台則不定,它會按 /proc/sys/net/ipv4/tcp_syn_retries 中配置的次數重試,一般為3s\7s\15s\31s\63s遞增

另外,即使 java 程序返回了超時結果,但是linux服務器依舊在執行重試直到服務器端超時,為了提高資源利用率,可以手動關閉

關於 linux socket 超時的問題,請參閱 無毀的湖光-Al從linux源碼看socket(tcp)的timeout

3.5 執行 http 請求

執行 http 請求比較簡單,直接調用 execute() 方法即可

    @Test
    public void execute(){
        CloseableHttpClient client = HttpClients.createDefault();
        try {
            client.execute(new HttpPost("https://www.baidu.com"));
            client.execute(new HttpGet("https://www.baidu.com"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.6 讀取返回內容並釋放連接

服務器返回結果被封裝到 HttpResponse 對象里,我們可以從這里拿到我們想要的返回結果

    @Test
    public void getResponse() {
        CloseableHttpClient client = HttpClients.createDefault();
        CloseableHttpResponse httpResponse = null;
        final HttpGet httpGet = new HttpGet("https://www.baidu.com");
        try {
            httpResponse = client.execute(httpGet);
            //1.獲取返回狀態
            System.out.println(httpResponse.getStatusLine().getStatusCode());
            //2.獲取返回頭信息
            Header[] headers = httpResponse.getAllHeaders();
            for (Header header : headers) {
                System.out.println(header.getName() + ":" + header.getValue());
            }
            //3.獲取返回消息體
            HttpEntity entity = httpResponse.getEntity();
            if(null != entity){
                //3.1 得到返回結果並關閉流,與下面的只能執行一個,因為流只能讀取一次
                String content = EntityUtils.toString(entity);
                System.out.println(content);
                //3.2 得到返回結果並關閉流,與上面的只能執行一個
//              byte[] contents = EntityUtils.toByteArray(entity);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != httpResponse) {
                //4.歸還連接到連接池
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //如果復用 httpGet ,則重置其狀態使其可以重復使用
            httpGet.releaseConnection();
        }

        //只在應用關閉的時候關閉client
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM