Ehcache是現在最流行的純Java開源緩衝架構__Java

來源:互聯網
上載者:User


Ehcache是現在最流行的純Java開源緩衝架構,配置簡單、結構清晰、功能強大,最初知道它,是從Hibernate的緩衝開始的。網上中文的EhCache材料以簡單介紹和配置方法居多,如果你有這方面的問題,請自行google;對於API,官網上介紹已經非常清楚,請參見官網;但是很少見到特性說明和對實現原理的分析,因此在這篇文章裡面,我會詳細介紹和分析EhCache的特性,加上一些自己的理解和思考,希望對緩衝感興趣的朋友有所收穫。

 

一、特性一覽,來自官網,簡單翻譯一下:

 

1、快速輕量
過去幾年,諸多測試表明Ehcache是最快的Java緩衝之一。
Ehcache的線程機制是為大型高並發系統設計的。
大量效能測試用例保證Ehcache在不同版本間效能表現得一致性。
很多使用者都不知道他們正在用Ehcache,因為不需要什麼特別的配置。
API便於使用,這就很容易部署上線和運行。
很小的jar包,Ehcache 2.2.3才668kb。
最小的依賴:唯一的依賴就是SLF4J了。

2、伸縮性
緩衝在記憶體和磁碟儲存可以伸縮到數G,Ehcache為大資料存放區做過最佳化。
大記憶體的情況下,所有進程可以支援數百G的吞吐。
為高並發和大型多CPU伺服器做最佳化。
安全執行緒和效能總是一對矛盾,Ehcache的線程機制設計採用了Doug Lea的想法來獲得較高的效能。
單台虛擬機器上支援多緩衝管理器。
通過Terracotta伺服器矩陣,可以伸縮到數百個節點。

3、靈活性
Ehcache 1.2具備對象API介面和可序列化API介面。
不能序列化的對象可以使用除磁碟儲存外Ehcache的所有功能。
除了元素的返回方法以外,API都是統一的。只有這兩個方法不一致:getObjectValue和getKeyValue。這就使得緩衝對象、序列化對象來擷取新的特性這個過程很簡單。
支援基於Cache和基於Element的到期策略,每個Cache的存活時間都是可以設定和控制的。
提供了LRU、LFU和FIFO緩衝淘汰演算法,Ehcache 1.2引入了最少使用和先進先出緩衝淘汰演算法,構成了完整的緩衝淘汰演算法。
提供記憶體和磁碟儲存,Ehcache和大多數緩衝解決方案一樣,提供高效能的記憶體和磁碟儲存。
動態、運行時緩衝配置,存活時間、空閑時間、記憶體和磁碟存放緩衝的最大數目都是可以在運行時修改的。

4、標準支援
Ehcache提供了對JSR107 JCACHE API最完整的實現。因為JCACHE在發布以前,Ehcache的實現(如net.sf.jsr107cache)已經發布了。
實現JCACHE API有利於到未來其他緩衝解決方案的可移植性。
Ehcache的維護者Greg Luck,正是JSR107的專家委員會委員。

5、可擴充性
監聽器可以外掛程式化。Ehcache 1.2提供了CacheManagerEventListener和CacheEventListener介面,實現可以外掛程式化,並且可以在ehcache.xml裡配置。
節點發現,冗餘器和監聽器都可以外掛程式化。
分布式緩衝,從Ehcache 1.2開始引入,包含了一些權衡的選項。Ehcache的團隊相信沒有什麼是萬能的配置。
實現者可以使用內建的機制或者完全自己實現,因為有完整的外掛程式開發指南。
緩衝的可擴充性可以外掛程式化。建立你自己的緩衝擴充,它可以持有一個緩衝的引用,並且綁定在緩衝的生命週期內。
緩衝載入器可以外掛程式化。建立你自己的緩衝載入器,可以使用一些非同步方法呼叫來載入資料到緩衝裡面。
緩衝異常處理器可以外掛程式化。建立一個異常處理器,在異常發生的時候,可以執行某些特定操作。

