cache技術提高Web應用性能(Enhancing Web Application Performance with Caching)

在學習享元模式時,發現這篇文章寫的不錯,翻譯水平不高,有看到的同學請見諒。

原文鏈接:http://www.theserverside.com/tt/articles/article.tss?l=Caching

簡介

      內存資源是大型,繁忙應用的常見瓶頸。同時也是Web開發中最容易發生濫用和最容易獲益的地方。在很多情況下,有效的緩存策略不但可以降低內存覆蓋,也可以提高系統響應速度。緩存把最近使用的項目保留在內存中,預期它們還會被使用,因此是一個廣爲人知的優化技術。緩存可以有多種實現方式,包括正確的使用設計模式。

使用享元模式實現緩存

      享元模式出現在四人組的書中,這本書軟件開發中關於模式的重要工作。享元模式使用共享來支持大規模的細粒度對象引用。享元模式是一種讓你保持對象池有效,然後爲獨特視角創建對象引用的策略。它使用標準對象的主意。一個標準對象是一個單一代表性的對象,代表了所有同一類型的其他對象。例如,你擁有一個特別的產品,它代表了所有同類型的產品。在一個應用中,取而代之的就是爲每個用戶創建一個產品的列表,你創建一個標準產品列表,而每個用戶擁有該列表中對象的一個引用列表。

      《Art of Java Web Development》中的默認應用eMotherEarth設計成爲每個用戶創建一個產品列表。然而,這樣做是對內存的浪費。產品對所有用戶都是相同的,而且產品的屬性變化不是很頻繁。圖1表示了用戶和目錄中的產品列表當前的架構關係。

      caching_01 圖1 eMotherEarch應用中,每個用戶在查看目錄時擁有自己的產品列表。即使他們有不同的產品視圖,也仍然看到相同的產品列表

      爲每個用戶保留一個唯一列表造成內存浪費。儘管不同的用戶有不同的產品視圖,只有一個產品列表。用戶可以改變順序和目錄頁看到的產品頁的價值,但是基本的產品屬性對每個用戶是相同。一個更好的設計是創建一個標準產品列表,對每個用戶保留一個該列表的引用,這種用戶/產品關係見圖2.

      caching_02 圖2單一產品列表節省內存,爲每個用戶在給定時間看特定商品保留一個列表引用

      在這個場景中,每個用戶仍擁有特定產品集合的引用(爲維護分頁和排序),引用指回到標準產品列表。這個列表是應用中唯一的實際產品對象集合。它存儲在中心位置,被所有應用的用戶訪問。

實現享元模式

      eMotherEarth是Art of Java Web Development中突出的一個應用。它採用Model2 Web應用模型建模,很容易改爲使用享元模式。這裏展示的代碼以改變邊界和控制器類來實現緩存。可以在www.nealford.com/art.htm訪問書的源代碼下載該應用。第一步是創建標準產品列表並放在一個全局可訪問的位置。很顯然使用應用上下文。因此,eMotherEarth中的Welcome控制器被改爲創建產品列表並放在應用上下文中,Welcome控制器中的修改過的init()和新的buildFlyweightReferences()方法見如下:

   buildFlyweightReferences()方法首先檢查確保它沒有被其他用戶的welcome servlet調用創建。這個可能需要非常謹慎因爲init()方法在載入內存時只調用一次。
但是,如果我們把這段代碼放到doGet()或者doPost()方法就會被調用多次。這是一個足夠簡單的測試,它不會傷害到任何當前實現。如果這個標準列表還不存在,它就會
被創建,居中,放置在全局上下文。現在,當一個人用戶需要從目錄上查看產品,他們將從全局列表讀取。這個目錄控制器已經改變爲從全球緩存讀取產品列表供顯示而不是創
建一個新的列表。目錄控制器的doPost()方法如下:
    先前版本的目錄控制器調用一個方法來創建和佔據一個ProductDb邊界類。然而,這個版本是簡化了,因爲它可以安全的假設產品記錄已經存在於內存中。
