阿里一面:TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是一個東西嗎?

語言: CN / TW / HK

之前有個朋友問我一個問題

大致問題是,TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是一個東西嗎?

這是個好問題,應該有不少人都會搞混,因為這兩個東西看上去太像了,很容易誤以為是同一個東西。

事實上,這兩個完全是兩樣不同東西,實現的層面也不同:

  • HTTP 的 Keep-Alive,是由應用層(使用者態)  實現的,稱為 HTTP 長連線;
  • TCP 的 Keepalive,是由 TCP 層(核心態)  實現的,稱為 TCP 保活機制;

接下來,分別說說它們。

HTTP 的 Keep-Alive

HTTP 協議採用的是「請求-應答」的模式,也就是客戶端發起了請求,服務端才會返回響應,一來一回這樣子。

請求-應答

由於 HTTP 是基於 TCP 傳輸協議實現的,客戶端與服務端要進行 HTTP 通訊前,需要先建立 TCP 連線,然後客戶端傳送 HTTP 請求,服務端收到後就返回響應,至此「請求-應答」的模式就完成了,隨後就會釋放 TCP 連線。

一個 HTTP 請求

如果每次請求都要經歷這樣的過程:建立 TCP -> 請求資源 -> 響應資源 -> 釋放連線,那麼此方式就是 HTTP 短連線,如下圖:

HTTP 短連線

這樣實在太累人了,一次連線只能請求一次資源。

能不能在第一個 HTTP 請求完後,先不斷開 TCP 連線,讓後續的 HTTP 請求繼續使用此連線?

當然可以,HTTP 的 Keep-Alive 就是實現了這個功能,可以使用同一個 TCP 連線來發送和接收多個 HTTP 請求/應答,避免了連線建立和釋放的開銷,這個方法稱為 HTTP 長連線

HTTP 長連線

HTTP 長連線的特點是,只要任意一端沒有明確提出斷開連線,則保持 TCP 連線狀態。

怎麼才能使用 HTTP 的 Keep-Alive 功能?

在 HTTP 1.0 中預設是關閉的,如果瀏覽器要開啟 Keep-Alive,它必須在請求的包頭中新增:

Connection: Keep-Alive

然後當伺服器收到請求,作出迴應的時候,它也新增一個頭在響應中:

Connection: Keep-Alive

這樣做,連線就不會中斷,而是保持連線。當客戶端傳送另一個請求時,它會使用同一個連線。這一直繼續到客戶端或伺服器端提出斷開連線。

從 HTTP 1.1 開始, 就預設是開啟了 Keep-Alive,如果要關閉 Keep-Alive,需要在 HTTP 請求的包頭裡新增:

Connection:close

現在大多數瀏覽器都預設是使用 HTTP/1.1,所以 Keep-Alive 都是預設開啟的。一旦客戶端和服務端達成協議,那麼長連線就建立好了。

HTTP 長連線不僅僅減少了 TCP 連線資源的開銷,而且這給 HTTP 流水線技術提供了可實現的基礎。

所謂的 HTTP 流水線,是客戶端可以先一次性發送多個請求,而在傳送過程中不需先等待伺服器的迴應,可以減少整體的響應時間。

舉例來說,客戶端需要請求兩個資源。以前的做法是,在同一個 TCP 連線裡面,先發送 A 請求,然後等待伺服器做出迴應,收到後再發出 B 請求。HTTP 流水線機制則允許客戶端同時發出 A 請求和 B 請求。

右邊為 HTTP 流水線機制

但是伺服器還是按照順序響應,先回應 A 請求,完成後再回應 B 請求。

而且要等伺服器響應完客戶端第一批發送的請求後,客戶端才能發出下一批的請求,也就說如果伺服器響應的過程發生了阻塞,那麼客戶端就無法發出下一批的請求,此時就造成了「隊頭阻塞」的問題。

可能有的同學會問,如果使用了 HTTP 長連線,如果客戶端完成一個 HTTP 請求後,就不再發起新的請求,此時這個 TCP 連線一直佔用著不是挺浪費資源的嗎?

對沒錯,所以為了避免資源浪費的情況,web 服務軟體一般都會提供keepalive_timeout 引數,用來指定 HTTP 長連線的超時時間。

比如設定了 HTTP 長連線的超時時間是 60 秒,web 服務軟體就會啟動一個定時器,如果客戶端在完後一個 HTTP 請求後,在 60 秒內都沒有再發起新的請求,定時器的時間一到,就會觸發回撥函式來釋放該連線。

HTTP 長連線超時

TCP 的 Keepalive

TCP 的 Keepalive 這東西其實就是 TCP 的保活機制,它的工作原理我之前的文章寫過,這裡就直接貼下以前的內容。

如果兩端的 TCP 連線一直沒有資料互動,達到了觸發 TCP 保活機制的條件,那麼核心裡的 TCP 協議棧就會發送探測報文。

  • 如果對端程式是正常工作的。當 TCP 保活的探測報文傳送給對端, 對端會正常響應,這樣 TCP 保活時間會被重置,等待下一個 TCP 保活時間的到來。
  • 如果對端主機崩潰,或對端由於其他原因導致報文不可達。當 TCP 保活的探測報文傳送給對端後,石沉大海,沒有響應,連續幾次,達到保活探測次數後,TCP 會報告該 TCP 連線已經死亡

所以,TCP 保活機制可以在雙方沒有資料互動的情況,通過探測報文,來確定對方的 TCP 連線是否存活,這個工作是在核心完成的。

TCP 保活機制

注意,應用程式若想使用 TCP 保活機制需要通過 socket 介面設定SO_KEEPALIVE 選項才能夠生效,如果沒有設定,那麼就無法使用 TCP 保活機制。

總結

HTTP 的 Keep-Alive 也叫 HTTP 長連線,該功能是由「應用程式」實現的,可以使得用同一個 TCP 連線來發送和接收多個 HTTP 請求/應答,減少了 HTTP 短連線帶來的多次 TCP 連線建立和釋放的開銷。

TCP 的 Keepalive 也叫 TCP 保活機制,該功能是由「核心」實現的,當客戶端和服務端長達一定時間沒有進行資料互動時,核心為了確保該連線是否還有效,就會發送探測報文,來檢測對方是否還線上,然後來決定是否要關閉該連線。