6、應用持久化
在VM重啟後,持久化到磁碟的儲存可以複原資料。
Ehcache是第一個引入快取資料持久化儲存的開源Java緩衝架構。緩衝的資料可以在機器重啟後從磁碟上重新獲得。
根據需要將緩衝刷到磁碟。將緩衝條目刷到磁碟的操作可以通過cache.flush()方法來執行,這大大方便了Ehcache的使用。

7、監聽器
緩衝管理器監聽器。允許註冊實現了CacheManagerEventListener介面的監聽器:
notifyCacheAdded()
notifyCacheRemoved()
緩衝事件監聽器。允許註冊實現了CacheEventListener介面的監聽器,它提供了許多對緩衝事件發生後的處理機制:
notifyElementRemoved/Put/Updated/Expired

8、開啟JMX
Ehcache的JMX功能是預設開啟的,你可以監控和管理如下的MBean:
CacheManager、Cache、CacheConfiguration、CacheStatistics

9、分布式緩衝
從Ehcache 1.2開始,支援高效能的分布式緩衝,兼具靈活性和擴充性。
分布式緩衝的選項包括:
通過Terracotta的緩衝叢集:設定和使用Terracotta模式的Ehcache緩衝。緩衝發現是自動完成的,並且有很多選項可以用來調試緩衝行為和效能。
使用RMI、JGroups或者JMS來冗餘快取資料:節點可以通過多播或發現者手動設定。狀態更新可以通過RMI串連來非同步或者同步完成。
Custom:一個綜合的外掛程式機制,支援發現和複製的能力。
可用的緩衝複製選項。支援的通過RMI、JGroups或JMS進行的非同步或同步的緩衝複製。
可靠的分發:使用TCP的內建分發機制。
節點發現:節點可以手動設定或者使用多播自動探索,並且可以自動添加和移除節點。對於多播阻塞的情況下,手動設定可以很好地控制。
分布式緩衝可以任意時間加入或者離開叢集。緩衝可以配置在初始化的時候執行引導程式員。
BootstrapCacheLoaderFactory抽象工廠,實現了BootstrapCacheLoader介面(RMI實現)。
快取服務端。Ehcache提供了一個Cache Server,一個war包,為絕大多數web容器或者是獨立的伺服器提供支援。
快取服務端有兩組API:面向資源的RESTful,還有就是SOAP。用戶端沒有實現語言的限制。
RESTful快取服務器:Ehcached的實現嚴格遵循RESTful面向資源的架構風格。
SOAP快取服務端:Ehcache RESTFul Web Services API暴露了單例的CacheManager,他能在ehcache.xml或者IoC容器裡面配置。
標準服務端包含了內嵌的Glassfish web容器。它被打成了war包,可以任意部署到支援Servlet 2.5的web容器內。Glassfish V2/3、Tomcat 6和Jetty 6都已經經過了測試。

10、搜尋
標準分布式搜尋使用了流式查詢介面的方式,請參閱文檔。

11、Java EE和應用緩衝
為普通緩衝情境和模式提供高品質的實現。
阻塞緩衝:它的機制避免了複製進程並行作業的問題。
SelfPopulatingCache在緩衝一些開銷昂貴操作時顯得特別有用,它是一種針對讀取最佳化的緩衝。它不需要調用者知道緩衝元素怎樣被返回,也支援在不阻塞讀的情況下重新整理緩衝條目。
CachingFilter:一個抽象、可擴充的cache filter。
SimplePageCachingFilter:用於緩衝基於request URI和Query String的頁面。它可以根據HTTP request header的值來選擇採用或者不採用gzip壓縮方式將頁面發到瀏覽器端。你可以用它來緩衝整個Servlet頁面,無論你採用的是JSP、velocity,或者其他的頁面渲染技術。
SimplePageFragmentCachingFilter:快取頁面面片段,基於request URI和Query String。在JSP中使用jsp:include標籤包含。
已經使用Orion和Tomcat測試過,相容Servlet 2.3、Servlet 2.4規範。
Cacheable命令:這是一種老的命令列模式,支援非同步行為、容錯。
相容Hibernate,相容Google App Engine。
基於JTA的事務支援,支援事務資源管理,二階段提交和復原,以及本地事務。

