httpclient連接池使用及簡(jiǎn)單分析

httpclient的連接池

為什么要使用httpclient連接池

連接池是為了復(fù)用連接而存在的,就像線程池一樣,創(chuàng)建了的線程在執(zhí)行完成任務(wù)后不銷毀,而是放入池中待命,以便執(zhí)行下次任務(wù)的時(shí)候可以直接從池中取出線程執(zhí)行,而不是先創(chuàng)建線程再執(zhí)行,省去了創(chuàng)建線程帶來(lái)的開(kāi)銷和時(shí)間。http連接也是一樣的思路,http1.1支持了keep-alive,我們?cè)賹?duì)同一個(gè)網(wǎng)址進(jìn)行請(qǐng)求的時(shí)候,就可以不用每次都建立連接;
我們都知道,http建立連接的過(guò)程是比較繁瑣的,要經(jīng)歷3次握手和4次揮手,那么省去這個(gè)建立連接的過(guò)程在高并發(fā)的時(shí)候就會(huì)比較的有必要;同時(shí),類似線程池,總有一個(gè)最大的線程數(shù),和線程失效時(shí)間,因?yàn)樵谌蝿?wù)空閑的時(shí)候,這些空閑線程占用系統(tǒng)資源,所以我們要釋放空閑時(shí)間長(zhǎng)的線程。同樣對(duì)于http連接,使用的是tcp的長(zhǎng)連接,但是長(zhǎng)連接的持有是非常耗資源的,特別是對(duì)于服務(wù)端,鏈接數(shù)是有限的,所以我們同樣需要釋放一定時(shí)間空閑的連接;

什么時(shí)候使用httpclient連接池

對(duì)自己的系統(tǒng)有正確的預(yù)估:

  1. 調(diào)用的請(qǐng)求是否對(duì)同一個(gè)host大量請(qǐng)求;因?yàn)橹挥袑?duì)于同一個(gè)host,長(zhǎng)連接才可能建立,如果每次請(qǐng)求一會(huì)一個(gè)http://a.com 一會(huì)一個(gè) http://b.com 這樣是起不到連接池的效果的,并且http/https也是不能共用的,因?yàn)樗麄兊亩丝诓煌?/li>
  2. 是否請(qǐng)求會(huì)達(dá)到一定的量級(jí);如果請(qǐng)求的數(shù)量不高,連接池體現(xiàn)不出多大的效果;如果請(qǐng)求達(dá)到一定的量級(jí),可能成為系統(tǒng)瓶頸或有較大影響的時(shí)候,可以嘗試使用;
  3. 對(duì)請(qǐng)求的系統(tǒng)有一定的了解;長(zhǎng)連接是雙向的,客戶端維護(hù)連接,服務(wù)端同樣需要維護(hù)連接,并且服務(wù)端服務(wù)的可能不止一個(gè)客戶端,就怕到時(shí)候這邊使用的連接池把服務(wù)端的連接占滿了,導(dǎo)致服務(wù)端無(wú)法為其他的客戶端提供服務(wù),這個(gè)鍋可能會(huì)扣在自己的頭上。

如何使用httpclient

httpclient給我們提供了PoolingHttpClientConnectionManager這個(gè)類幫助我們來(lái)管理連接(版本4.5及以上)。在我們使用httpclient的時(shí)候,如果配置了這個(gè)連接管理,那么就會(huì)通過(guò)這個(gè)來(lái)按host管理連接;
還是一樣,最簡(jiǎn)單的用法先來(lái)一個(gè)使用單例的:

public final class HttpClientUtils {

    private static CloseableHttpClient client;

    public static CloseableHttpClient getHttpClient() {

        if(client == null) {
            synchronized(HttpClientUtils.class) {
                if(client == null) {
                    // 先設(shè)置http連接的一些配置
                    requestConfig = RequestConfig.custom()
                                    // 從連接池獲取連接的超時(shí)時(shí)間
                                    .setConnectionRequestTimeout(3000)
                                    // 建立連接的超時(shí)時(shí)間
                                    .setConnectTimeout(3000)
                                    // 請(qǐng)求的超時(shí)時(shí)間
                                    .setSocketTimeout(3000).build();

                    // 有的地方會(huì)配置一堆的https ssl的策略,點(diǎn)進(jìn)這個(gè)構(gòu)造函數(shù)他默認(rèn)已經(jīng)配置了,所以不用再配;
                    PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
                    // 配置最大的連接數(shù)
                    manager.setMaxTotal(300);
                    // 每個(gè)路由最大連接數(shù),路由是根據(jù)host來(lái)管理的,所以這里的數(shù)量不太容易把握;
                    manager.setDefaultMaxPerRoute(20);
                    client = HttpClients.custom().
                            setConnectionManager(manager).
                            setDefaultRequestConfig(requestConfig).
                            build();
                }
            }
        }
        return client;
    }

}

這樣每次要使用http請(qǐng)求的時(shí)候,從這個(gè)工具中獲取客戶端來(lái)使用

HttpClientUtils.getHttpClient(); 

