cache是老生常談的事情,這里我想強(qiáng)調(diào)一下KISS原則,就是keep it simple and stupid。最近看到很多場(chǎng)景下cache使用的不適當(dāng),特別是被過(guò)度使用了。一個(gè)簡(jiǎn)單鍵值存儲(chǔ)并不需要復(fù)雜的cache方案,好的方案就是用最簡(jiǎn)單的方法解決問(wèn)題。簡(jiǎn)潔是美!
一個(gè)標(biāo)準(zhǔn)Cache的主要特征是: 過(guò)期時(shí)間 容量規(guī)劃(重要) 清除策略(重要) 命中率統(tǒng)計(jì)
基于以上特征,使用HashMap作為本地cache似乎很不適當(dāng)。更重要的是,有不少朋友認(rèn)為HashMap可能會(huì)使內(nèi)存耗盡。其實(shí)不然,自jdk1.2后,Java就引入了WeakHashMap??纯碼pi文檔是怎么說(shuō)的:對(duì)于一個(gè)給定的鍵,其映射的存在并不阻止垃圾回收器對(duì)該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。丟棄某個(gè)鍵時(shí),其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實(shí)現(xiàn)有所不同。
這個(gè)翻譯明顯有點(diǎn)爛,總結(jié)一下,和強(qiáng)引用的HashMap相比,WeakHashMap在本身引用不被回收的前提下允許GC回收它的鍵值對(duì)。當(dāng)GC發(fā)現(xiàn)內(nèi)存即將耗盡且沒(méi)有其他對(duì)象可以釋放時(shí),會(huì)主動(dòng)回收WeakHashMap所占用的對(duì)象。因此使用WeakHashMap作為本地cache是不會(huì)造成內(nèi)存耗盡。如果你僅僅需要一個(gè)簡(jiǎn)單的鍵值對(duì)存儲(chǔ),而并不關(guān)心命中率統(tǒng)計(jì),那么放心的使用WeakHashMap作為cache吧。
通過(guò)簡(jiǎn)單的包裝,就可以為你的HashMap增加過(guò)期時(shí)間和容量規(guī)劃。而且比其他cache更高效。openfire的DefaultCache就是一個(gè)很好的例子:http://svn./svn/repos/openfire/trunk/src/java/org/jivesoftware/util/cache/DefaultCache.java
DefaultCache的核心是HashMap,另外增加了一層很薄的包裝來(lái)實(shí)現(xiàn)過(guò)期和LRU。DefaultCache包括兩個(gè)LinkedList,一個(gè)用于存儲(chǔ)插入順序,另一個(gè)用于存儲(chǔ)插入時(shí)間。當(dāng)添加cache時(shí),都addFirst。當(dāng)cache size達(dá)到臨界值時(shí),從最尾部刪除。有朋友測(cè)試過(guò),比ehcache快5倍。
如果本地cache不能滿足你的要求,ehcache是個(gè)很好的選擇。不僅僅作為分布式的cache,甚至作為狀態(tài)同步,ehcache都有非常優(yōu)秀的案例。也可以實(shí)現(xiàn)多機(jī)copy。分享一個(gè)數(shù)據(jù),某生產(chǎn)系統(tǒng)中ehcache每天處理17,466,415次replication。
基于ehcache的分布式緩存,你可以簡(jiǎn)單的實(shí)現(xiàn)分布式計(jì)算。分析一個(gè)場(chǎng)景,某iOS后端服務(wù)要求用戶先注冊(cè)ios設(shè)備,可以按下圖處理設(shè)備信息。DeviceServer接受device注冊(cè)后同步copy的本地cache和ehcache。該ehcache按以下方式配置成對(duì)。本地cache再定期(每隔幾秒鐘)從ehcache中加載peer server注冊(cè)的數(shù)據(jù)。
ehcache.xml 寫道
<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配置需要做相應(yīng)改動(dòng)。
偽代碼如下
Java style fake code代碼
根據(jù)KISS原則,建議簡(jiǎn)單狀態(tài)同步都使用ehcache完成。知識(shí)投入低,且易于維護(hù)。 |
|
來(lái)自: WindySky > 《分布式架構(gòu)》