12、開源協議
Apache 2.0 license

 

二、Ehcache的載入模組列表,他們都是獨立的庫,每個都為Ehcache添加新的功能,可以在此下載:

  ehcache-core:API,標準緩衝引擎,RMI複製和Hibernate支援 ehcache:分布式Ehcache,包括Ehcache的核心和Terracotta的庫 ehcache-monitor:企業級監控和管理 ehcache-web:為Java Servlet Container提供緩衝、gzip壓縮支援的filters ehcache-jcache:JSR107 JCACHE的實現 ehcache-jgroupsreplication:使用JGroup的複製 ehcache-jmsreplication:使用JMS的複製 ehcache-openjpa:OpenJPA外掛程式 ehcache-server:war內部署或者單獨部署的RESTful cache server ehcache-unlockedreadsview:允許Terracotta cache的無鎖讀 ehcache-debugger:記錄RMI分布式呼叫事件 Ehcache for Ruby:Jruby and Rails支援

Ehcache的結構設計概覽:

三、核心定義

 

cache manager:緩衝管理器,以前是只允許單例的,不過現在也可以多執行個體了

cache:緩衝管理器內可以放置若干cache,存放資料的實質,所有cache都實現了Ehcache介面

element:單條快取資料的組成單位

system of record(SOR):可以取到真實資料的組件,可以是真正的商務邏輯、外部介面調用、存放真實資料的資料庫等等,緩衝就是從SOR中讀取或者寫入到SOR中去的。

 

程式碼範例: Java代碼   CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");   manager.addCache("testCache");   Cache test = singletonManager.getCache("testCache");   test.put(new Element("key1", "value1"));   manager.shutdown();  

當然,也支援這種類似DSL的配置方式,配置都是可以在運行時動態修改的: Java代碼   Cache testCache = new Cache(     new CacheConfiguration("testCache", maxElements)       .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU)       .overflowToDisk(true)       .eternal(false)       .timeToLiveSeconds(60)       .timeToIdleSeconds(30)       .diskPersistent(false)       .diskExpiryThreadIntervalSeconds(0));  

事務的例子: Java代碼   Ehcache cache = cacheManager.getEhcache("xaCache");   transactionManager.begin();   try {       Element e = cache.get(key);       Object result = complexService.doStuff(element.getValue());       cache.put(new Element(key, result));       complexService.doMoreStuff(result);       transactionManager.commit();   } catch (Exception e) {       transactionManager.rollback();   }  

 

四、一致性模型

 

說到一致性,資料庫的一致性是怎樣的。不妨先來回顧一下資料庫的幾個隔離等級:

未提交讀(Read Uncommitted):在讀資料時不會檢查或使用任何鎖。因此,在這種隔離等級中可能讀取到沒有提交的資料。會出現髒讀、不可重複讀取、幻象讀。
已提交讀(Read Committed):唯讀取提交的資料並等待其他事務釋放獨佔鎖定。讀資料的共用鎖定在讀操作完成後立即釋放。已提交讀是資料庫的預設隔離等級。會出現不可重複讀取、幻象讀。
可重複讀(Repeatable Read):像已提交讀層級那樣讀資料,但會保持共用鎖定直到事務結束。會出現幻象讀。
可序列化(Serializable):工作方式類似於可重複讀。但它不僅會鎖定受影響的資料,還會鎖定這個範圍,這就阻止了新資料插入查詢所涉及的範圍。

 

基於以上,再來對比思考下面的一致性模型:

 

1、強一致性模型:系統中的某個資料被成功更新(事務成功返回)後,後續任何對該資料的讀取操作都得到更新後的值。這是傳統關聯式資料庫提供的一致性模型,也是關聯式資料庫深受人們喜愛的原因之一。強一致性模型下的效能消耗通常是最大的。

 

