1. 程式人生 > >005-優化web請求一-gzip壓縮、http緩存控制和緩存校驗[Pragma、Expires、Cache-Control、max-age、Last-Modified、用戶刷新訪問、避免過度304]

005-優化web請求一-gzip壓縮、http緩存控制和緩存校驗[Pragma、Expires、Cache-Control、max-age、Last-Modified、用戶刷新訪問、避免過度304]

無法 新鮮度 開發者模式 請求報文 XML 自定義 server clas 存在

  優化Web應用的典型技術:緩存控制頭信息、Gzip、應用緩存、ETag、反應型技術【異步方法調用和WebSocket】

一、模板緩存

spring.thymeleaf.cache=true
spring.messages.cache-duration=

二、Gzip壓縮

  Gzip是一種能夠被瀏覽器直接理解的壓縮算法。服務器會提供壓縮響應,會耗一些cpu,但是減少帶寬

  GZIP壓縮是一個經常被用到的WEB性能優化的技巧,它主要是對頁面代碼,CSS,Javascript,PHP等文件進行壓縮,而且在壓縮的前後,文件的大小會有明顯的改變,從而達到網站訪問加速的目的。

  GZIP壓縮時,WEB服務器與瀏覽器之間的協商過程如下:

1、首先瀏覽器請求某個URL地址,並在請求的開始部分頭(head) 設置屬性accept-encoding值為gzip、deflate,表明瀏覽器支持gzip和deflate這兩種壓縮方式(事實上deflate也是使用GZIP壓縮協議,在之後的內容之我們會介紹二者之間的區別);

2、WEB服務器接收到請求後判斷瀏覽器是否支持GZIP壓縮,如果支持就傳送壓縮後的響應內容,否則傳送不經過壓縮的內容;

3、瀏覽器獲取響應內容後,判斷內容是否被壓縮,如果是壓縮文件則解壓縮,然後顯示響應頁面的內容。

在Springboot中配置gzip

# 是否啟用壓縮 默認false
server.compression.enabled
=true # 默認"text/html", "text/xml", "text/plain","text/css", "text/javascript", "application/javascript", "application/json", # "application/xml" server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript ,application/json, #content-length 在壓縮啟用後。返回數據多大開始啟用gzip,默認2048 為了測試添加為1 server.compression.min
-response-size=1

測試1、未開啟壓縮

# 是否啟用壓縮 默認false
server.compression.enabled=false

客戶端請求頭

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9,en;q=0.8

服務端響應

Content-Length:60973
Content-Type:text/html;charset=UTF-8
Date:Wed, 30 Jan 2019 08:19:19 GMT

測試2、開啟壓縮

# 是否啟用壓縮 默認false
server.compression.enabled=true
#content-length 在壓縮啟用後。返回數據多大開始啟用gzip,默認2048 為了測試添加為1
server.compression.min-response-size=1

客戶端請求頭不變

服務端響應

Content-Encoding:gzip
Content-Type:text/html;charset=UTF-8
Date:Wed, 30 Jan 2019 08:20:50 GMT
Transfer-Encoding:chunked
Vary:Accept-Encoding

三、緩存控制和緩存校驗

3.1、使用chrome的開發者模式

首先瀏覽器請求某個URL地址,並在請求的開始部分頭(head) 設置屬性accept-encoding值為gzip、deflate,表明瀏覽器支持gzip和def

  第一部分General是概要,包含請求地址,請求方式,狀態碼,服務器地址以及Referrer 策略。
  第二部分是應答頭部,是服務器返回的。
  第三部分是請求頭部,是客戶端發送的。

  RFC2616規定的47種http報文首部字段中與緩存相關的字段:

1、通用首部字段

  技術分享圖片

2、請求首部字段

  技術分享圖片

3、響應首部字段

  技術分享圖片

4、實體首部字段

  技術分享圖片

3.2、Http 1.0 緩存控制方式

  在 http1.0 時代,給客戶端設定緩存方式可通過兩個字段——Pragma和Expires來規範。雖然這兩個字段早可拋棄,但為了做http協議的向下兼容,你還是可以看到很多網站依舊會帶上這兩個字段。例如在訪問個別網站的時候,通過瀏覽器調試工具可以看到部分HTTP響應是包含Expires頭部的。

3.2.1、Pragma-禁用緩存

  當該字段值為no-cache的時候(事實上現在RFC中也僅標明該可選值),會知會客戶端不要對該資源讀緩存,即每次都得向服務器發一次請求才行。

