web開發中緩存問題的研究

    一般情況下,瀏覽器都會緩存已經訪問過的頁面內容,關於如何禁止瀏覽器緩存的介紹,在網上到處都有相關的文章,但是,關於瀏覽器如何利用緩存,如何 處理緩存的講解,卻鮮有人談及.我一直爲這個問題所困惑,這個問題也是絕大多數有經驗的WEB開發人員所共同面臨的問題,我有些朋友已做過幾十個大大小小 的WEB項目,當與他們交流這些問題時,他們雖然也在項目中遇到和解決過這些問題,但由於沒有足夠的時間和精力來仔細思考這些問題的原因和細節,他們對這 些問題始終也是一知半解、含糊不清,而目前又很少關於這些問題的專門和詳細講解,我最近用了兩天的時間,把瀏覽器緩存的問題透徹地研究了一翻,主要包括一 下方面的細節.

    1.如何禁止瀏覽器緩存,這是最簡單的問題,本來羞於在此講解,但是爲了完整性,不妨將其列爲一個知識點.

    2.瀏覽器在訪問已緩存過的資源時,它在什麼情況下會向服務器發送請求?在什麼情況下根本就不向服務器發送請求.這與瀏覽器的緩存設置有關!但是,由於幾乎所有人的瀏覽器都是採用的默認設置,所以,重點應該放在分析瀏覽器的默認緩存設置的研究上.

    3.當通過其他網頁文檔中的超鏈接來訪問某一個已經緩存過的資源時,瀏覽器是否要向服務器發出訪問請求?如果不發,則會出現一個問題:當銷售一件商 品後再回到商品庫存的顯示頁面時,看到的還將是先前看到的內容,而不是更新的庫存數據。但是,在訪問一個普通的HTML文件時,如果瀏覽器每次都向服務器 發送訪問請求,效率就會相對低下,這就失去了緩存的意義和價值.所以,結論應是瀏覽器訪問動態頁面時不能使用緩存,而訪問靜態頁面時應該使用緩存,但是, 僅僅根據被訪問頁面的資源名稱,瀏覽器是無法知道商品庫存的顯示頁面是屬於動態內容,還是屬於靜態內容。瀏覽器是根據什麼方式來判斷它緩存的資源是動態 的,還是靜態的呢?在什麼請求下,它會對緩存的資源總是發出新的請求呢?

    4.對於緩存的內容,即使瀏覽器向服務器發送了請求,但服務器在接收到請求後,可能不會返回內容,而是讓瀏覽器繼續使用緩存的內容,這在實際應用中有什麼好處呢?如何處理其具體細節呢?

    5.服務器端也有緩存,當服務器接收到瀏覽器的請求後,假設它返回響應內容,但返回的響應內容可能不是最新的內容,而很可能是一箇舊的緩存版本,這又是怎麼回事呢?

    所有這些問題,在筆者的《深入體驗java web開發內幕》一書中都有深刻的分析和詳細的實驗步驟.

    以下是該書的節選(不包括服務器端緩存技術的講解,服務器端緩存技術在其他章節有案例分析)

4.5.8 瀏覽器緩存內幕與getLastModified方法
  在HttpServlet類中定義了一個getLastModified方法,其完整語法定義如下:
      protected long getLastModified(HttpServletRequest req)
  其中的返回值表示自1970年1月1日的0點0分0秒開始計算的一個毫秒數,HttpServlet類中定義的getLastModified方法總是返 回一個負數,在HttpServlet子類中可以對這個方法進行覆蓋,以便返回一個代表當前輸出的響應內容的修改時間,HttpServlet類的 service方法可以根據這個返回值在響應消息中自動生成Last-Modified頭字段。
    一般情況下,瀏覽器都會緩存已經訪問過的頁面內容,getLastModified方法的返回值可以影響瀏覽器如何處理和利用緩存內容。在詳細瞭解 getLastModified方法的應用之前,應該先對瀏覽器的緩存機制有所瞭解。單擊IE瀏覽器的“工具”“Internet選項”菜單,打開 “Internet選項”對話框,接着再單擊“常規”選項卡中的“Internet臨時文件”欄中的“設置”按鈕,打開如圖4.16所示的“設置”對話框

還可以看到,其“檢查所存網頁的較新版本”功能項的設置值有4個選項,只要先單擊“設置”對話框標題欄中的問號按鈕,然後再單擊相應的選項,就可以看到每個選項的作用和意義:

