基於資源的HTTP Cache的實現介紹

來源:互聯網
上載者:User

標籤:style   blog   http   color   java   os   使用   io   for   

我們都知道瀏覽器會緩衝訪問過網站的網頁,瀏覽器通過URL地址訪問一個網頁,顯示網頁內容的同時會在電腦上面緩衝網頁內容。如果網頁沒有更新的話,瀏覽器再次訪問這個URL地址的時候,就不會再次下載網頁,而是直接使用本機快取的網頁。只有當網站明確標識資源已經更新,瀏覽器才會再次下載網頁。 

一、什麼是HTTP Cache 

對於瀏覽器的這種網頁緩衝機制大家已經耳熟能詳了,舉個例子來說,JavaEye的新聞訂閱地址:http://www.iteye.com/rss/news , 當瀏覽器或者訂閱程式訪問這個URL地址的時候,JavaEye的伺服器在response的header裡面會發送給瀏覽器如下狀態標識:

C代碼  

  1. Etag    "427fe7b6442f2096dff4f92339305444"  

  2. Last-Modified   Fri, 04 Sep 2009 05:55:43 GMT  



這就是告訴瀏覽器,新聞訂閱這個網路資源的最後修改時間和Etag。於是瀏覽器把這兩個狀態資訊連同網頁內容在本地進行緩衝,當瀏覽器再次訪問JavaEye新聞訂閱地址的時候,瀏覽器會發送如下兩個狀態標識給JavaEye伺服器: 

C代碼  

  1. If-None-Match   "427fe7b6442f2096dff4f92339305444"  

  2. If-Modified-Since   Fri, 04 Sep 2009 05:55:43 GMT  



就是告訴伺服器,我本機快取的網頁最後修改時間和Etag是什麼,請問你伺服器的資源有沒有在我上次訪問之後有更新啊?於是JavaEye伺服器會核對一下,如果該使用者上次訪問之後沒有更新過新聞,那麼根本就不必產生這個RSS了,直接告訴瀏覽器:“沒什麼新東西,你還是看自己緩衝的網頁吧”,於是伺服器就發送一個304 Not Modified的訊息,其他什麼都不用幹了。 

這就是HTTP層的Cache,使用這種基於資源的緩衝機制,不但大大節省伺服器程式資源,而且還減少了網頁下載次數,節約了很多網路頻寬。 

二、HTTP Cache究竟有什麼作用? 

我們通常的動態網站編程,伺服器端程式根本就不去處理瀏覽器發送過來的If-None-Match和If-Modified-Since狀態標識,只要有請求就產生網頁發送給瀏覽器。對於一般情況來說,使用者不會總是沒完沒了重新整理一個頁面,所以大家並不認為這種基於資源的緩衝有什麼太大的作用,但實際情況並非如此: 

1、像Google這種比較智能的網路爬蟲可以有效識別資源的狀態資訊,如果使用這種緩衝機制,可以大大減少爬蟲的爬取次數。 

比方說Google每天爬JavaEye網站大概15萬次左右,但實際上JavaEye每天有更新的內容不會超過1萬個網頁。因為很多內容更新比較快,因此Google就會反覆不停的爬取,這樣本身就造成了很多資源的浪費。如果我們使用HTTP Cache,那麼只有當網頁內容發生改變的時候,才會真正進行爬取,其他時候我們直接告訴Google的爬蟲304 Not Modified就可以了。這樣不但降低了伺服器本身的負載和爬蟲造成的網路頻寬消耗,實際上也大大提高了Google爬蟲的工作效率,豈不是皆大歡喜? 

2、很多內容更新不頻繁的網頁,儘管使用者不會頻繁的重新整理,但是從一個比較長的時間段來看使用HTTP Cache,仍然可以起到很大的緩衝作用。 

比方說一些曆史討論文章,已經過去了幾個月了,這些文章內容很少更新。使用者可能通過搜尋,收藏連結,文章關聯等方式時不時訪問到這個頁面。那麼只要使用者訪問過一次以後,後續所有訪問伺服器直接發送304 Not Modified就可以了,不用真正產生頁面。 

3、對於曆史文章使用HTTP Cache可以避免爬蟲反覆的爬取。 

比方說JavaEye的論壇貼文清單頁面,分頁到20頁後面的文章已經很少有人直接存取了,但是從伺服器日誌去看,每天仍然有大量爬蟲反覆爬取這些分頁到很後面的頁面。這些頁面由於使用者很少去點擊,所以基本上沒有被應用程式的memcached緩衝住,每次訪問都會造成很高的資源消耗,爬蟲隔一段時間就爬一次,對伺服器是很大的負擔。如果使用了HTTP Cache,那麼只要爬蟲爬過一次以後,以後無論爬蟲爬多少次,都可以直接返回304 Not Modified了,極大的節省了伺服器的負載。 

三、如何在應用程式裡面使用HTTP Cache 

如果我們要在自己的程式裡面實現HTTP Cache,是件非常簡單的事情,特別是對Rails來說只需要添加一點點代碼,以上面的JavaEye新聞訂閱來說,只要添加一行代碼: 

Ruby代碼  

  1. def news  

  2.   fresh_when(:last_modified => News.last.created_at, :etag => News.last)  

  3. end  



用最新新聞文章作為Etag,該文章最後修改時間作為資源的最後修改時間,這樣就OK了。如果瀏覽器發送過來的標識和伺服器標識一致,說明內容沒有更新,直接發送304 Not Modified;如果不一致,說明內容更新,瀏覽器本地的緩衝太古老了,那麼就需要伺服器真正產生頁面了。 

以上只是一個最簡單的例子,如果我們需要根據狀態做一些更多的工作也是很容易的。比方說JavaEye部落格的RSS訂閱地址: http://robbin.iteye.com/rss 

