Apache commons 系列的HttpClient 相信大家都用過,選擇它而非JDK 的java.net.HttpURLConnection ,是為了使用HttpClient 封裝的幾個(gè)實(shí)用的功能。 目前使用最多的版本還是httpclient-3.x ,在官網(wǎng)http://hc./httpclient-3.x/tutorial.html 有這么一段示例代碼:
大部分人也是以這個(gè)為規(guī)范來使用的,但是注意有一段關(guān)于“Release the Connection”的說明:
我看得也不是很明白,意思是我們必須在使用后調(diào)用 來告訴HttpClient 這個(gè)連接可以重用了。這個(gè)在串行的處理中或許很有用,但是我所遇到的情況是多線程并發(fā)下,不能共享同一個(gè)HttpClient 實(shí)例,按照官方示例寫好代碼后,程序跑起來似乎沒什么問題,但是隨著時(shí)間的累計(jì),有一天突然發(fā)現(xiàn)這個(gè)模塊不工作了,查看了一下當(dāng)前的網(wǎng)絡(luò)連接,這個(gè)java 程序同一個(gè)地址保持著200多個(gè)CLOSE_WAIT 的連接,好吧,連接沒有釋放。 為什么沒有釋放?查看doc,有這樣的說明:
注意最后一句,如果該連接可以重用則不關(guān)閉,是“可以重用”,當(dāng)然可以重用了,就在那兒等著我去重用,可是我都是新建的實(shí)例,怎么重用 查看源碼,找到HttpClient 的構(gòu)造方法,有一個(gè)可以指定HttpConnectionManager ,然后這個(gè)HttpConnectionManager 又有一個(gè)實(shí)現(xiàn)的構(gòu)造:
顯然alawaysClose 的默認(rèn)值是false ,在釋放后連接并不總是會(huì)關(guān)閉。 所以,必須
Java代碼
大部分人使用HttpClient都是使用類似上面的事例代碼,包括Apache官方的例子也是如此。最近我在使用HttpClient是發(fā)現(xiàn)一次循環(huán)發(fā)送大量請(qǐng)求到服務(wù)器會(huì)導(dǎo)致APACHE服務(wù)器的鏈接被占滿,后續(xù)的請(qǐng)求便排隊(duì)等待。 我服務(wù)器端APACHE的配置 Java代碼
因此這樣的配置就會(huì)導(dǎo)致每個(gè)鏈接至少要過180S才會(huì)被釋放,這樣在大量請(qǐng)求訪問時(shí)就必然會(huì)造成鏈接被占滿,請(qǐng)求等待的情況。 在通過DEBUH后發(fā)現(xiàn)HttpClient在method.releaseConnection()后并沒有把鏈接關(guān)閉,這個(gè)方法只是將鏈接返回給connection manager。如果使用HttpClient client = new HttpClient()實(shí)例化一個(gè)HttpClient connection manager默認(rèn)實(shí)現(xiàn)是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有個(gè)構(gòu)造函數(shù)如下 Java代碼
看方法注釋我們就可以看到如果alwaysClose設(shè)為true在鏈接釋放之后connection manager 就會(huì)關(guān)閉鏈。在我們HttpClient client = new HttpClient()這樣實(shí)例化一個(gè)client時(shí)connection manager是這樣被實(shí)例化的 Java代碼
因此alwaysClose默認(rèn)是false,connection是不會(huì)被主動(dòng)關(guān)閉的,因此我們就有了一個(gè)客戶端關(guān)閉鏈接的方法。 方法一: 把事例代碼中的第一行實(shí)例化代碼改為如下即可,在method.releaseConnection();之后connection manager會(huì)關(guān)閉connection 。 Java代碼
方法二: 實(shí)例化代碼使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 Java代碼
shutdown源代碼很簡(jiǎn)單,看了一目了然 Java代碼
方法三: 實(shí)例化代碼使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 client.getHttpConnectionManager().closeIdleConnections(0);此方法源碼代碼如下: Java代碼
將idleTimeout設(shè)為0可以確保鏈接被關(guān)閉。 以上這三種方法都是有客戶端主動(dòng)關(guān)閉TCP鏈接的方法。下面再介紹由服務(wù)器端自動(dòng)關(guān)閉鏈接的方法。 方法四: 代碼實(shí)現(xiàn)很簡(jiǎn)單,所有代碼就和最上面的事例代碼一樣。只需要在HttpMethod method = new GetMethod("http://www.");加上一行HTTP頭的設(shè)置即可 Java代碼
看一下HTTP協(xié)議中關(guān)于這個(gè)屬性的定義: HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, Connection: close 現(xiàn)在再說一下客戶端關(guān)閉鏈接和服務(wù)器端關(guān)閉鏈接的區(qū)別。如果采用客戶端關(guān)閉鏈接的方法,在客戶端的機(jī)器上使用netstat –an命令會(huì)看到很多TIME_WAIT的TCP鏈接。如果服務(wù)器端主動(dòng)關(guān)閉鏈接這中情況就出現(xiàn)在服務(wù)器端。 參考WIKI上的說明http://wiki./HttpComponents/FrequentlyAskedConnectionManagementQuestions The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes. TIME_WAIT的狀態(tài)會(huì)出現(xiàn)在主動(dòng)關(guān)閉鏈接的這一端。TCP協(xié)議中TIME_WAIT狀態(tài)主要是為了保證數(shù)據(jù)的完整傳輸。具體可以參考此文檔: http://www.softlab./facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7 另外強(qiáng)調(diào)一下使用上面這些方法關(guān)閉鏈接是在我們的應(yīng)用中明確知道不需要重用鏈接時(shí)可以主動(dòng)關(guān)閉鏈接來釋放資源。如果你的應(yīng)用是需要重用鏈接的話就沒必要這么做,使用原有的鏈接還可以提供性能。 |
|