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

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

1、模板緩存

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

2、Gzip壓縮

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

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

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

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

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

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

在Springboot中配置gzipjava

# 是否啓用壓縮 默認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

測試一、未開啓壓縮web

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

客戶端請求頭ajax

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

測試二、開啓壓縮spring

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

客戶端請求頭不變chrome

服務端響應

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、緩存控制和緩存校驗

3.一、使用chrome的開發者模式

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

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

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

一、通用首部字段

  

二、請求首部字段

  

三、響應首部字段

  

四、實體首部字段

  

3.二、Http 1.0 緩存控制方式

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

3.2.一、Pragma-禁用緩存

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

3.2.二、Expires-啓用緩存和緩存時間

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

  須要注意的是,響應報文中Expires所定義的緩存時間是相對服務器上的時間而言的,其定義的是資源「失效時刻」,若是客戶端上的時間跟服務器上的時間不一致(特別是用戶修改了本身電腦的系統時間),那緩存時間可能就沒意義了。 

3.三、Http 1.1 緩存控制

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

  若是一個報文中同時出現Pragma和Cache-Control時,以Pragma爲準。同時出現Cache-Control和Expires時,以Cache-Control爲準。

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

3.3.一、Cache-Control

一、前提注意:

  符合緩存策略時,服務器不會發送新的資源,但不是說客戶端和服務器就沒有會話了,客戶端仍是會發請求到服務器的。
  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

二、Http Status 304 說明

  Http status 304 當一個客戶端(一般是瀏覽器)向web服務器發送一個請求,若是web服務器返回304響應,他不包含任何響應的內容,只是提示客戶端緩存的內容是最新的,能夠直接使用。這種方法能夠節省帶寬,避免重複響應。

三、做爲請求首部時,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 自定義擴展值,若服務器器不識別該值將被忽略
   

四、做爲響應首部時,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 自定義擴展值,若服務器器不識別該值將被忽略
   

五、no-store優先級最高

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

六、public和private的選擇

  若是你用了CDN,你須要關注下這個值。CDN廠商通常會要求cache-control的值爲public,提高緩存命中率。若是你的緩存命中率很低,而訪問量很大的話,能夠看下是否是設置了private,no-cache這類的值。若是定義了max-age,能夠不用再定義public,它們的意義是同樣的。

七、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.四、緩存校驗

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

  HTTP提供了自帶的緩存框架。你須要作的是在返回的時候加入一些返回頭信息,在接受輸入的時候加入輸入驗證。基本兩種方法:

   ETag:當生成請求的時候,在HTTP頭裏面加入ETag,其中包含請求的校驗和和哈希值,這個值和在輸入變化的時候也應該變化。若是輸入的HTTP請求包含IF-NONE-MATCH頭以及一個ETag值,那麼API應該返回304 not modified狀態碼,而不是常規的輸出結果。

   Last-Modified:和etag同樣,只是多了一個時間戳。返回頭裏的Last-Modified:包含了 RFC 1123 時間戳,它和IF-MODIFIED-SINCE一致。HTTP規範裏面有三種date格式,服務器應該都能處理。

一、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

二、etag

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

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

3.五、小結

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

  二、緩存校驗有:Expires,Last-Modified,etag。須要兼容HTTP1.0的時候須要使用Expires,否則能夠考慮直接使用Cache-Control。須要處理一秒內屢次修改的狀況,或者其餘Last-Modified處理不了的狀況,才使用ETag,不然使用Last-Modified。

  三、緩存頭部對比

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


  三、從狀態碼的角度來看,它們的關係以下圖:

        

  四、cache-control的各個值關係以下圖

    

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

3.六、用戶刷新訪問行爲

一、在URI輸入欄中輸入而後回車/經過書籤訪問

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

二、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很少,網頁很快就刷新了。

三、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.七、避免過分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)。

相關文章
相關標籤/搜索