Java Cache 的HashMap實現, 適用場景及分布式ehcache實例
cache是老生常談的事情,但開始這個題目之前,我想先強調一下KISS原則,就是keep it simple and stupid。最近看到很多場景下cache使用的不適當,特別是被過度使用了。一個簡單鍵值存儲并不需要復雜的cache方案,好的方案就是用最簡單的方法解決問題。簡潔是美!
一個標準Cache的主要特征是:
過期時間
容量規劃(重要)
清除策略(重要)
命中率統計
基于以上特征,使用HashMap作為本地cache似乎很不適當。更重要的是,有不少朋友認為HashMap可能會使內存耗盡。其實不然,自 jdk1.2后,Java就引入了WeakHashMap。看看api文檔是怎么說的:對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。丟棄某個鍵時,其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實現有所不同。
這個翻譯明顯有點爛,總結一下,和強引用的HashMap相比,WeakHashMap在本身引用不被回收的前提下允許GC回收它的鍵值對。當GC 發現內存即將耗盡且沒有其他對象可以釋放時,會主動回收WeakHashMap所占用的對象。因此使用WeakHashMap作為本地cache是不會造成內存耗盡。如果你僅僅需要一個簡單的鍵值對存儲,而并不關心命中率統計,那么放心的使用WeakHashMap作為cache吧。
通過簡單的包裝,就可以為你的HashMap增加過期時間和容量規劃。而且比其他cache更高效。openfire的DefaultCache就是一個很好的例子:http://svn.igniterealtime.org/svn/repos/openfire/trunk/src/java /org/jivesoftware/util/cache/DefaultCache.java
DefaultCache的核心是HashMap,另外增加了一層很薄的包裝來實現過期和LRU。DefaultCache包括兩個 LinkedList,一個用于存儲插入順序,另一個用于存儲插入時間。當添加cache時,都addFirst。當cache size達到臨界值時,從最尾部刪除。有朋友測試過,比ehcache快5倍。
如果本地cache不能滿足你的要求,ehcache是個很好的選擇。不僅僅作為分布式的cache,甚至作為狀態同步,ehcache都有非常優秀的案例。也可以實現多機copy。分享一個數據,某生產系統中ehcache每天處理17,466,415次replication。
基于ehcache的分布式緩存,你可以簡單的實現分布式計算。分析一個場景,某ios后端服務要求用戶先注冊ios設備,可以按下圖處理設備信息。DeviceServer接受device注冊后同步copy的本地cache和ehcache。該ehcache按以下方式配置成對。本地 cache再定期(每隔幾秒鐘)從ehcache中加載peer server注冊的數據。
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual,rmiUrls=//192.168.0.1:20121/testCache"/> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=192.168.0.2,port=20121,socketTimeoutMillis=60000"/> <cache name="testCache" maxElementsInMemory="20000" eternal="false" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=true, asynchronousReplicationIntervalMillis=2000" /> </cache>
peer server的hostName和rmiUrls配置需要做相應改動。
根據KISS原則,建議簡單狀態同步都使用ehcache完成。知識投入低,且易于維護。