2、弱一致性模型:系統中的某個資料被更新後,後續對該資料的讀取操作得到的不一定是更新後的值,這種情況下通常有個“不一致性時間視窗”存在:即資料更新完成後在經過這個時間視窗,後續讀取操作就能夠得到更新後的值。

 

3、最終一致性模型:屬於弱一致性的一種,即某個資料被更新後,如果該資料後續沒有被再次更新,那麼最終所有的讀取操作都會返回更新後的值。

 

最終一致性模型包含如下幾個必要屬性,都比較好理解:

  讀寫一致:某線程A,更新某條資料以後,後續的訪問全部都能取得更新後的資料。 會話內一致:它本質上和上面那一條是一致的,某使用者更改了資料,只要會話還存在,後續他取得的所有資料都必須是更改後的資料。 單調讀一致:如果一個進程可以看到當前的值,那麼後續的訪問不能返回之前的值。 單調寫一致:對同一進程內的寫行為必須是保序的,否則,寫完畢的結果就是不可預期的了。

4、Bulk Load:這種模型是基於批量載入資料到緩衝裡面的情境而最佳化的,沒有引入鎖和常規的淘汰演算法這些降低效能的東西,它和最終一致性模型很像,但是有批量、高速寫和弱一致性保證的機制。

 

這樣幾個API也會影響到一致性的結果:

 

1、顯式鎖(Explicit Locking):如果我們本身就配置為強一致性,那麼自然所有的快取作業都具備事務性質。而如果我們配置成最終一致性時,再在外部使用顯式鎖API,也可以達到事務的效果。當然這樣的鎖可以控製得更細粒度,但是依然可能存在競爭和線程阻塞。

 

2、無鎖可閱讀檢視(UnlockedReadsView):一個允許髒讀的decorator,它只能用在強一致性的配置下,它通過申請一個特殊的寫鎖來比完全的強一致性配置提升效能。

舉例如下,xml配置為強一致性模型: Xml代碼   <cache name="myCache"        maxElementsInMemory="500"        eternal="false"        overflowToDisk="false"      <terracotta clustered="true" consistency="strong" />   </cache>  

但是使用UnlockedReadsView: Java代碼   Cache cache = cacheManager.getEhcache("myCache");   UnlockedReadsView unlockedReadsView = new UnlockedReadsView(cache, "myUnlockedCache");  

 

3、原子方法(Atomic methods):方法執行是原子化的,即CAS操作(Compare and Swap)。CAS最終也實現了強一致性的效果,但不同的是,它是採用樂觀鎖而不是悲觀鎖來實現的。在樂觀鎖機制下,更新的操作可能不成功,因為在這過程中可能會有其他線程對同一條資料進行變更,那麼在失敗後需要重新執行更新操作。現代的CPU都支援CAS原語了。 Java代碼   cache.putIfAbsent(Element element);   cache.replace(Element oldOne, Element newOne);   cache.remove(Element);  

 

五、緩衝拓撲類型

 

1、獨立緩衝(Standalone Ehcache):這樣的緩衝應用節點都是獨立的,互相不通訊。

 

2、分布式緩衝(Distributed Ehcache):資料存放區在Terracotta的伺服器陣列(Terracotta Server Array,TSA)中,但是最近使用的資料,可以儲存在各個應用節點中。

 

邏輯視角:


L1緩衝就在各個應用節點上,而L2緩衝則放在Cache Server陣列中。

 

組網視角:

 

模型儲存視角:


L1級緩衝是沒有持久化儲存的。另外,從快取資料量上看,server端遠大於應用節點。

 

3、複製式緩衝(Replicated Ehcache):快取資料時同時存放在多個應用節點的,資料複製和失效的事件以同步或者非同步形式在各個叢集節點間傳播。上述事件到來時,會阻塞寫線程的操作。在這種模式下,只有弱一致性模型。

 

