HTTP/0.9 和早期的 HTTP/1.0 協議對 HTTP 請求處理是串行化的。假如一個頁面包含 3 個樣式文件,同屬於一個協議、域名、端口。那麼,瀏覽器一共須要發起四次請求,而且每次只能打開一個 TCP 通道,在一個請求資源完成下載後,馬上斷開該鏈接,再開啓一個新的鏈接去處理隊列中的下一個請求。隨着頁面資源大小、數量的不斷擴增,網絡延遲時間會不斷堆積,用戶會面對滿屏空白,等待過長時間而失去耐心。瀏覽器
爲了提升網絡的吞吐能力,改進後的 HTTP 協議容許客戶端同時打開多個 TCP 鏈接,並行地請求多個資源,充分利用帶寬。一般,每個鏈接之間都會有必定延遲,但請求的傳輸時間是重疊的,整體上時延要比串行鏈接低不少。考慮到每個鏈接都會消耗系統資源,而且服務器須要處理海量的用戶併發請求,瀏覽器會對併發請求數量作必定的限制。即便 RFC 並無規定具體的限制數量,各瀏覽器廠商也都會有本身的標準:緩存
早期的 HTTP 協議對每一個請求都佔用一個獨立的 TCP 鏈接,這無疑增長了 TCP 的創建鏈接開銷、擁塞控制開銷、釋放鏈接開銷,改進後的 HTTP/1.0 和 HTTP/1.1(默認)都支持了持久鏈接。若是一個請求完成後,不會馬上斷開鏈接,而是在必定的時間內保持鏈接,以便快速處理即將到來的 HTTP 請求,複用同一個 TCP 通道,直到客戶端心跳檢測失敗或服務器鏈接超時。這個特性能夠經過 HTTP 首部 Connection: keep-alive
來激活,客戶端也能夠發送 Connection: close
來主動關閉鏈接。因此,咱們看到,並行鏈接和持久鏈接這兩種優化是相輔相成的,並行鏈接使得首次加載頁面能夠同時打開多個 TCP 鏈接,而持久鏈接保證了後續的請求複用已打開的 TCP 鏈接,這也是現代 Web 頁面的廣泛機制。服務器
持久鏈接讓咱們能夠重用鏈接來完成屢次請求,但它必須知足 FIFO 的隊列順序,必須保證前一個請求成功到達服務器、處理成功而且收到服務器返回的首個字節,才能夠發起隊列中下一個請求。HTTP 管道容許客戶端在同一個 TCP 通道內連續發起多個請求,而沒必要等待響應,消除了往返延遲時間差。但現實狀況因爲 HTTP/1.x 協議的限制,不容許數據在一個鏈路上交錯到達(IO 多路複用)。設想一種狀況,客戶端服務器端同時發送一個 HTML 和多個 CSS 請求,服務器並行處理全部請求,當全部的 CSS 請求處理完成並加入到緩衝隊列,卻發現 HTML 請求處理遇到問題而無限被掛起,嚴重時甚至形成緩衝區溢出,這種狀況就叫作隊首阻塞。所以,這個方案在 HTTP/1.x 協議中並無被採納。網絡
隊首阻塞並非 HTTP 中獨有的概念,而是在緩存式通訊網絡交換中的一種廣泛現象併發