什么是HTTP長連接? HTTP長連接,與一般每次發(fā)起http請求或響應(yīng)都要建立一個tcp連接不同,http長連接利用同一個tcp連接處理多個http請求和響應(yīng),也叫 HTTP keep-alive,或者http連接重用。使用http長連接可以提高http請求/響應(yīng)的性能。
使用http長連接有很多好處,包括:
更少的建立和關(guān)閉tcp連接,可以減少網(wǎng)絡(luò)流量。 因為已建立的tcp握手,減少后續(xù)請求的延時。 長時間的連接讓tcp有充足的時間判斷網(wǎng)絡(luò)的擁塞情況,方便做出下步操作。
這些優(yōu)點在使用https連接時更顯著。可以減少多次建立高消耗的SSL/TLS握手。 在HTTP/1.1中,默認使用的是長連接方式。客戶端默認服務(wù)端會保持長連接,即便返回錯誤響應(yīng);除非明確指示不使用長連接。同時,協(xié)議中也指定了客戶 端可以發(fā)送關(guān)閉信號到服務(wù)端來關(guān)閉TCP連接。
怎樣是連接可以重用? 因為TCP是基于流的協(xié)議,所以HTTP協(xié)議需要有一種方式來指示前一個響應(yīng)的結(jié)束和后一個響應(yīng)的開始來重用已建立的連接。所以,它要求連接中傳輸?shù)男畔?必須有自定義的消息長度。自定義消息長度可以通過設(shè)置 Content-Length 消息頭,若傳輸編碼的實體內(nèi)容塊,則每個數(shù)據(jù)塊的標(biāo)明數(shù)據(jù)塊的大小,而且響應(yīng)體也是以一個特殊的數(shù)據(jù)塊結(jié)束。
若中間存在代理服務(wù)器將會如何? 因為長連接僅占用一條傳輸鏈路,所以代理服務(wù)器能否正確得與客戶端和服務(wù)器端(或者其他代理服務(wù)器)發(fā)送長連接或非長連接的信號尤為重要。但是HTTP的 客戶端或服務(wù)器端來看,代理服務(wù)器對他們來說是透明的,即便長連接是需要關(guān)注的。
當(dāng)前的JDK如何處理Keep-Alive? JDK同時支持HTTP/1.1 和 HTTP/1.0。 當(dāng)應(yīng)用程序讀取完響應(yīng)體內(nèi)容后或者調(diào)用 close() 關(guān)閉了URLConnection.getInputStream()返回的流,JDK中的HTTP協(xié)議句柄將關(guān)閉連接,并將連接放到連接緩存中,以便后 面的HTTP請求使用。 對HTTP keep-Alive 的支持是透明的。但是,你也可以通過系統(tǒng)屬性http.keepAlive和http.maxConnections以及HTTP/1.1協(xié)議中的特定的 請求響應(yīng)頭來控制。控制Keep-Alive表現(xiàn)的系統(tǒng)屬性有:
http.keepAlive=<布爾值> 默認: true 指定長連接是否支持
http.maxConnections=<整數(shù)> 默認: 5 指定對同一個服務(wù)器保持的長連接的最大個數(shù)。
影響長連接的HTTP header是: Connection: close 如果請求或響應(yīng)中的Connection header被指定為close,表示在當(dāng)前請求或響應(yīng)完成后將關(guān)閉TCP連接。
JDK中的當(dāng)前實現(xiàn)不支持緩存響應(yīng)體,所以應(yīng)用程序必須讀取完響應(yīng)體內(nèi)容或者調(diào)用close()關(guān)閉流并丟棄未讀內(nèi)容來重用連接。此外,當(dāng)前實現(xiàn)在清理連接時并未使用阻塞讀,這就意味這如果響應(yīng)體不可用,連接將不能被重用。
JDK1.5中的新特性 當(dāng)應(yīng)用接收到400或500的HTTP響應(yīng)時,它將忽略IOException 而另發(fā)一個HTTP 請求。這種情況下,底層的TCP連接將不會再保持,因為響應(yīng)內(nèi)容還在等待被讀取,socket 連接未清理,不能被重用。應(yīng)用可以在捕獲IOException 以后調(diào)用HttpURLConnection.getErrorStream() ,讀取響應(yīng)內(nèi)容然后關(guān)閉流。但是現(xiàn)存的應(yīng)用沒有這么做,不能體現(xiàn)出長連接的優(yōu)勢。為了解決這個問題,介紹下workaround。
當(dāng)響應(yīng)體的狀態(tài)碼大于或等于400的時候,workaround 將在一定時間內(nèi)緩存一定數(shù)量的響應(yīng)內(nèi)容,釋放底層的socket連接來重用。基本原理是當(dāng)響應(yīng)狀態(tài)碼大于或等于400時,服務(wù)器端會發(fā)送一個簡短的響應(yīng)體來指明連接誰以及如何恢復(fù)連接。
下面介紹一些SUN實現(xiàn)中的特定屬性來幫助接收到錯誤響應(yīng)體后清理連接: 主要的一個是: sun.net.http.errorstream.enableBuffering=<布爾值> 默認: false
當(dāng)上面屬性設(shè)置為true后,在接收到響應(yīng)碼大于或等于400是,HTTP 句柄將嘗試緩存響應(yīng)內(nèi)容。釋放底層的socket連接來重用。所以,即便應(yīng)用不調(diào)用getErrorStream()來讀取響應(yīng)內(nèi)容,或者調(diào)用 close()關(guān)閉流,底層的socket連接也將保持連接狀態(tài)。
下面的兩個系統(tǒng)屬性是為了更進一步控制錯誤流的緩存行為: sun.net.http.errorstream.timeout=<int> in 毫秒 默認: 300 毫秒
你如何做可以保持連接為連接狀態(tài)呢? 不要忽略響應(yīng)體而丟棄連接。這樣會是TCP連接閑置,當(dāng)不再被引用后將會被垃圾回收器回收。 如果getInputStream()返回成功,讀取全部響應(yīng)內(nèi)容。如果拋出IOException ,捕獲異常并調(diào)用getErrorStream() 讀取響應(yīng)內(nèi)容(如果存在響應(yīng)內(nèi)容)。
即便你對響應(yīng)內(nèi)容不感興趣,也要讀取它,以便清理連接。但是,如果響應(yīng)內(nèi)容很長,你讀取到開始部分后就不感興趣了,可以調(diào)用close()來關(guān)閉流。值得注意的是,其他部分的數(shù)據(jù)已在讀取中,所以連接將不能被清理進而被重用。
下面是一個基于上面建議的代碼樣例:
try { URL a = new URL(args[0]); URLConnection urlc = a.openConnection(); is = conn.getInputStream(); int ret = 0; while ((ret = is.read(buf)) > 0) { processBuf(buf); } // close the inputstream is.close(); } catch (IOException e) { try { respCode = ((HttpURLConnection)conn).getResponseCode(); es = ((HttpURLConnection)conn).getErrorStream(); int ret = 0; // read the response body while ((ret = es.read(buf)) > 0) { processBuf(buf); } // close the errorstream es.close(); } catch (IOException ex) { // deal with the exception } } |
如果你預(yù)先就對響應(yīng)內(nèi)容不感興趣,你可以使用HEAD 請求來代替GET 請求。例如,獲取web資源的meta信息或者測試它的有效性,可訪問性以及最近的修改。下面是代碼片段:
URL a =
new
URL(args[0]);
URLConnection urlc = a.openConnection();
HttpURLConnection httpc = (HttpURLConnection)urlc;
// only interested in the length of the resource
httpc.setRequestMethod(
"HEAD"
);
int len = httpc.getContentLength();
顏色 | Class名稱 | short |
BLACK | 8 | |
BROWN | 60 | |
OLIVE_GREEN | 59 | |
DARK_GREEN | 58 | |
DARK_TEAL | 56 | |
DARK_BLUE | 18 | |
INDIGO | 62 | |
GREY_80_PERCENT | 63 | |
DARK_RED | 16 | |
ORANGE | 53 | |
DARK_YELLOW | 19 | |
GREEN | 17 | |
TEAL | 21 | |
BLUE | 12 | |
BLUE_GREY | 54 | |
GREY_50_PERCENT | 23 | |
RED | 10 | |
LIGHT_ORANGE | 52 | |
LIME | 50 | |
SEA_GREEN | 57 | |
AQUA | 49 | |
LIGHT_BLUE | 48 | |
VIOLET | 20 | |
GREY_40_PERCENT | 55 | |
PINK | 14 | |
GOLD | 51 | |
YELLOW | 13 | |
BRIGHT_GREEN | 11 | |
TURQUOISE | 15 | |
SKY_BLUE | 40 | |
PLUM | 61 | |
GREY_25_PERCENT | 22 | |
ROSE | 45 | |
TAN | 47 | |
LIGHT_YELLOW | 43 | |
LIGHT_GREEN | 42 | |
LIGHT_TURQUOISE | 41 | |
PALE_BLUE | 44 | |
LAVENDER | 46 | |
WHITE | 9 | |
CORNFLOWER_BLUE | 24 | |
LEMON_CHIFFON | 26 | |
MAROON | 25 | |
ORCHID | 28 | |
CORAL | 29 | |
ROYAL_BLUE | 30 | |
LIGHT_CORNFLOWER_BLUE | 31 | |
AUTOMATIC | 64 |