瀏覽器緩衝和HTTP協議

來源:互聯網
上載者:User

http協議中與cache相關的header主要有這麼幾個:
1.Pragma:只能取固定值no-cache,用於http/1.0裡面,禁止瀏覽器緩衝
2. Expires:用於指定當前緩衝的文檔在什麼時候被認為到期,GMT格式,超過了這個日期瀏覽器就不使用本機快取,而是像伺服器發出新的請求。
3.Cache-Control
no-cache:禁止瀏覽器和Proxy 伺服器緩衝,可以單獨指定不緩衝某些資訊頭,例如:no-cache=Set-Cookie不緩衝cookie資訊
no-store:專指不能緩衝到硬碟
max-age:xx秒以後文檔過時,可以代替Expires,如果同時出現,max-age優先
s-max-age:Proxy 伺服器中緩衝的文檔的有效期間
must-revalidate:對於客戶機的請求,Proxy 伺服器必須向伺服器驗證緩衝的文檔是否過時,總是擷取最新的文檔給客戶機
4.if-modified-since
當客戶機訪問一個已經緩衝的頁面時,只有該頁面自某個指定的時間以來發生過更改,才需要重新擷取該文檔。
if-modified-since用於指定這個時間,GMT格式。
5.last-modified:服務端設定的文檔的最後的更新日期。
6.if-match:定義判斷網頁到期的條件
伺服器給客戶機傳送網頁的時候,可以傳遞代表實體內容特徵的頭欄位(ETag),這種頭欄位被叫做實體標籤。當客戶機再次向服務端發請求的時候,
會使用if-match攜帶實體標籤資訊。
7.ETag:用於伺服器向用戶端傳送的代表實體內容特徵的標記資訊。
下面我們就通過具體的例子來看一下,這幾個header是如何控制瀏覽器的緩衝行文的:
1.    緩衝協商機制Last-Modified/If-Modified-Since
瀏覽器會緩衝瀏覽過的頁面,如果伺服器告訴了瀏覽器Last-Modified,瀏覽器在下次訪問緩衝的頁面的時候,就可以使用If-Modified-Since詢問伺服器可否使用本機快取,如果可以,伺服器會發送304狀態代碼。
但是,如果伺服器沒有輸出Last-Modified頭欄位,瀏覽器就不會發送If-Modified-Since頭。
我們先寫一個CacheServlet:
@WebServlet(urlPatterns={"/cacheservlet.html"}) 
public class CacheServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=gb2312");
        PrintWriter out = response.getWriter();
        out.print("hello,cache!");
        out.close();
    }
}
清一下瀏覽器的緩衝,訪問http://localhost:8080/cache/cacheservlet.html,可以看到輸出:hello,cache!
通過httpwatch,可以看到瀏覽器發出的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
服務端返回的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:30:19 GMT

hello,cache!
可以看到伺服器並沒有輸出last-modified頭欄位,再次重新整理瀏覽器,可以看到同樣的結果,瀏覽器也並沒有發送If-Modified-Since。開啟瀏覽器的緩衝:

我們可以看到,已經緩衝了cacheservlet.html這個頁面,但是,“上次修改時間”是“無”,“到期日”也是“無”。因此,無論怎麼重新整理,都是返回200.
下面我們修改一下我們的servlet,添加last-modified頭:
@WebServlet(urlPatterns={"/cacheservlet.html"}) 
public class CacheServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=gb2312");
        response.setDateHeader("Last-Modified", new Date().getTime());
        PrintWriter out = response.getWriter();
        out.print("hello,cache!");
        out.close();
    }
}
然偶我們清一下緩衝,重啟tomcat,再次訪問這個頁面,
瀏覽器的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:33:09 GMT
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:33:09 GMT

hello,cache!
可以看出來,這次輸出了Last-Modified頭,然後看一下緩衝:

 這次就有上次修改時間了,因為服務端輸出的last-modified是按照GMT格式的,瀏覽器顯示的東八區的,二者正好相差了8個小時。