為什么要使用單例呢?其實(shí)直接HttpClients.custom().setDefaultRequestConfig(requestConfig).build();同樣也是使用了連接池的,可以看build中的源碼,沒(méi)有設(shè)置manager的時(shí)候默認(rèn)有一個(gè)。既然要管理連接池,那么這個(gè)管理器就只能有一個(gè),不然管理就亂了,所以我們?cè)谑褂玫臅r(shí)候,只需要要一個(gè)CloseableHttpClient,這個(gè)client配置一個(gè)連接池的管理,每次使用都去找他才能達(dá)到連接管理的目的,否則這樣寫(xiě):

public static CloseableHttpClient getHttpClient() {
    return HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
}

每次使用的時(shí)候都新建一個(gè)客戶端,每個(gè)客戶端是獨(dú)立的,這樣的話每次使用都完全是從頭來(lái)一次。就好像使用線程池的時(shí)候,每次都是一個(gè)新的ExecutorService。

我見(jiàn)過(guò)的連接池配置人家寫(xiě)了一堆這里就這?

的確我見(jiàn)過(guò)人家配置了一堆的東西:

  1. https/http協(xié)議策略,這個(gè)在PoolingHttpClientConnectionManager的空參數(shù)構(gòu)造函數(shù)中就有;除非自己需要一些高級(jí)的自定義,否則不用重復(fù)添加;
  2. HttpRequestRetryHandler配置的重試策略,對(duì)于個(gè)人來(lái)說(shuō)像這樣的請(qǐng)求不太喜歡重試,失敗自己處理比較好;
  3. 有一個(gè)定時(shí)任務(wù)的線程定時(shí)檢測(cè)超過(guò)多長(zhǎng)時(shí)間空閑的連接并銷毀;

對(duì)于第三點(diǎn),當(dāng)我看到人家自己實(shí)現(xiàn)的定時(shí)檢測(cè)任務(wù)的時(shí)候,我就在想,一個(gè)成熟的框架,人家不知至于想不到這點(diǎn),那么就去看看它到底有沒(méi)有做這件事。果然,框架的確考慮到了,但是這個(gè)默認(rèn)沒(méi)有開(kāi)啟,先看源碼:在類 HttpClientBuilder

if (this.evictExpiredConnections || this.evictIdleConnections) {
    final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((HttpClientConnectionManager)connManagerCopy, this.maxIdleTime > 0L ? this.maxIdleTime : 10L, this.maxIdleTimeUnit != null ? this.maxIdleTimeUnit : TimeUnit.SECONDS, this.maxIdleTime, this.maxIdleTimeUnit);
    closeablesCopy.add(new Closeable() {
        public void close() throws IOException {
            connectionEvictor.shutdown();

            try {
                connectionEvictor.awaitTermination(1L, TimeUnit.SECONDS);
            } catch (InterruptedException var2) {
                Thread.currentThread().interrupt();
            }

        }
    });
    connectionEvictor.start();
}

可以看到,在evictExpiredConnections 或者 evictIdleConnections其中一個(gè)屬性是true的時(shí)候,就會(huì)開(kāi)啟定時(shí)檢測(cè)關(guān)閉連接的任務(wù),那么我們就可以使用這樣的方式來(lái)開(kāi)啟它:

client = HttpClients.custom().
            setConnectionManager(manager).
            setDefaultRequestConfig(requestConfig).
            // 簡(jiǎn)單開(kāi)啟
            evictExpiredConnections().
            // 如果還要自定義超時(shí)時(shí)間(可以看到它默認(rèn)的是10s)
            evictIdleConnections(30L, TimeUnit.SECONDS).
            build();

關(guān)于這些東西的使用,官方文檔也有描述,但是描述的時(shí)候,他不會(huì)和你解釋為什么,所以有的時(shí)候,看看源碼就能理解它為什么要這樣做以及他是如何做到這些的,httpclient的配置可不止這些,如果使用的話可以先看看它有沒(méi)有幫我們實(shí)現(xiàn),沒(méi)有的話再去自己做;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第二章 連接管理 HttpClient有一個(gè)對(duì)連接初始化和終止,還有在活動(dòng)連接上I/O操作的完整控制。而連接操作的...
    狂奔的蝸牛_wxc閱讀 1,399評(píng)論 0 0
  • 2.1 連接持久性 建立從一個(gè)主機(jī)到另一個(gè)主機(jī)的連接的過(guò)程非常復(fù)雜,并且涉及兩個(gè)端點(diǎn)之間的多個(gè)分組交換,這可能會(huì)非...
    changhr2013閱讀 877評(píng)論 0 1
  • 原文鏈接 第2章:連接管理 2.1. 連接持久性 建立從一個(gè)主機(jī)到另一個(gè)主機(jī)的連接的過(guò)程非常復(fù)雜,并且涉及兩個(gè)端點(diǎn)...
    王德培閱讀 1,119評(píng)論 0 1
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,663評(píng)論 1 32
  • 繁星點(diǎn)點(diǎn)的夜晚,教學(xué)樓A棟前。 盤(pán)腿而坐的丁可愛(ài)與我正在暢聊中,兩只豬蹄糯嘰嘰地向我們走來(lái),溫柔地問(wèn)了一句“不好意...
    樓上何姥姥閱讀 320評(píng)論 0 1

友情鏈接更多精彩內(nèi)容