3.2.2、Expires-啟用緩存和緩存時間

  有了Pragma來禁用緩存,自然也需要有個東西來啟用緩存和定義緩存時間,對http1.0而言,Expires就是做這件事的首部字段。 Expires的值對應一個GMT(格林尼治時間),比如Mon, 22 Jul 2002 11:12:01 GMT來告訴瀏覽器資源緩存過期時間,如果還沒過該時間點則不發請求。 

  需要註意的是,響應報文中Expires所定義的緩存時間是相對服務器上的時間而言的,其定義的是資源“失效時刻”,如果客戶端上的時間跟服務器上的時間不一致(特別是用戶修改了自己電腦的系統時間),那緩存時間可能就沒意義了。 

3.3、Http 1.1 緩存控制

  緩存控制由服務器端發送一組HTTP頭信息,他將會控制用戶瀏覽器如何緩存資源。

  如果一個報文中同時出現Pragma和Cache-Control時,以Pragma為準。同時出現Cache-Control和Expires時,以Cache-Control為準。

  即優先級從高到低是 Pragma -> Cache-Control -> Expires

3.3.1、Cache-Control

1、前提註意:

  符合緩存策略時,服務器不會發送新的資源,但不是說客戶端和服務器就沒有會話了,客戶端還是會發請求到服務器的。
  Cache-Control除了在響應中使用,在請求中也可以使用。我們用開發者工具來模擬下請求時帶上Cache-Control:勾選Disable cache,刷新頁面,可以看到Request Headers中有個字段Cache-Control: no-cache。
  同時在Response Headers中也能到Cache-Control字段,它的值是must-revalidate,這是服務端設置的。

  Cache-Control也是一個通用首部字段,這意味著它能分別在請求報文和響應報文中使用。在RFC中規範了 Cache-Control 的格式為:

"Cache-Control" ":" cache-directive

2、Http Status 304 說明

  Http status 304 當一個客戶端(通常是瀏覽器)向web服務器發送一個請求,如果web服務器返回304響應,則表示此請求的本地緩存是最新的,可以直接使用。這種方法可以節省帶寬,避免重復響應。

3、作為請求首部時,cache-directive 的可選值有:

字段名稱 說明
no-cache 告知(代理)服務器不直接使用緩存,要求向原服務器發起請求
no-store 所有內容都不會被保存到緩存或Internet臨時文件中
max-age=delta-seconds 告知服務器客戶端希望接收一個存在時間(age)不大於delta-seconds秒的資源
max-stale[=delta-seconds]

告知(代理)服務器客戶端願意接收一個超過緩存時間的資源,若有定義

delta-seconds則為delta-srconds秒,若沒有則為任意超出的時間

min-freash=delta-seconds 告知(代理)服務器客戶端希望接收一個在小於delta-seconds秒內被更新過的資源
no-transform 告知(代理)服務器客戶端希望獲取實體數據沒有被轉換(比如壓縮)過的資源
only-if-cached 告知(代理)服務器客戶端希望獲取緩存的內容(若有),而不用向原服務器發去請求
cache-extension 自定義擴展值,若服務器器不識別該值將被忽略

4、作為響應首部時,cache-directive 的可選值有:

字段名稱 說明
public 表名任何情況下都得緩存該資源(即使是需要http認證的資源)
Private[="field-name"] 表明返回報文中全部或部分(若指定了field-name則為field-name的字段數據)僅開
放給某些用戶(服務器指定的share-user,如代理服務器)做緩存使用,其他用戶則
不能緩存這些數據
no-cache 不直接使用緩存,要求向服務器發起(新鮮度校驗)請求
no-store 所有內容都不會被保存到緩存或Internet臨時文件中
max-age=delta-seconds 告知客戶端該資源在delta-seconds秒內是新鮮的,無需向服務器發請求
s-maxage=delta-seconds

同max-age,但僅應用於共享緩存(如代理)

no-transform 告知客戶端緩存文件時不得對實體數據做任何改變
only-if-cached 告知(代理)服務器客戶端希望獲取緩存的內容(若有),而不用向原服務器發去請求
must-revalidate 當前資源一定是向原服務器發去驗證請求的,若請求失敗會返回504(而非代理服務器
上的緩存)
proxy-revalidate 與must-revalidate類似,但僅能應用於共享緩存(如代理)
cache-extension 自定義擴展值,若服務器器不識別該值將被忽略

5、no-store優先級最高

  在Cache-Control 中,這些值可以自由組合,多個值如果沖突時,也是有優先級的,而no-store優先級最高。本地不保存,每次都需要服務器發送資源。