“每次訪問此頁時檢查”選項表示瀏覽器每次訪問一個頁面時,不管瀏覽器是否緩存過此頁面,都要向服務器發出訪問請求。這種設置的優點是實時性很強,肯定能夠訪問到網頁的最新內容,但是如果網頁內容很少更新,這種設置的訪問效率就比較低了。
    “ 每次啓動Internet Explorer時檢查”選項表示在瀏覽器的每次啓動運行期間,在第一次訪問一個頁面時,不管瀏覽器是否緩存過此頁面,都要向服務器發出訪問請求,但是在 瀏覽器的本次啓動運行期間對該頁面的後續訪問,瀏覽器將不再向服務器發出訪問請求,而是直接使用緩存中的內容。這種設置具有較高的訪問效率,同時也兼顧了 較好的實時性,它可以保證每次啓動瀏覽器後看到的都是最新的網頁內容。
   “自動”選項與“每次啓動Internet Explorer時檢查”選項的功能相似,只是對圖像的訪問有所不同,如果隨着時間的推移,瀏覽器發現網頁上的圖像更新並不頻繁,這樣,即使瀏覽器在對某 個已緩存的圖像執行本次啓動運行以來的第一次訪問時,它也不一定會向服務器發出訪問請求,而是乾脆直接使用緩存中的內容。“自動”選項是瀏覽器的默認設 置,所以,幾乎所有人的瀏覽器都是按照這種方式工作的,這個選項的作用和意義應該成爲讀者熟悉的重點。
    “不檢查”選項表示瀏覽器不管在什麼情況下訪問一個頁面時,只要能夠在本地找到此頁面的緩存信息,瀏覽器就不會向服務器發出訪問請求,而是直接使用緩存的內容。這種設置的優點是訪問效率很高,但是如果服務器端的網頁內容更新後,瀏覽器看到的內容很可能是過期的內容。
    在瀏覽器的“檢查所存網頁的較新版本”的功能項採用默認的“自動”設置項的情況下,如果瀏覽器剛剛訪問過一個網頁,服務器端就更新了這個網頁的內容,當瀏 覽在關閉前又重新訪問這個頁面時,用戶看到的將不是更新的網頁內容,而是過期的網頁內容。爲了提高瀏覽效率,在訪問靜態的網頁內容時,這麼一點小概率的過 期信息還是應該允許的,並且這些過期信息也不會造成什麼不好的後果,就像你偶爾有一次看到了前一天發生的新聞,而不是當天的新聞,這又有什麼問題呢?可 是,如果瀏覽器訪問的是一個動態網頁,這本來就要求瀏覽器在其整個運行期間的每次訪問都能看到最新的內容,例如,銷售一件商品後再回到商品庫存的顯示頁面 時,看到的就應該是更新的庫存數據,而不應該是先前看到的內容。僅僅根據被訪問頁面的資源名稱,瀏覽器是無法知道商品庫存的顯示頁面是屬於動態內容,還是 屬於靜態內容。對於這種情況,瀏覽器將根據響應消息中是否包含Last-Modified頭字段來進行處理,如果響應消息中沒有包含Last- Modified頭字段,它將在每次訪問此頁面時都向服務器發出訪問請求,否則,它僅在每次啓動運行後的第一次訪問此頁面時才向服務器發出訪問請求,而在 啓動運行期間對此頁面的後續訪問都不再向服務器發出訪問請求。
    在第2章中曾經講解過,響應消息中的Last-Modified頭字段可用於指定響應內容的最後更新時間,當客戶機緩存此文檔內容後,它在以後的請求消息 中將根據Last-Modified頭字段指定的時間來生成If-Modified-Since請求頭字段,以指出緩存文檔的最後更新時間。只有文檔的修 改時間比If-Modified-Since請求頭指定的時間新時,服務器纔會返回文檔內容。如果自從If-Modified-Since指定的時間以 來,網頁內容沒有發生修改,服務器將返回一個304(Not Modified)狀態碼來表示瀏覽器緩存的版本是最新的,而不會向瀏覽器返回文檔內容,瀏覽器則繼續使用以前緩存的內容。通過這種方式,可以在一定程度 上減少瀏覽器與服務器之間的通信數據量,從而提高了通信效率。
    HttpServlet類爲If-Modified-Since請求頭和Last-Modified頭字段的這種應用提供了處理機制,當繼承了 HttpServlet類的Servlet程序接收到一個GET方式的訪問請求時,HttpServlet中重載的service方法在調用doGet方 法之前,它還將調用getLastModified方法,並根據getLastModified方法的返回值來決定是否調用doGet方法和在響應消息中 是否生成Last-Modified頭字段,具體規則如下:

當getLastModified方法返回一個負數時,不管請求消息中的情況怎樣,service方法都將直接調用doGet方法來生成響應內容,這正是HTTPServlet類中定義的getLastModified方法的行爲;
    當 getLastModified方法返回一個正數,且請求消息中沒有包含If-Modified-Since請求頭時(這往往出現在第對某個資源的第一次 訪問時),或者請求消息中包含的If-Modified-Since請求頭中的時間值比getLastModified方法返回的時間值舊 時,service方法將根據getLastModified方法的返回值生成一個Last-Modified頭字段,然後調用doGet方法生成響應內 容;
    當getLastModified方法返回一個正數時,且請求消息中包含的If-Modified-Since請求頭中的時間值比 getLastModified方法返回的時間值新或者與之相同時,service方法將不調用doGet方法,而是向瀏覽器返回一個304(Not Modified)狀態碼錶示瀏覽器可以使用其以前緩存的內容。
動手體驗:揭開瀏覽器緩存的奧祕
(1)編寫一個名爲 CacheServlet的Servlet程序,在其doGet方法中向瀏覽器和Tomcat的命令行窗口中都打印出當前的時間 值,getLastModified方法也是向Tomcat的命令行窗口中打印出當前的時間值和返回當前時間值,這裏先將getLastModified 方法註釋掉,如例程4-9所示。

例程4-9  CacheServlet.java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class CacheServlet extends HttpServlet
{
   public void doGet(HttpServletRequest request,
     HttpServletResponse response) throws ServletException, IOException
   {
      PrintWriter out = response.getWriter();
      long now = System.currentTimeMillis();
      out.println("doGet:" + now);
      System.out.println("doGet:" + now);
   }
 
    /*protected long getLastModified(HttpServletRequest req)
     {
        long now = System.currentTimeMillis();
        System.out.println("getLastModified:" + now);
        return now;
       }*/
}

編譯CacheServlet.java文件,確保編譯後生成的class文件放置進了d:mywebWEB-INFclasses目錄中。


(2)在d:myweb目錄中編寫一個名稱爲CacheTest.html的網頁文件,如例程4-10所示。

例程4-10  CacheTest.html

緩存測試

(3)爲了便於查看瀏覽器如何生成緩存內容,最好是先刪除掉Internet臨時文件夾中保存的所有緩存內容。單擊IE瀏覽器的“工具 ”“Internet選項”菜單,打開如圖4.16所示的“Internet選項”對話框,接着再單擊“常規”選項卡中的“Internet臨時文件” 欄中的“刪除文件”按鈕,這就刪除了Internet臨時文件夾中保存的所有緩存內容。單擊“常規”選項卡中的“Internet臨時文件”欄中的“設置 ”按鈕,在打開的“設置”對話框框中單擊“Internet臨時文件夾”欄中的“查看文件”按鈕,打開如圖4.17所示的Internet臨時文件夾,可 以看到其中已經不再有任何緩存的文件。另外,在“設置”對話框框中還需要將“檢查所存網頁的較新版本”的功能項恢復爲默認的“自動”。
    在瀏覽器地址欄中輸入如下地址:
         http://localhost:8080/it315/CacheTest.html
    在瀏覽器窗口中顯示的結果頁面中,單擊“緩存測試”超鏈接訪問CacheServlet。再次打開Internet臨時文件夾,這時可以看到其中生成了剛 才訪問過的CacheTest.html和CacheServlet這兩個頁面的緩存文件,如圖4.18所示。選中其中的CacheServlet緩存文 件,從Windows資源管理器窗口中顯示出的摘要信息中可以看到,CacheServlet緩存文件中記錄的上次修改時間爲“無”。

 

(4)在命令行窗口中執行telnet 127.0.0.1 8080命令,連接上Tomcat WEB服務器後,接着在telnet程序命令窗口中,輸入如下內容:
                    GET /it315/servlet/CacheServlet HTTP/1.1<回車>
                     Host:<空格><回車>
                     <回車>
    telnet窗口中顯示出的結果如圖4.19所示。

CacheServlet返回的響應消息中沒有Last-Modified頭字段,這正是圖4.18中顯示的CacheServlet緩存文件的上次修改時間爲“無”的原因
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章