在看Spring中HttpServlet的Service方法時,對於GET請求,代碼邏輯如下:
if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } }
其中涉及的關鍵信息是修改時間,這就涉及到瀏覽器數據緩存問題。以下對個緩存機制進行簡單總結,可用於請求的優化。(本文中的圖都摘自其他文章)
Last-Modified和If-Modified-Since
當瀏覽器GET請求的時候,如果有If-Modified-Since,則會與當前服務器上相關資源最后一次修改時間進行對比,如果相同則返回304(資源可訪問,但沒修改),否則加載最新數據,瀏覽器再緩存起來。這樣減少網絡數據傳輸和服務器壓力。

ETags和If-None-Match
根據修改時間判斷文件是否被修改,如果一個網頁被頻繁更新,但實際內容並沒有更新,依然會對服務器造成壓力。引入了ETags和If-None-Match,其不同於Last-Modified和If-Modified-Since取決於修改時間,可以依賴其他屬性,如資源的MD5等。當資源被更新,但實際內容沒有更新,則會比較修改時間和ETags,如果ETags沒變,則不更新數據資源。但查閱源碼戴發現,目前底層實現上實現的是弱Etags,其由文本長度和修改時間組成,資源被更新后,ETags依然會更新。在AbstractResource中,代碼如下:
@Override public final String getETag() { if (weakETag == null) { synchronized (this) { if (weakETag == null) { long contentLength = getContentLength(); long lastModified = getLastModified(); if ((contentLength >= 0) || (lastModified >= 0)) { weakETag = "W/\"" + contentLength + "-" + lastModified + "\""; } } } } return weakETag; }

Expires
添加Expires頭能有效的利用瀏覽器的緩存能力來改善頁面的性能,能在后續的頁面中有效避免很多不必要的Http請求,WEB服務器使用Expires頭來告訴Web客戶端它可以使用一個組件的當前副本,直到指定的時間為止。例如:Expires:Thu,15 Apr 2010 20:00:00 GMT; 他告訴瀏覽器緩存有效性持續到2010年4月15日為止,在這個時間之內相同的請求使用緩存,這個時間之外使用http請求。Expires有一個非常大的缺陷,它使用一個固定的時間,要求服務器與客戶端的時鍾保持嚴格的同步,並且這一天到來后,服務器還得重新設定新的時間。

Cache-Control
HTTP1.1引入了Cathe-Control,它使用max-age指定組件被緩存多久(時間相對請求的時間),從請求開始在max-age時間內瀏覽器使用緩存,之外的使用請求,這樣就可以消除Expires的限制。但有個缺點就是,用戶不能第一時間拿到最新修改的文件。請求過程如下:

另外,gulp 給靜態資源文件添加hash(md5)后綴防止緩存無效,能獲取最新文件(這個需要進一步研究)
參考:
If-Modified-Since和If-None-Match
http://www.360doc.com/content/17/0721/17/41344223_673116604.shtml