6、public和private的選擇

  如果你用了CDN,你需要關註下這個值。CDN廠商一般會要求cache-control的值為public,提升緩存命中率。如果你的緩存命中率很低,而訪問量很大的話,可以看下是不是設置了private,no-cache這類的值。如果定義了max-age,可以不用再定義public,它們的意義是一樣的。

7、max-age

  max-age:用來指定引用文檔過期時間【如頁面內引用的js文件等】。    

    max-age>0 時 頁面內引用的資源直接從遊覽器緩存中 提取,此時http status是304,無論被引用的資源服務器端是否改變,可以查看

      示例:第一次請求,test.html,test.js的http status均是200

        技術分享圖片

        技術分享圖片

      第二次請求,test.html的http status是304,test.js[引用資源]的http status是200,但是數據來自緩存

        技術分享圖片

      第三次請求,修改服務端js,後請求,因為max-age=30000,test.html的http status是304,test.js[引用資源]的http status是200,但是數據來自緩存

        技術分享圖片

    max-age<=0 時 頁面或頁面內引用的資源都會向server發送http請求,請求確認該資源是否有修改 有的話 返回200 ,無的話返回304。

      第一次請求,test.html,test.js的http status均是200

        技術分享圖片

      第二次請求,test.html,test.js的http status均是304

        技術分享圖片

      第三次請求,修改遠端js,客戶端重新獲取,test.html的http status是304,test.js[引用資源]的http status是200,數據來自服務端,size不是from cache

        技術分享圖片         

    註意:無論max-age什麽值,單獨請求回車刷新是會發請求的 如果服務器端的文件沒有產生變化,那麽會返回304,比如單獨訪問 一個js

3.4、緩存校驗

  在緩存中,我們需要一個機制來驗證緩存是否有效。比如服務器的資源更新了,客戶端需要及時刷新緩存;又或者客戶端的資源過了有效期,但服務器上的資源還是舊的,此時並不需要重新發送。緩存校驗就是用來解決這些問題的,在http 1.1 中,我們主要關註下Last-Modified 和 etag 這兩個字段。

1、Last-Modified

  服務端在返回資源時,會將該資源的最後更改時間通過Last-Modified字段返回給客戶端。客戶端下次請求時通過If-Modified-Since或者If-Unmodified-Since帶上Last-Modified,服務端檢查該時間是否與服務器的最後修改時間一致:如果一致,則返回304狀態碼,不返回資源;如果不一致則返回200和修改後的資源,並帶上新的時間。

   技術分享圖片

  If-Modified-Since和If-Unmodified-Since的區別是:
    If-Modified-Since:告訴服務器如果時間一致,返回狀態碼304
    If-Unmodified-Since:告訴服務器如果時間不一致,返回狀態碼412

2、etag

  單純的以修改時間來判斷還是有缺陷,比如文件的最後修改時間變了,但內容沒變。對於這樣的情況,我們可以使用etag來處理。
  etag的方式是這樣:服務器通過某個算法對資源進行計算,取得一串值(類似於文件的md5值),之後將該值通過etag返回給客戶端,客戶端下次請求時通過If-None-Match或If-Match帶上該值,服務器對該值進行對比校驗:如果一致則不要返回資源。

  If-None-Match和If-Match的區別是:
    If-None-Match:告訴服務器如果一致,返回狀態碼304,不一致則返回資源
    If-Match:告訴服務器如果不一致,返回狀態碼412

3.5、小結

  1、緩存開關是: pragma, cache-control。

  2、緩存校驗有:Expires,Last-Modified,etag。需要兼容HTTP1.0的時候需要使用Expires,不然可以考慮直接使用Cache-Control。需要處理一秒內多次修改的情況,或者其他Last-Modified處理不了的情況,才使用ETag,否則使用Last-Modified。

  3、緩存頭部對比

