優化Web應用的典型技術:緩存控制頭信息、Gzip、應用緩存、ETag、反應型技術【異步方法調用和WebSocket】javascript
spring.thymeleaf.cache=true spring.messages.cache-duration=
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
首先瀏覽器請求某個URL地址,並在請求的開始部分頭(head) 設置屬性accept-encoding值爲gzip、deflate,代表瀏覽器支持gzip和def
第一部分General是概要,包含請求地址,請求方式,狀態碼,服務器地址以及Referrer 策略。
第二部分是應答頭部,是服務器返回的。
第三部分是請求頭部,是客戶端發送的。
RFC2616規定的47種http報文首部字段中與緩存相關的字段:
一、通用首部字段
二、請求首部字段
三、響應首部字段
四、實體首部字段
在 http1.0 時代,給客戶端設定緩存方式可經過兩個字段——Pragma和Expires來規範。雖然這兩個字段早可拋棄,但爲了作http協議的向下兼容,你仍是能夠看到不少網站依舊會帶上這兩個字段。例如在訪問個別網站的時候,經過瀏覽器調試工具能夠看到部分HTTP響應是包含Expires頭部的。
當該字段值爲no-cache的時候(事實上如今RFC中也僅標明該可選值),會知會客戶端不要對該資源讀緩存,即每次都得向服務器發一次請求才行。
有了Pragma來禁用緩存,天然也須要有個東西來啓用緩存和定義緩存時間,對http1.0而言,Expires就是作這件事的首部字段。 Expires的值對應一個GMT(格林尼治時間),好比Mon, 22 Jul 2002 11:12:01 GMT來告訴瀏覽器資源緩存過時時間,若是還沒過該時間點則不發請求。
須要注意的是,響應報文中Expires所定義的緩存時間是相對服務器上的時間而言的,其定義的是資源「失效時刻」,若是客戶端上的時間跟服務器上的時間不一致(特別是用戶修改了本身電腦的系統時間),那緩存時間可能就沒意義了。
緩存控制由服務器端發送一組HTTP頭信息,他將會控制用戶瀏覽器如何緩存資源。
若是一個報文中同時出現Pragma和Cache-Control時,以Pragma爲準。同時出現Cache-Control和Expires時,以Cache-Control爲準。
即優先級從高到低是 Pragma -> Cache-Control -> Expires
一、前提注意:
符合緩存策略時,服務器不會發送新的資源,但不是說客戶端和服務器就沒有會話了,客戶端仍是會發請求到服務器的。
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
在緩存中,咱們須要一個機制來驗證緩存是否有效。好比服務器的資源更新了,客戶端須要及時刷新緩存;又或者客戶端的資源過了有效期,但服務器上的資源仍是舊的,此時並不須要從新發送。緩存校驗就是用來解決這些問題的,在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
一、緩存開關是: 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
一、在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
能夠經過標識文件版本名、加長緩存時間的方式來減小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)。