Ruby代碼  

  1. @blogs  = @blog_owner.last_blogs  

  2. @hash  = @blogs .collect{|b| {b.id => b.post.modified_at.to_i + b.posts_count}}.hash  

  3. if stale?(:last_modified => (@blog_owner.last_blog.post.modified_at || @blog_owner.last_blog.post.created_at), :etag => @hash)  

  4.   render :template => "rss/blog"  

  5. end  



這個實現稍微複雜一些。我們需要判斷部落格訂閱所有的輸出文章是否有更新,所以我們用部落格文章內容最後修改時間和部落格的評論數量做一個hash,然後用這個hash值作為資源的Etag,那麼只要這些部落格文章當中任何文章內容被修改,或者有新評論,都會改變Etag值,從而通知瀏覽器內容有更新了。 

除了RSS訂閱之外,JavaEye網站還有很多地方適合使用HTTP Cache,比方說JavaEye論壇的版面列表頁面,一些經常喜歡泡論壇的使用者,可能時不時會上來重新整理一下版面, 看看有沒有新的文章,那麼我們就不必每次使用者請求的時候都去執行程式,產生頁面給他。我們判斷一下如果沒有新文章的話,直接告訴他304 Not Modified就可以了,在沒有使用HTTP Cache之前的版面Action代碼: 

Ruby代碼  

  1. def board  

  2.   @topics = @forum.topics.paginate...  

  3.   @announcements = (params[:page] || 1).to_i == 1 ? Topic.find :all, :conditions => ...  

  4.   render :action => ‘show‘  

  5. end  



添加HTTP Cache以後,代碼如下: 

Ruby代碼  

  1. def board  

  2.   @topics = @forum.topics.paginate...  

  3.   if logged_in? || stale?(:last_modified => @topics[0].last_post.created_at, :etag => @topics.collect{|t| {t.id => t.posts_count}}.hash)  

  4.     @announcements = (params[:page] || 1).to_i == 1 ? Topic.find :all, :conditions...  

  5.     render :action => ‘show‘  

  6.   end  

  7. end  



對於登入使用者,不使用HTTP Cache,這是因為登入使用者需要即時接收站內簡訊通知和訂閱通知,因此我們只能對匿名使用者使用HTTP Cache,然後我們使用當前所有文章id和回帖數構造hash作Etag,這樣只要當前分頁列表頁面有任何文章發生改變或者有了新回帖,就更新頁面,否則就不必重建頁面。 

論壇文章頁面實際上也可以使用HTTP Cache,只不過Etag的hash演算法稍微複雜一些,需要保證文章的任何改動都要引起hash值的改變,範例程式碼如下: 

Ruby代碼  

  1. def show  

  2.   @topic = Topic.find params[:id]  

  3.   user_session.update_.......  if logged_in?  

  4.   Topic.increment_counter(...) if ......  

  5.   @posts = @topic.post_by_page params[:page]  

  6.   posts_hash = @posts.collect{|p| {p.id => p.modified_at}}.hash  

  7.   topic_hash = @topic.forum_id + @topic.sys_tag_id.to_i + @topic.title.hash + @topic.status_flag.hash  

  8.   ad_hash = ...  (廣告的hash演算法,略)  

  9.   if logged_in? || stale?(:etag => [posts_hash, topic_hash, ad_hash])  

  10.     render  

  11.   end    

  12. end  



要分別根據主題貼,該分頁的所有回帖和文章頁面的廣告內容進行hash,計算出來一個唯一的Etag值,保證任何改動都會產生新的Etag,這樣就搞定了,是不是很簡單!這種文章的緩衝非常有效,可以避免Rails去render頁面和下載頁面,極大的減輕了伺服器負載和頻寬。 

再舉一個需求比較特殊的例子:對於知識庫搜尋相關文章的推薦頁面,比方說:http://www.iteye.com/wiki/topic/462476,也就是本文的相關文章推薦內容,我們並不希望使用者和爬蟲每次訪問這個頁面都實際執行一遍全文檢索索引,然後構造頁面內容,在一個相對不長的時間範圍內,這篇文章的相關推薦文章改變的機率不大,因此我們希望比方說5天之內,使用者重複訪問該頁面,就直接返回304 Not Modified,那麼Rails沒有直接的設施給我們使用,需要我們稍微瞭解一些Rails的機制,自己編寫,程式碼範例如下: 

Ruby代碼  

  1. def topic  

  2.   @topic = Topic.find(params[:id])  

  3.   unless logged_in?  

  4.     if request.not_modified?(5.days.ago)  

  5.       head :not_modified  

  6.     else  

  7.       response.last_modified = Time.now  

  8.     end  

  9.   end  

  10. end  



每次使用者請求,我們判斷使用者是否5天之內訪問過該頁面,如果訪問過,直接返回304 Not Modified,如果沒有訪問過,或者上次訪問已經超過了5天,那麼設定最近修改時間為目前時間,然後產生頁面給使用者。是不是很簡單? 

在給JavaEye網站所有的RSS訂閱輸出添加了HTTP Cache以後,通過一天的觀察發現,超過一半的RSS訂閱請求已經被緩衝了,直接返回304 Not Modified,所以效果非常明顯,由於JavaEye網站每天RSS訂閱的動態請求就超過了10萬次,因此添加HTTP Cache可以減輕不少伺服器的負擔和頻寬消耗。除此之外,新聞文章頁面,整個論壇頻道,知識庫相關推薦文章頁面都可以添加HTTP Cache,粗粗計算下來,JavaEye這些頁面統統使用HTTP Cache以後,網站整體效能至少可以提高10%。


基於資源的HTTP Cache的實現介紹

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.