在開發中大型Java軟件項目時,很多Java架構師都會遇到數據庫讀寫瓶頸,如果你在系統架構時並沒有將緩存策略考慮進去,或者並沒有選擇更優的緩存策略,那么到時候重構起來將會是一個噩夢。本文主要是分享了5個常用的Java分布式緩存框架,這些緩存框架支持多台服務器的緩存讀寫功能,可以讓你的緩存系統更容易擴展。
1、Ehcache – Java分布式緩存框架
Ehcache是一個Java實現的開源分布式緩存框架,EhCache 可以有效地減輕數據庫的負載,可以讓數據保存在不同服務器的內存中,在需要數據的時候可以快速存取。同時EhCache 擴展非常簡單,官方提供的Cache配置方式有好幾種。你可以通過聲明配置、在xml中配置、在程序里配置或者調用構造方法時傳入不同的參數。
Ehcache有以下特點:
- 存取速度非常快,性能很不錯。
- 可以應用多種緩存策略。
- 分級緩存,用戶可以指定哪些數據在硬盤中緩存,哪些數據在內存中緩存。
- 可以通過RMI、可插入API等方式進行分布式緩存。
- 具有緩存和緩存管理器的偵聽接口。
- 支持多緩存管理器實例,以及一個實例的多個緩存區域。
- 默認提供Hibernate的緩存實現。
- Spring默認配置緩存
官方網站:http://ehcache.org/
Ehcache的配置示例代碼:
<ehcache> <diskStore path=”java.io.tmpdir”/> <defaultCache maxElementsInMemory=”10000″ eternal=”false” timeToIdleSeconds=”120″ timeToLiveSeconds=”120″ overflowToDisk=”true” maxElementsOnDisk=”10000000″ diskPersistent=”false” diskExpiryThreadIntervalSeconds=”120″ memoryStoreEvictionPolicy=”LRU” /> </ehcache>
<!-- 配置自定義緩存
maxElementsInMemory:緩存中允許創建的最大對象數
eternal:緩存中對象是否為永久的,如果是,超時設置將被忽略,對象從不過期。
timeToIdleSeconds:緩存數據的鈍化時間,也就是在一個元素消亡之前,
兩次訪問時間的最大時間間隔值,這只能在元素不是永久駐留時有效,
如果該值是 0 就意味着元素可以停頓無窮長的時間。
timeToLiveSeconds:緩存數據的生存時間,也就是一個元素從構建到消亡的最大時間間隔值,
這只能在元素不是永久駐留時有效,如果該值是0就意味着元素可以停頓無窮長的時間。
overflowToDisk:內存不足時,是否啟用磁盤緩存。
memoryStoreEvictionPolicy:緩存滿了之后的淘汰算法。
-->
在同類的Java緩存框架中,Ehcache配置相對簡單,也比較容易上手,最大的優勢是它支持分布式緩存。
2、Cacheonix – 高性能Java分布式緩存系統
Cacheonix同樣也是一個基於Java的分布式集群緩存系統,它同樣可以幫助你實現分布式緩存的部署。
官方網站:http://www.cacheonix.com/
3、ASimpleCache – 輕量級Android緩存框架
ASimpleCache是一款基於Android的輕量級緩存框架,它只有一個Java文件,ASimpleCache基本可以緩存常用的Android對象,包括普通字符串、JSON對象、經過序列化的Java對象、字節數組等。
官方網站:https://github.com/yangfuhai/ASimpleCache
4、JBoss Cache – 基於事物的Java緩存框架
JBoss Cache是一款基於Java的事務處理緩存系統,它的目標是構建一個以Java框架為基礎的集群解決方案,可以是服務器應用,也可以是Java SE應用。
官方網站:http://jbosscache.jboss.org/
集群高可用性
JBoss Cache將會自動復制緩存數據,並且在集群中的服務器之間進行緩存數據的同步,這樣可以保證任何一台服務器重啟了都不會影響緩存的可用性。
集群緩存可避免系統瓶頸
JBoss Cache顧名思義是利用緩存來提高系統擴展性的,當我們的WEB系統遇到大量的數據庫讀寫時,系統的瓶頸將會出現在數據庫端,JBoss Cache正好可以解決數據庫的頻繁讀取問題,解決這個瓶頸。
另外,由於JBoss Cache的緩存是在集群中的每一個服務器間同步的,因此也不會因為一台緩存服務器遇到性能問題而影響整個系統。
JBoss Cache的standalone用法
首先是初始化TreeCache
TreeCache tree = new TreeCache();
然后是讀進配置文件
PropertyConfigurator config = new PropertyConfigurator(); config.configure("配置文件.xml");
然后開始服務
Tree.startService();
因為Tree的結構是用NODE來Access的,TreeCache這里就很簡單的用:
/level1/level2/node1 來表示兩級Tree下面的Node1。
現在我們添加幾個要Cache的對象。
Tree.put("/level1/level2/node1", "key1", "value1"); String[] array = { "1", "2", "3", "4" } Tree.put("/level3/array/", "myarray", array);
可以看到,TreeCache里面可以存儲任何種類的對象,包括所有復雜對象。
讀取對象就很方便了,
String s = (String)Tree.get("/level1/level2/node1/", "key1");
value1就讀出來了。
同理:
String[] sarr = (String[]) Tree.get("/level3/array/","myarray");
System.out.println(sarr[1]) 會顯示2
最后停止服務:
Tree.stopService();
JBoss Cache的FileCacheLoader示例
首先創建一個FileCache類封裝JBoss Cache的相關操作,如下:
package com.javacache.steven.jbosscache; import java.io.File; import java.util.Map; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.FileCacheLoader; import org.jboss.cache.loader.FileCacheLoaderConfig; /** * <p> * This is demo to illustrate how to use the JBoss Cache to cache your * frequently accessed Java objects in order to dramatically improve * the performance of your applications. This makes it easy to remove * data access bottlenecks, such as connecting to a database. * </p> * <p> * As a rule of thumb, it is recommended that the FileCacheLoader not * be used in a highly concurrent, transactional or stressful environment, * ant its use is restricted to testing. * </p> * * @author steven * * @param <T> */ public class FileCache<T> { /** * The JBoss Cache, used to cache frequently accessed Java objects. */ private Cache<String, T> cache; /** * @constructor * @param fsCacheLoaderLocation The file system location to store the cache */ public FileCache(File fsCacheLoaderLocation) { cache = initCache(fsCacheLoaderLocation); } /** * Create a Cache and whose cache loader type is File Cache Loader * * @param fsCacheLoaderLocation The file position used to store the cache. * * @return Cache */ public Cache<String, T> initCache(File fsCacheLoaderLocation) { // initiate a FileCacheLoader instance FileCacheLoader fsCacheLoader = new FileCacheLoader(); // prepare the file cache loader configuration file for File Cache Loader FileCacheLoaderConfig fsCacheLoaderConfig = new FileCacheLoaderConfig(); fsCacheLoaderConfig.setLocation(fsCacheLoaderLocation.toString()); fsCacheLoaderConfig.setCacheLoader(fsCacheLoader); // set configuration to File Cache Loader fsCacheLoader.setConfig(fsCacheLoaderConfig); // prepare the configuration for Cache Configuration config = new Configuration(); config.setCacheLoaderConfig(new CacheLoaderConfig()); config.getCacheLoaderConfig().addIndividualCacheLoaderConfig(fsCacheLoaderConfig); // create a Cache through the default cache factory return new DefaultCacheFactory<String, T>().createCache(config); } /** * Add a new node into the tree-node hierarchy * * @param fqn Full Qualified Name for the new node * @return */ public Node<String, T> addNode(Fqn<String> fqn) { return cache.getRoot().addChild(fqn); } /** * Remove a specified node from the tree-node hierarchy * * @param fqn Full Qualified Name for the specified node */ public void removeNode(Fqn<String> fqn) { cache.removeNode(fqn); } /** * Add node information to the specified node. * * @param fqn Full Qualified Name for the specified node * @param key The key of the node information * @param value The value of the node information */ public void addNodeInfo(Fqn<String> fqn, String key, T value) { cache.put(fqn, key, value); } /** * Batch add node information to the specified node. * * @param fqn Full Qualified Name for the specified node * @param infos Node informations map */ public void addNodeInfos(Fqn<String> fqn, Map<String, T> infos) { cache.put(fqn, infos); } /** * Get node information from the specified node. * * @param fqn Full Qualified Name for the specified node * @param key The key of the node information * @return */ public T getNodeInfo(Fqn<String> fqn, String key) { return cache.get(fqn, key); } /** * Remove node information from the specified node. * * @param fqn Full Qualified Name for the specified node * @param key The key of the node information */ public void removeNodeInfo(Fqn<String> fqn, String key) { cache.remove(fqn, key); } }
下面是一個測試案例:
package com.javacache.steven.jbosscache; import java.io.File; import org.jboss.cache.Fqn; public class Main { public static void main(String[] args) { FileCache<String> fileCache = new FileCache<String>(new File("f:\\tmp")); Fqn<String> jimmyFqn = Fqn.fromString("/com/manager/jimmy"); Fqn<String> jackFqn = Fqn.fromString("/com/developer/jack"); fileCache.addNode(jimmyFqn); fileCache.addNode(jackFqn); fileCache.addNodeInfo(jimmyFqn, "en-name", "Jimmy Zhang"); fileCache.addNodeInfo(jimmyFqn, "zh-name", "Zhang Ji"); fileCache.addNodeInfo(jackFqn, "en-name", "Jack Li"); fileCache.addNodeInfo(jackFqn, "zh-name", "Li Jie"); String enName = fileCache.getNodeInfo(jackFqn, "en-name"); System.out.println(enName); } }
運行結果如下:
- JBossCache MBeans were successfully registered to the platform mbean server. - JBoss Cache version: JBossCache 'Malagueta' 3.2.5.GA Jack Li
生成的緩存文件目錄結構如下:
F:/tmp/com.fdb/manage.fdb/jimmy.fdb/data.dat
F:/tmp/com.fdb/developer.fdb/jack.fdb/data.dat
JBoss Cache還有更多的用法,如果你的系統遇到數據庫瓶頸問題,可以考慮使用JBoss Cache來解決。
5、Voldemort – 基於鍵-值(key-value)的緩存框架
Voldemort是一款基於Java開發的分布式鍵-值緩存系統,像JBoss Cache一樣,Voldemort同樣支持多台服務器之間的緩存同步,以增強系統的可靠性和讀取性能。
官方網站:http://www.project-voldemort.com/voldemort/