這次我們再重新整理瀏覽器:
用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:33:09 GMT
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:34:29 GMT
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:34:29 GMT

hello,cache!
顯然,這次用戶端的請求就攜帶了If-Modified-Since頭,但是伺服器的預設實現中:
@Override
protected long getLastModified(HttpServletRequest req) {
        return super.getLastModified(req);
}
這個方法預設返回-1,因此,伺服器認為用戶端的緩衝到期,因此還是返回了200.
具體參考HttpServlet的源碼:
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        String method = req.getMethod();
        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 = req.getDateHeader(HEADER_IFMODSINCE);
                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);
                }
            }
        }
    …….
}
現在,我們讓瀏覽器快取頁面面60秒,修改CacheServlet:
@WebServlet(urlPatterns={"/cacheservlet.html"}) 
public class CacheServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        long clientLastModified = request.getDateHeader("If-Modified-Since");
        if(clientLastModified + 60 * 1000 > new Date().getTime()){//如果在60秒以內,直接返回304,,讓瀏覽器使用本機快取
            response.setStatus(304);
            return;
        }

        response.setContentType("text/html;charset=gb2312");
        response.setDateHeader("Last-Modified", new Date().getTime());
        PrintWriter out = response.getWriter();
        out.print("hello,cache!");
        out.close();
    }
}
清一下緩衝,重啟tomcat,再次訪問這個頁面,
用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:34:29 GMT
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:36:08 GMT
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:36:08 GMT

hello,cache!
重新整理頁面,用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:36:08 GMT
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 304 Not Modified
Server: Apache-Coyote/1.1
Date: Tue, 17 Apr 2012 07:36:08 GMT
可以看到,服務端返回304,瀏覽器就可以直接使用本機快取,一分鐘以後,再次重新整理:
用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:36:08 GMT
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:37:28 GMT
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:37:28 GMT

hello,cache!
雖然用戶端發送了If-Modified-Since頭,但是,因為已經超過了60秒,因此返回200.
2.    徹底的消滅請求-Expires
雖然瀏覽器的緩衝沒有到期的情況下,伺服器可以返回304,能節省頻寬提高伺服器的處理效率,但是,畢竟瀏覽器還是向伺服器發出了請求,能不能在不到期的時候,瀏覽器可以不詢問伺服器,直接使用本地的緩衝?答案是肯定的。Expires頭用來告訴瀏覽器,緩衝的頁面何時到期,在到期之前,瀏覽器可以直接使用本地的緩衝,而不需要詢問伺服器。
修改我們的CacheServlet:
@WebServlet(urlPatterns={"/cacheservlet.html"}) 
public class CacheServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        long clientLastModified = request.getDateHeader("If-Modified-Since");
        if(clientLastModified + 60 * 1000 > new Date().getTime()){
            response.setStatus(304);
            return;
        }
        response.setContentType("text/html;charset=gb2312");
        response.setDateHeader("Last-Modified", new Date().getTime());
        response.setDateHeader("Expires", new Date().getTime() + 60 * 1000);//60秒之後,瀏覽器的緩衝到期
        PrintWriter out = response.getWriter();
        out.print("hello,cache!");
        out.close();
    }
}
清一下緩衝,重啟tomcat,再次訪問這個頁面,
用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:44:39 GMT
Expires: Tue, 17 Apr 2012 07:45:39 GMT
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:44:39 GMT

hello,cache!
瀏覽器的緩衝:

可以看出來,這次的到期日已經不再是無。
F5重新整理瀏覽器,還是會向伺服器發請求,而不是直接使用本地的緩衝,
用戶端請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:44:39 GMT
Host: localhost:8080
Connection: Keep-Alive
服務端響應:
HTTP/1.1 304 Not Modified
Server: Apache-Coyote/1.1
Date: Tue, 17 Apr 2012 07:44:40 GMT

但是,如果是直接在瀏覽器按斷行符號,會顯示:

 也就是說是直接取的本機快取,而並沒有與伺服器互動。