它有如下幾種事件傳播機制:RMI、JGroups、JMS和Cache Server。

 

RMI模式下,所有節點全部對等:

 

JGroup模式:可以配置單播或者多播,協議棧和配置都非常靈活。

  Xml代碼   <cacheManagerPeerProviderFactory   class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"   properties="connect=UDP(mcast_addr=231.12.21.132;mcast_port=45566;):PING:   MERGE2:FD_SOCK:VERIFY_SUSPECT:pbcast.NAKACK:UNICAST:pbcast.STABLE:FRAG:pbcast.GMS"   propertySeparator="::"   />  

 

 

JMS模式:這種模式的核心就是一個訊息佇列,每個應用節點都訂閱預先定義好的主題,同時,節點有元素更新時,也會發布更新元素到主題中去。JMS規範實現者上,Open MQ和Active MQ這兩個,Ehcache的相容性都已經測試過。

Cache Server模式:這種模式下存在主從節點,通訊可以通過RESTful的API或者SOAP。

無論上面哪個模式,更新事件又可以分為updateViaCopy或updateViaInvalidate,後者只是發送一個到期訊息,效率要高得多。

複製式緩衝容易出現資料不一致的問題,如果這成為一個問題,可以考慮使用資料同步分發的機制。

 

即便不採用分布式緩衝和複製式緩衝,依然會出現一些不好的行為,比如:

 

緩衝漂移(Cache Drift):每個應用節點只管理自己的緩衝,在更新某個節點的時候,不會影響到其他的節點,這樣資料之間可能就不同步了。這在web會話資料緩衝中情況尤甚。

 

資料庫瓶頸(Database Bottlenecks ):對於單一實例的應用來說,緩衝可以保護資料庫的讀風暴;但是,在叢集的環境下,每一個應用節點都要定期保持資料最新,節點越多,要維持這樣的情況對資料庫的開銷也越大。

 

六、儲存方式

 

1、堆記憶體儲:速度快,但是容量有限。

 

2、堆外(OffHeapStore)儲存:被稱為BigMemory,只在企業版本的Ehcache中提供,原理是利用nio的DirectByteBuffers實現,比儲存到磁碟上快,而且完全不受GC的影響,可以保證回應時間的穩定性;但是direct buffer的在分配上的開銷要比heap buffer大,而且要求必須以位元組數組方式儲存,因此對象必須在預存程序中進行序列化,讀取則進行還原序列化操作,它的速度大約比堆記憶體儲慢一個數量級。

(註:direct buffer不受GC影響,但是direct buffer歸屬的的JAVA對象是在堆上且能夠被GC回收的,一旦它被回收,JVM將釋放direct buffer的堆外空間。)

 

3、磁碟儲存。

 

七、緩衝使用模式

 

cache-aside:直接操作。先詢問cache某條快取資料是否存在,存在的話直接從cache中返回資料,繞過SOR;如果不存在,從SOR中取得資料,然後再放入cache中。

  Java代碼   public V readSomeData(K key)    {      Element element;      if ((element = cache.get(key)) != null) {          return element.getValue();      }      if (value = readDataFromDataStore(key)) != null) {          cache.put(new Element(key, value));      }       return value;   }  

 

cache-as-sor:結合了read-through、write-through或write-behind操作,通過給SOR增加了一層代理,對外部應用訪問來說,它不用區別資料是從緩衝中還是從SOR中取得的。

read-through。

write-through。

write-behind(write-back):既將寫的過程變為非同步,又進一步延遲寫入資料的過程。

 

 

Copy Cache的兩個模式:CopyOnRead和CopyOnWrite。

CopyOnRead指的是在讀快取資料的請求到達時,如果探索資料已經到期,需要重新從源處擷取,發起的copy element的操作(pull);

CopyOnWrite則是發生在真實資料寫入緩衝時,發起的更新其他節點的copy element的操作(push)。

 

前者適合在不允許多個線程訪問同一個element的時候使用,後者則允許你自由控制緩衝更新通知的時機。

