「開位」你所應該知道的HTTP——優化篇

前言

這是本系列的終章,主要講解有關HTTP在實踐中的優化方案。採用遇到問題、分析問題、解決問題的思路進行敘述。
無特別說明均基於HTTP/1.x(簡稱H1)進行討論,後面也會提到HTTP/2.0(簡稱H2)相關的反模式。css

《你所應該知道的HTTP》系列的其餘篇章:web

握手延遲

握手延遲是指開始HTTP通訊以前所花費的時間,這裏的影響因素是不少的。
握手延遲主要來自三方面:DNS查詢、TCP三次握手和TLS四次握手。
DNS查詢也叫域名解析,是域名轉換到IP地址的過程,如今基本上都是使用域名進行URI的表示,所以DNS查詢是必須的步驟。
HTTP是基於TCP的協議,所以TCP的三次握手也是不可避免的步驟。
TLS四次握手的優化已經在HTTPS篇講過,故此處再也不重複。
咱們優化的目標就是要儘可能下降握手延遲,可用方案以下:ajax

DNS查詢優化

  • 限制不一樣域名的數量:
    每個域名,就意味着可能須要一次DNS查詢,減小域名數量天然有助於減小查詢次數。
  • 使用最近的DNS服務器:
    將物理距離的影響儘可能下降,能夠優化DNS服務的建設部署或購買靠譜的第三方服務。
  • 在主體頁面HTML中使用DNS預取指令:
    DNS-Prefetching是讓具備此屬性的域名不須要用戶點擊連接就在後臺解析,待用戶真的點擊連接,就能夠少去DNS查詢的步驟,從而提升用戶體驗。

形如:segmentfault

<link rel="dns-prefetch" href="//ajax.googleapis.com">

優化TCP鏈接

  • 藉助CDN網絡儘早響應:
    將物理距離的影響儘可能下降,能夠自行建設部署CDN網絡或購買靠譜的第三方服務。
  • 使用connection:keep-alive:
    這樣能必定程度複用TCP,減小TCP鏈接的創建。
  • 在主體頁面HTML中使用preconnect指令:
    向瀏覽器提供提示,建議瀏覽器提早打開與連接網站的鏈接,以便在跟隨連接時能夠更快地獲取連接內容。但不可濫用,一樣會佔用瀏覽器的鏈接數。

形如:api

<link rel="preconnect" href="//font.example.com" crossorigin>

避免重定向

重定向是須要在創建完TCP鏈接後,服務器才以301或302的狀態碼告知客戶端,這時候一般須要從新創建TCP鏈接。
所以最好的解決方案固然是完全不要重定向。固然,非要重定向也能夠考慮使用下面兩個變通辦法。瀏覽器

  • 利用CDN代替客戶端在雲端實現重定向;
  • 若是是同一個域名的重定向,使用web服務器的rewrite規則,避免瀏覽器跳轉。

減小請求須要

這是最直接的解決辦法,不須要那麼多的請求需求,天然就沒有延遲。主要思路不外乎合併外聯資源,但這要適度,由於若是合併的文件過大,反而會下降了加載速度。緩存

  • CSS Sprites(精靈圖):
    即將多張圖片合併成一張,本來須要多個請求獲取的圖片,如今只須要一次請求就能獲取到。
  • 內聯圖片:
    使用data:URL模式,就是把圖片編碼爲字串直接嵌入網頁。
  • 合併或內聯腳本和樣式表:
    減小外聯的js和css文件天然會減小請求,但內聯有助於緩存,這須要平衡考慮。
  • 緩存:
    參見緩存篇提到的Expires或Cache-Control。

隊頭阻塞

進階篇中講到H1的鏈接管理模型並未提供機制來同時請求多個資源。也就是說它須要發起請求、等待響應,以後才能發起下一個請求。資源將排隊等待一問一答的加載,若是中間出現任何情況,都會致使剩下的工做被阻塞
咱們優化的目標就是要儘可能提升併發,減小隊頭阻塞的影響,可用方案以下:服務器

域名拆分