到此為止,似乎很完美,但是,有這麼兩個問題:(1)瀏覽器安全色問題,有的瀏覽器不支援Expires頭(2)伺服器的時間很可能和用戶端的時間不一致,如果用戶端的時間落後於伺服器的時間一個小時的話,如果伺服器的expires設定為1個小時之內,就都沒有用,這確實是個問題!很幸運的是,http還提供了另一個頭叫做:Cache-Control,他可以設定緩衝的時間,用秒作單位,這個時間是相對於用戶端的瀏覽器的。
我們再次修改我們的CacheServlet:
@WebServlet(urlPatterns={"/cacheservlet.html"}) 
public class CacheServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        long clientLastModified = request.getDateHeader("If-Modified-Since");
        if(clientLastModified + 60 * 1000 > new Date().getTime()){
            response.setStatus(304);
            return;
        }
        response.setContentType("text/html;charset=gb2312");
        response.setDateHeader("Last-Modified", new Date().getTime());
        response.setDateHeader("Expires", new Date().getTime() + 60 * 1000);
        response.setHeader("Cache-Control", "max-age="+60);//告訴瀏覽器緩衝60秒
        PrintWriter out = response.getWriter();
        out.print("hello,cache!");
        out.close();
    }
}
清一下緩衝,重啟tomcat,再次訪問這個頁面,
用戶端的請求:
GET /cache/cacheservlet.html HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2)
Accept-Encoding: gzip, deflate
If-Modified-Since: Tue, 17 Apr 2012 07:44:39 GMT
Host: localhost:8080
Connection: Keep-Alive服務端的響應:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Tue, 17 Apr 2012 07:50:42 GMT
Expires: Tue, 17 Apr 2012 07:51:42 GMT
Cache-Control: max-age=60
Content-Type: text/html;charset=gb2312
Content-Length: 12
Date: Tue, 17 Apr 2012 07:50:42 GMT

hello,cache!
已經輸出了Cache-Control頭.
幾種不同的重新整理之間的區別:
(1)    ctrl+F5:強制重新整理,不管瀏覽器有沒有快取頁面面,都會向伺服器發送請求,而且不會發送If-modified-since頭。
(2)    F5:會使用If-modified-since進行緩衝協商,但是expires無效
(3)    在地址欄按斷行符號和點擊頁面超連結,會進行協商,而且expires頭有效。
服務端頁面緩衝:
前面說的都是瀏覽器端的緩衝,通過使用諸如velocity或者freemarker之類模板技術,很容易就能夠實現服務端的頁面緩衝,大體的思路應該是這樣:
當controller收到一個請求的時候:
1.尋找memcache裡面是否緩衝過與這個url匹配的ResponseContent對象
2.如果沒有找到,就調用action的方法,然後把產生的ResponseContent存放到memcache裡面
3.如果能找到,說明服務端緩衝可用
3.1如果用戶端緩衝可用(根據last-modified判斷),直接返回304,使用用戶端緩衝
3.2如果用戶端緩衝失效,使用服務端緩衝的ResponseContent,輸出到用戶端。
但是,請注意,使用了緩衝以後,是無法保證頁面的即時性的。
【注】
(1)在做實驗的時候,請設定瀏覽器的檢查網頁更新為自動。

 (2)IE瀏覽器比較笨,它不會緩衝沒有尾碼名或者尾碼名是.do的檔案(我的是IE8),因此,在做實驗的時候,需要把Servlet的映射路徑變為html的樣子,否則,很有可能實驗失敗。

(3)其他與緩衝相關的http頭還有:
Pragma,ETag/If-None-Match
(4)    禁止瀏覽器緩衝
Pragma:no-cache             // HTTP 1.0的不緩衝回應標頭
Expires:0                
Cache-Control :no-cache     
Cache-Control :no-store       //該設定是防止Firefox緩衝

參考:構建高效能WEB網站 郭欣,深入體驗javaweb開發內幕-核心基礎 張孝祥

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.