更多push和pull的變化和不同,也可參見這裡。

 

八、多種配置方式

 

包括設定檔、聲明式配置、編程式配置,甚至通過指定構造器的參數來完成配置,配置設計的原則包括:

所有配置要放到一起

緩衝的配置可以很容易在開發階段、運行時修改

錯誤的配置能夠在程式啟動時發現,在運行時修改出錯則需要拋出運行時異常

提供預設配置,幾乎所有的配置都是可選的,都有預設值

 

九、自動資源控制(Automatic Resource Control,ARC):

 

它是提供了一種智能途徑來控制緩衝,調優效能。特性包括:

記憶體內緩衝對象大小的控制,避免OOM出現

池化(cache manager層級)的緩衝大小擷取,避免單獨計算緩衝大小的消耗

靈活的獨立基於層的大小計算能力,下圖中可以看到,不同層的大小都是可以單獨控制的

可以統計位元組大小、緩衝條目數和百分比

最佳化高命中資料的擷取,以提升效能,參見下面對快取資料在不同層之間的流轉的介紹

快取資料的流轉包括了這樣幾種行為:

Flush:緩衝條目向低層次移動。

Fault:從低層拷貝一個對象到高層。在擷取緩衝的過程中,某一層發現自己的該緩衝條目已經失效,就觸發了Fault行為。

Eviction:把緩衝條目除去。

Expiration:失效狀態。

Pinning:強制緩衝條目保持在某一層。

下面的圖反映了資料在各個層之間的流轉,也反映了資料的生命週期:

 

十、監控功能

 

監控的拓撲:


每個應用節點部署一個監控探針,通過TCP協議與監控伺服器聯絡,最終將資料提供給富文本用戶端或者監控動作伺服器。

 

十一、廣域網路複製

快取資料複製方面,Ehcache允許兩個地理位置各異的節點在廣域網路下鑑效組資料一致性,同時它提供了這樣幾種方案(註:下面的樣本都只繪製了兩個節點的情形,實際可以推廣到N個節點):

 

第一種方案:Terracotta Active/Mirror Replication。


這種方案下,服務端包含一個活躍節點,一個備份節點;各個應用節點全部靠該活躍節點提供讀寫服務。這種方式最簡單,管理容易;但是,需要寄希望於理想的網路狀況,伺服器之間和用戶端到伺服器之間都存在走WAN的情況,這樣的方案其實最不穩定。

 

第二種方案:Transactional Cache Manager Replication。


這種方案下,資料讀取不需要經過WAN,寫入資料時寫入兩份,分別由兩個cache manager處理,一份在本地Server,一份到其他Server去。這種方案下讀的輸送量較高而且延遲較低;但是需要引入一個XA交易管理員,兩個cache manager寫兩份資料導致寫開銷較大,而且過WAN的寫延遲依然可能導致系統響應的瓶頸。

 

第三種方案:Messaging based (AMQ) replication。


這種方案下,引入了批量處理和隊列,用以減緩WAN的瓶頸出現,同時,把處理讀請求和複製邏輯從Server Array物理上就剝離開,避免了WAN情況惡化對節點讀取業務的影響。這種方案要較高的輸送量和較低的延遲,讀/複製的分離保證了可以提供完備的訊息分發保證、衝突處理等特性;但是它較為複雜,而且還需要一個訊息匯流排。

 

有一些Ehcache特性應用較少或者比較邊緣化,沒有提到,例如對於JMX的支援;還有一些則是有類似的特性和介紹了,例如對於WEB的支援,請參見我這篇關於OSCache的解讀,其中的“web支援”一節有詳細的原理分析。

 

最後,關於Ehcache的效能比對,下面這張圖來自Ehcache的創始人Greg Luck的blog:

 

put/get上Ehcache要500-1000倍快過Memcached。原因何在。他自己分析道:“In-process caching and asynchronous replication are a clear performance winner”。有關它詳細的內容還是請參閱他的blog吧。

聯繫我們

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