現代瀏覽器爲了解決這個問題會對單個域名開啓6個左右的鏈接,經過各個鏈接分別發送請求。它實現了某種程度上的並行,但每一個鏈接仍會受到「隊頭阻塞」的影響。
咱們能夠利用這一機制,將資源分佈在多個域名下,這樣一個域名6個請求,兩個域名就能有12個請求。但這裏也涉及到增長了dns查詢、TCP鏈接增長的問題,故須要達到一個最佳平衡,不可盲目。
Yahoo研究代表,一個網站使用2個主機名進行資源加載可達到最優。cookie

低效的TCP利用

TCP的設計思路:對假設狀況很保守,並可以公平對待同一網絡的不一樣流量的應用。它的成功並非由於傳輸速度快,而是由於它是最可靠的協議之一。
TCP的經過慢啓動,探索當前鏈接擁塞窗口的合適大小。即先發送少許數據包,若是接收到響應且無丟包,就在下一次發送多一倍的數據包,直到發包上限。也就是說說TCP的傳輸速度是逐步加快的,並不能一會兒滿速的。網絡

擁塞窗口(congestion window)
擁塞窗口是指,在接收方確認數據包以前,發送方能夠發出的TCP包的數量。
例如:若是擁塞窗口爲1,則發送方發出1個數據包以後,只有接收方確認了那個包,才能發送下一個。

擁塞控制.png

而頁面文件數據量原本就不大,創建TCP鏈接每每還沒到最佳速度就結束了,即便多條鏈接併發也不能保證它們性能最優。
咱們優化的目標就是要儘可能複用TCP鏈接,可用方案以下:

使用長鏈接

使用connection:keep-alive是H1僅有的提升TCP使用率的辦法。

臃腫的消息首部

H1雖然提供了壓縮請求內容(body)的機制,可是消息首部卻沒法壓縮。特別是其中的cookie有時很大,這樣就天然增長了每次重複的數據量傳輸,並且自定義頭部的增長,這種狀況愈來愈嚴重。
咱們優化的目標就是要儘可能減小消息首部,可用方案以下:

減小cookie

cookie雖然保存在本地,但每次請求都會被髮送到服務器,須要儘可能減少cookie大小。須要較大的信息存儲時,能夠考慮使用其餘客戶端的緩存,好比:WebStorage、WebDatabases等。

分離資源域名與ajax域名

資源傳輸通常都不須要cookie,故能夠在這類域名上設置cookie禁用。

受限的優先級設置

H1基本沒有關於優先級的設計,單純由瀏覽器決定,瀏覽器的有些解析過程還會阻塞資源的請求。
咱們優化的目標就是使用瀏覽器的特性手動安排優先級,可用方案以下:

合理安排資源加載

  • JS放HTML文檔末尾能夠防止阻塞其餘資源加載;
  • 若是JS執行順序可有可無,而且必須在onload事件觸發以前執行,能夠設置asyn屬性;
  • 若是JS執行順序很重要,而且容許腳本在dom加載完後執行,能夠設置defer屬性;
  • 對不會影響頁面初次渲染的JS腳本,能夠在onload事件以後再經過動態新建標籤請求加載。

升級到HTTP/2.0

升級到H2能夠解決大部分上面提到的有關H1的性能問題,上面提到的「隊頭阻塞」和「低效的TCP利用」會被H2的多路複用解決,「臃腫的消息首部」會被首部表和首部壓縮解決,「受限的優先級設置」會被請求優先級解決。
那前面提到的一些優化方案是否還須要保留呢?答案是否認的,一些優化方案不單沒有效果反而會成爲反模式,這裏須要注意:

反模式:生成精靈圖和資源合併/內聯

單個文件都是能夠被緩存的,合併/內聯實際上會失去緩存的特性。在H1的是時代犧牲緩存減小請求數是划算的,但H2時代全部資源均可併發,而且只有一個鏈接,因此緩存的優點會更大。

反模式:域名拆分

爲了增長併發請求數,H1時代會將資源分散到多個域名下,但H2時代只有一個鏈接,而且均可並行請求,因此多個域名只會增長DNS解析的代價和創建鏈接的耗時。

反模式:禁用cookie的域名

H1時代一些不須要cookie的資源能夠放在禁用cookie的域名下減小請求大小,但H2時代的頭部是壓縮處理的,因此將資源的域名都與主頁面一致反而能夠減小DNS解析。

相關文章
相關標籤/搜索