關於HttpClient中連接池是如何保持長鏈接的

根據Apache HttpClient 4.5.6講解。

HttpClient使用如下方式創建HttpClient時,在build()中會默認給HttpClient設置一個連接池PoolingHttpClientConnectionManager

return HttpClientBuilder.create().build();

默認創建的HttpClient是默認保持連接的keepalive。

我們使用HttpClient請求第三方服務器時,如果請求的是同一個服務器的地址是可以提高訪問效率的。這是由於HTTP協議的底層是TCP,TCP建立連接的時候需要三次握手。如果使用連接池並keeplive是true,那麼就是一個長鏈接,在第一次握手成功後,後面的請求就都不需要握手了,直接請求數據,這樣就加快了訪問速度。但是,如果多次請求的不是同一個服務器,還是需要爲每次請求握手建立連接。如果項目中有對某一個平臺比如微信開放平臺有頻繁的調用,可以使用一個HttpClient去專門請求,這樣只會進行一次握手。

我們一般使用HttpClient後都會關閉,比如:

 CloseableHttpResponse response = this.execute(target, request, context);
....
finally {
            response.close();
        }

那有沒有想過這樣關閉會對我上面說的長鏈接有影響嗎?

這樣關閉是沒有影響的,它還是在連接池中。close()最後會調用下面這個方法。在finally中的close傳入的參數是false,也就是會釋放掉鏈接,但是它並不會調用成功。因爲在前面已經執行過一次releaseConnection方法了。在response放回處理返回的結果的時候會調用close方法。那裏的參數使用的是ConnectionHolder類的reusable成員變量。所以如果前面的程序執行成功最後的finally中的close方法到releaseConnection方法的CAS時會執行失敗。

private void releaseConnection(boolean reusable) {
        if (this.released.compareAndSet(false, true)) {
            HttpClientConnection var2 = this.managedConn;
            synchronized(this.managedConn) {
                if (reusable) {
                    this.manager.releaseConnection(this.managedConn, this.state, this.validDuration, this.tunit);
                } else {
                    try {
                        this.managedConn.close();
                        this.log.debug("Connection discarded");
                    } catch (IOException var9) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug(var9.getMessage(), var9);
                        }
                    } finally {
                        this.manager.releaseConnection(this.managedConn, (Object)null, 0L, TimeUnit.MILLISECONDS);
                    }
                }
            }
        }

    }

可以看到是通過reusable去判斷的。這個在CloseableHttpResponse構造的時候就已經設定了。

  if (reuseStrategy.keepAlive(response, context)) {
                    // Set the idle duration of this connection
                    final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
                    if (this.log.isDebugEnabled()) {
                        final String s;
                        if (duration > 0) {
                            s = "for " + duration + " " + TimeUnit.MILLISECONDS;
                        } else {
                            s = "indefinitely";
                        }
                        this.log.debug("Connection can be kept alive " + s);
                    }
                    connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
                    connHolder.markReusable();
                }

public void markReusable() {
        this.reusable = true;
    }

看這個大概就可以看出,就是通過keepAlive去判斷的。

回到前面的releaseConnection(),它沒有去關閉socket,而是將連接放入了一個鏈表供後面使用。然後再執行的時候去這個鏈表中獲取連接。這個鏈表是RouteSpecificPool類available成員變量。獲取連接使用的是getFree方法。

以上。

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