應用層與 HTTP

持久連線(persistent connection)

想像你打電話到一家店,問一個問題、掛斷,然後為了下一個問題再撥一次,接著為再下一個又撥一次。重撥所浪費的時間比問問題本身還多。早期的 HTTP 就是這樣運作的:頁面上的每一個檔案——HTML、每張圖、每支腳本——都開一條全新的 TCP 連線,用一次就關掉。持久連線(persistent connection)的解法是讓線路保持開著,並連續重複使用它來處理許多請求。

省下來的成本是 TCP 的建立。開一條 TCP 連線需要三向交握(three-way handshake:SYN、SYN-ACK、ACK),在任何資料能流動之前要先花掉一個完整的往返——而對一台遙遠的伺服器來說,那個往返可能是 100 毫秒。一個典型的網頁會拉進數十項資源。非持久的 HTTP 為每項資源都付一次那個交握(以及 TCP 慢啟動的爬升);持久的 HTTP 只付一次,之後便在同一條暖好的連線上一個接一個地送出請求。HTTP/1.1 把持久連線設為預設,以「Connection: keep-alive」為共識,伺服器會把通訊端開著,直到它閒置一段時間為止。

一個相關的概念是管線化(pipelining):把好幾個請求一個緊接一個地送出,不先等每個回應回來,藉此把管道填滿。實務上 HTTP/1.1 的管線化很少被採用,因為回應必須依請求的同樣順序回來——於是一個慢回應就會卡住排在它後面的一切(行頭阻塞,head-of-line blocking)。正是這個未解的限制,催生了 HTTP/2 的多工以及後來的 HTTP/3。所以持久連線消除了每次請求的交握,但在 HTTP/1.1 中,回應仍是串列化的。

一個頁面需要 HTML 加上 30 張圖,全都來自一台 100 毫秒外的伺服器。非持久:大約 31 次交握,每次都先付一個往返。持久:一次交握,之後 31 個請求全都在這條暖好的連線上流動——光是在任何東西開始下載之前,就省下大約 3 秒純粹的等待。

重複使用一條暖好的 TCP 連線,避免一次又一次付那個交握的往返。

HTTP/1.1 的持久連線並不允許回應彼此超車——在那一條連線上,它們仍依請求順序送回。要解決這點,得靠 HTTP/2 的多工;別把 keep-alive 誤當成真正的並行。

又稱
keep-aliveHTTP keep-alive持久連線持續連線