字節一面:HTTP 長連接和TCP 長連接有區別?

2022.12.04

字節一面:HTTP 長連接和TCP 長連接有區別?

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

大家好,我是小林。

之前有位讀者私信我,他在字節面試時,被問到這兩個問題:

圖片

第一個問題:MySQL 的NULL 值是怎麼存放的?

第二個問題:HTTP 長連接和TCP 長連接有什麼區別?

第一個問題,主要是考核你是否清楚MySQL 一條記錄是怎麼存儲的,我在前幾天已經寫了一篇文章講解了,還沒看過的同學,可以去看這篇:字節一面:MySQL 的NULL 值是怎麼存放的?

第二問題,其實是在問HTTP 的Keep-Alive 和TCP 的Keepalive 有什麼區別?

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

如果認真讀過我網站上圖解網絡系列文章的同學,應該這個問題你們都會,因為我之前就寫過。

不過,應該也有不少同學,看過後忘記了,這次就帶大家重新復習一波。

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

  • 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
  • 1.

然後當服務器收到請求,作出回應的時候,它也添加一個頭在響應中:

Connection: Keep-Alive
  • 1.

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

從HTTP 1.1 開始, 就默認是開啟了Keep-Alive,如果要關閉Keep-Alive,需要在HTTP 請求的包頭里添加:

Connection:close
  • 1.

現在大多數瀏覽器都默認是使用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 保活機制,該功能是由「內核」實現的,當客戶端和服務端長達一定時間沒有進行數據交互時,內核為了確保該連接是否還有效,就會發送探測報文,來檢測對方是否還在線,然後來決定是否要關閉該連接。