頭部優勢和特點劣勢和問題
Expires 1、HTTP 1.0 產物,可以在HTTP 1.0和1.1中使用,簡單易用。
2、以時刻標識失效時間。
1、時間是由服務器發送的(UTC),如果服務器時間和客戶端時間存在不一致,可能會出現問題。
2、存在版本問題,到期之前的修改客戶端是不可知的。
Cache-Control 1、HTTP 1.1 產物,以時間間隔標識失效時間,解決了Expires服務器和客戶端相對時間的問題。
2、比Expires多了很多選項設置。
1、HTTP 1.1 才有的內容,不適用於HTTP 1.0 。
2、存在版本問題,到期之前的修改客戶端是不可知的。
Last-Modified 1、不存在版本問題,每次請求都會去服務器進行校驗。服務器對比最後修改時間如果相同則返回304,
不同返回200以及資源內容。
1、只要資源修改,無論內容是否發生實質性的變化,都會將該資源返回客戶端。例如周期性重寫,
這種情況下該資源包含的數據實際上一樣的。
2、以時刻作為標識,無法識別一秒內進行多次修改的情況。
3、某些服務器不能精確的得到文件的最後修改時間。
ETag 1、可以更加精確的判斷資源是否被修改,可以識別一秒內多次修改的情況。
2、不存在版本問題,每次請求都回去服務器進行校驗。
1、計算ETag值需要性能損耗。
2、分布式服務器存儲的情況下,計算ETag的算法如果不一樣,會導致瀏覽器從一臺服務器上獲得頁面
內容後到另外一臺服務器上進行驗證時發現ETag不匹配的情況。


  3、從狀態碼的角度來看,它們的關系如下圖:

        技術分享圖片

  4、cache-control的各個值關系如下圖

    技術分享圖片

  原文參看地址:https://imweb.io/topic/5795dcb6fb312541492eda8c

3.6、用戶刷新訪問行為

1、在URI輸入欄中輸入然後回車/通過書簽訪問

  可以看到返回響應碼是 200 OK (from cache),瀏覽器發現該資源已經緩存了而且沒有過期(通過Expires頭部或者Cache-Control頭部),沒有跟服務器確認,而是直接使用了瀏覽器緩存的內容。其中響應內容和之前的響應內容一模一樣,例如其中的Date時間是上一次響應的時間。

2、F5/點擊工具欄中的刷新按鈕/右鍵菜單重新加載

  F5的作用和直接在URI輸入欄中輸入然後回車是不一樣的,F5會讓瀏覽器無論如何都發一個HTTP Request給Server,即使先前的響應中有Expires頭部。

  其中Cache-Control是Chrome強制加上的,而If-Modified-Since是因為獲取該資源的時候包含了Last-Modified頭部,瀏覽器會使用If-Modified-Since頭部信息重新發送該時間以確認資源是否需要重新發送。 實際上Server沒有修改這個index.css文件,所以返回了一個304(Not Modified),這樣的響應信息很小,所消耗的route-trip不多,網頁很快就刷新了。

3、Ctl+F5

  Ctrl+F5是徹底的從Server拿一份新的資源過來,所以不光要發送HTTP request給Server,而且這個請求裏面連If-Modified-Since/If-None-Match都沒有,這樣Server不能返回304,而是把整個資源原原本本地返回一份,這樣,Ctrl+F5引發的傳輸時間變長了,自然網頁Refresh的也慢一些。我們可以看到該操作返回了200,並刷新了相關的緩存控制時間。

  實際上,為了保證拿到的是從Server上最新的,Ctrl+F5不只是去掉了If-Modified-Since/If-None-Match,還需要添加一些HTTP Headers。按照HTTP/1.1協議,Cache不光只是存在Browser終端,從Browser到Server之間的中間節點(比如Proxy)也可能扮演Cache的作用,為了防止獲得的只是這些中間節點的Cache,需要告訴他們,別用自己的Cache敷衍我,往Upstream的節點要一個最新的copy吧。
  在Chrome 51 中會包含兩個頭部信息, 作用就是讓中間的Cache對這個請求失效,這樣返回的絕對是新鮮的資源。

Cache-Control: no-cache
Pragma: no-cache

3.7、避免過度304

  可以通過標識文件版本名、加長緩存時間的方式來減少304響應。

  如果Expires和Cache-Control時間過長長,導致用戶無法得到其最近的內容。

  把服務側ETag的那一套理論搬到了前端來使用。 頁面的靜態資源以版本形式發布,常用的方法是在文件名或參數帶上一串md5或時間標記符:

https://hm.baidu.com/hm.js?e23800c454aa573c0ccb16b52665ac26
http://tb1.bdstatic.com/tb/_/tbean_safe_ajax_94e7ca2.js
http://img1.gtimg.com/ninja/2/2016/04/ninja145972803357449.jpg

  那麽在文件沒有變動的時候,瀏覽器不用發起請求直接可以使用緩存文件;而在文件有變化的時候,由於文件版本號的變更,導致文件名變化,請求的url變了,自然文件就更新了。這樣能確保客戶端能及時從服務器收取到新修改的文件。通過這樣的處理,增長了靜態資源,特別是圖片資源的緩存時間,避免該資源很快過期,客戶端頻繁向服務端發起資源請求,服務器再返回304響應的情況(有Last-Modified/Etag)。

005-優化web請求一-gzip壓縮、http緩存控制和緩存校驗[Pragma、Expires、Cache-Control、max-age、Last-Modified、用戶刷新訪問、避免過度304]