因此,整個getProductBoundary()方法不再存在於程序這個版本。這是一種更少的代碼,更快的性能,並佔用內存少的少見情況!然而,另一個微小的變化是需要適應緩存。
在這以前,sortPagesForDisplay()方法如果沒有分類標準存在於請求參數中,它簡單的返回無序記錄。這個控制器被設計成在getProductListSlice()方法中返回標
準列表的一部分。

      以前,缺乏排序標準不引起任何問題,因爲每一位用戶都有主要列表的副本。這個方法返回用戶的列表的一個子集。然而,現在所有的用戶共享同一列表。這個來自集合API的subList()方法並不複製列表中的項目,而返回它們的引用。這是一個希望的特徵,因爲如果它複製了列表條目並返回,緩存列表的產品是無意義的。然而,因爲現在只有一個實際的列表,當用戶得到一些列表中記錄的引用並調用該該sortPagesForDisplay()方法,列表成員在頁面大小的塊中排序。

      先前的版本的sortPagesForDisplay()方法只當用戶在請求參數中指定比較器時(即當用戶在視圖中點擊一個列的標題)才調用排序方法。然而,如果這個實現保留的話,那麼一個新的用戶登錄進來後仍然會看到上個用戶對頁面記錄快的排序列表。這是因爲一個新的用戶沒有指定一個分類標準(換句話說,他們還沒有機會去點擊一列標題和生成的分類標誌)。緩存技術的副作用就是每一個用戶排序列表的產品在page-sized塊。儘管這對於一個給定的頁面大小的塊只是記錄位置的改變,每個用戶在他們看到的記錄之前運用自己的分類標準的對列表排序。在記錄數不多時,這種實現可以防止這種副作用而不會影響的性能。
使用這種設計模式是該控制器容易更新。用戶在排序之前選擇他們的記錄頁。如果排序在用戶選擇他們想要的記錄之前發生,該控制器必須改變。然而,用戶不會做出這樣的要求,他們將不得不猜得到的頁面記錄分類。

考慮享元模式

      享元模式作爲一種緩存機制很大程度上依賴於數據的特點:

  •       應用程序使用大量的對象
  •       爲多用戶複製對象的內存開銷很大
  •       對象不可變或者狀態可以被外化
  •       相對來說很少的共享對象可以取代很多對象組
  •       這個應用程序並不取決於對象的屬性。當用戶可能認爲他們是獲得一個唯一的對象,實際上從緩存取得引用。

      使用這種風格的緩存是關鍵是對象的狀態信息。在上面的例子中,對用戶而言產品對象是永恆不變。如果允許用戶修改對象,這種緩存機制不會工作。它依靠的是對象存儲在緩存的只讀性。也可以對可變對象使用享元模式,但是他們的一些狀態信息必須在對象外部。如圖3。

caching_03 圖3 通過添加外部信息連接產品對象與其引用,使得享元模式應用於可變對象。

      存儲可變對象需要對享元引用和享元對象之間的小的連接類的引用。一個很好的例子,這種類型的外部狀態信息在eMotherEarth就是特定的項目是重量。這是特別給用戶的信息,所以它不應被儲存在高速緩存中。然而,有一個每一個產品的離散塊信息。這種(或其他)會儲存在一個關聯類中。系在產品和引用之間。當你使用這個選項,和享元引用本身相比這些信息消耗很少的內存。否則,使用享元你不要保存任何資源。
當緩存中的對象改變很快時這種模式並不可取。當eMotherEarth產品每天修改多次,它就不是一個合適的緩存策略。然而,產品是入庫的這似乎是不可能的。當你在你的全部或大部分用戶之間共享不變對象時這種解決方案工作很好。當併發用戶越多內存節省越明顯。從而引出了一個很有趣的問題——你怎麼知道緩存有效呢?有許多新式的虛擬機、高速緩存不是必需的,因爲有高效的垃圾回收、內存管理器。測度這種改變是值得的。

效率測試

(略)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章