構建高性能WEB之HTTP首部優化
0×00 前言
在討論瀏覽器優化之前,首先我們先分析下從客戶端發起一個HTTP請求到用戶接收到響應之間,都發生了什么?知己知彼,才能百戰不殆。這也是 作為一個WEB開發者,為什么一定要深入學習TCP/IP等網絡知識 。
0×01 到底發生什么了?
當用戶發起一個HTTP請求時,首先客戶端將與服務端之間建立TCP連接,成功建立連接后,服務端將對請求進行處理,并對客戶端做出響應,響應內容一般包括響應主體。
(此處我們僅簡單說明,但真實的一次請求其中發生的事情是相當復雜的,這里貼條連接,講得比較詳細)。
從輸入 URL 到頁面加載完成的過程中都發生了什么事情?建立TCP連接
為了進行可靠的數據傳輸,TCP在進行發送數據之前,會進行TCP三次握手,以此確定接收方能夠成功收取傳輸的數據,而建立連接的過程,必然是要耗費系統資源,以及時間資源的。
服務端處理并響應
當服務端接收到客戶端發送來的請求之后,如果請求內容是靜態資源,服務端會從硬盤中取出靜態資源,然后將靜態資源放在響應主體中,發送給客戶端。如果是動態資源,服務端首先取出資源,并通過業務邏輯操作,動態生成最終的響應主體,然后發送給客戶端。
客戶端渲染
客戶端接受到服務端傳輸過來的網絡資源,然后進行渲染,繪制等,最終展示給用戶。
0×02 優化點在哪里?
通過簡單的了解,我們了解到TCP建立連接是有資源消耗,時間消耗的,那么如果我們無需每次簡歷TCP連接,那是否可以提高網站的性能呢?答案是肯定的。
- 優化點1:減少TCP連接
我們知道,在獲取資源的時候,以獲取速度從慢到快是:網絡資源->本地硬盤資源->本地內存資源。而網絡資源也分硬盤資源以及內存資源。并且網絡資源的傳輸,也是有相當大的時延的。
- 優化點2:對數據進行緩存
- 優化點3:減少數據傳輸量
0×03 如何進行優化?
本篇文章主要說的優化點是與HTTP首部有關的優化,或者說是與瀏覽器端有關的優化,其它優化這里暫不贅述。
持久連接: Keep-Alive
HTTP連接設計之初是請求-響應-關閉,也就是每建立一次HTTP連接,只能進行一次資源請求,當需要在同一目標服務器上獲取多個資源的時候,就需要多次建立HTTP連接,而這個多次建立連接的過程,便降低了網站的性能。
于是,出現了 Connection:Keep-Alive
,人稱持久連接。 Keep-Alive
避免了建立或者說重新建立連接的過程,減少了HTTP連接。
而與此配套的有 Keep-Alive:timeout=120,max=5
其中, timeout=120
是指這個TCP通道保持120S, max=5
指這個TCP通道最多接收5個HTTP請求,之后便自動關閉該連接。
修改時間: Last-Modified
和 If-Modified-Since
Last-Modified首部是服務端對客戶端的HTTP響應所加的一個與緩存有關的HTTP首部,該首部標記了所請求資源在服務端的最后修改時間。類似:
Last-Modified : Fri , 12 May 2015 13:10:33 GMT
當客戶端發現HTTP響應頭中有 Last-Modified
,會對資源進行緩存,在下次請求資源時,在HTTP請求頭中添加 If-Modified-Since
首部,首部中將會添加上次成功請求資源時響應頭部的 Last-Modified
屬性值,即:
If-Modified-Since : Fri , 12 May 2015 13:10:33 GMT
當服務端接收到的HTTP請求中,發現有 If-Modified-Since
頭部時,會將該屬性值與請求資源的最后修改時間進行比對,如果最后修改時間與該屬性值一致時,服務端會返回一個 304 Not Modified
響應,該響應中不包括響應實體。瀏覽器收到304的響應后,會進行重定向,獲取本地緩存資源。如果最后修改時間與該屬性值不一致,則會從服務端重新獲取資源,做出200響應。
版本標記: ETag
和 If-None-Match
ETag
其實與 Last-Modified
是差不多的方式,但是 ETag
并沒有選擇以時間作為標記,而是對所請求文件進行某些算法來生成一串唯一的字符串,作為對某一文件的標記。當收到客戶端對某一資源的請求時,服務端在響應時,添加 ETag
首部,如下:
ETag:W/"a627ff1c9e65d2dede2efe0dd25efb8c"
當客戶端發現ETag頭部時,同樣會對資源進行緩存,并在下次請求時,在請求頭部添加 If-None-Match
,如:
If-None-Match:W/"a627ff1c9e65d2dede2efe0dd25efb8c"
當服務端收到請求中含有該頭部時,會使用同樣的 ETag
生成算法對文件ETag進行計算,并與 If-None-Match
屬性值進行比對,如果一致,則返回一個 304 Not Modified
響應,基本與上一種方式是一致的。
緩存時間: Expires
和 Cache-Control
上述兩種方式中,每次請求資源時,雖然在有緩存的情況下,選擇緩存進行渲染繪制,但是在這之前還是發起了一次HTTP請求,雖然并沒有真實的響應實體,但是依然會造成一些資源消耗。而 Expires
與上述兩種方式使用了不同的思路。
當服務端希望客戶端瀏覽器對某一資源進行緩存時,為了免去客戶端每次都要詢問自己:我上次的緩存現在還能用嗎?所以,服務端選擇了放權。只去告訴瀏覽器,我這次給你的資源你可以用多長時間,在這個時間段內,你可以一直使用它,無需每次咨詢我。而服務端就是通過 Expires
屬性來告訴客戶端瀏覽器可以多長時間內不需要詢問服務端。如下:
Expires:Thu, 19 Nov 2015 15:00:00 GMT
當客戶端在響應首部中發現該屬性值時,便會將該資源緩存起來,而緩存的過期時間即是 Expires
中的時間。在這個時間段內,瀏覽器完全自主。
但是, Expires
有一個不足的地方是,如果服務端時間與客戶端本地時間不統一時,可能服務端讓客戶端可以對該資源緩存一個小時,而客戶端本地時間比服務端時間快了兩個小時,那就意味著,所有緩存都將不會生效。
于是有了彌補該不足的一個屬性,即: Cache-Control
。如果服務端在響應首部添加該屬性時,客戶端將直接使用該屬性值來生成本地時間的緩存過期時間,這樣便解決了這個問題,如下:
Cache-Control:max-age=3600
如果客戶端在2015年10月01日13時00分00秒收到該響應時,便會加上3600秒也就是2015年10月01日14時00分00秒作為緩存過期時間。如果響應頭部既有 Expires
和 Cache-Control
,瀏覽器會首選 Cache-Control
。
0×04 結束
這里,基本上說的都是與HTTP首部有關的網站性能優化。本文主要是在對《構建高性能WEB站點. 郭欣著》中第六章瀏覽器緩存的學習總結筆記。這本書對于WEB站點的優化,從各個層面都做了很詳盡的講解,確實是一本很棒的書,也在這里感謝HQBOSS的推薦。