java分佈式緩存技術

開發中大型Java軟件項目時,很多Java架構師都會遇到數據庫讀寫瓶頸,如果你在系統架構時並沒有將緩存策略考慮進去,或者並沒有選擇更優的緩存策略,那麼到時候重構起來將會是一個噩夢。本文主要是分享了5個常用的Java分佈式緩存框架,這些緩存框架支持多臺服務器的緩存讀寫功能,可以讓你的緩存系統更容易擴展。

1Ehcache – Java分佈式緩存框架

Ehcache是一個Java實現的開源分佈式緩存框架,EhCache 可以有效地減輕數據庫的負載,可以讓數據保存在不同服務器的內存中,在需要數據的時候可以快速存取。同時EhCache 擴展非常簡單,官方提供的Cache配置方式有好幾種。你可以通過聲明配置、在xml中配置、在程序裏配置或者調用構造方法時傳入不同的參數。

 官方網站:http://ehcache.org/

Ehcache有以下特點:

· 存取速度非常快,性能很不錯。

· 可以應用多種緩存策略。

· 分級緩存,用戶可以指定哪些數據在硬盤中緩存,哪些數據在內存中緩存。

· 可以通過RMI、可插入API等方式進行分佈式緩存。

· 具有緩存和緩存管理器的偵聽接口。

· 支持多緩存管理器實例,以及一個實例的多個緩存區域。

· 默認提供Hibernate的緩存實現。

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>

 在同類的Java緩存框架中,Ehcache配置相對簡單,也比較容易上手,最大的優勢是它支持分佈式緩存。

2Cacheonix – 高性能Java分佈式緩存系統

Cacheonix同樣也是一個基於Java的分佈式集羣緩存系統,它同樣可以幫助你實現分佈式緩存的部署。

 官方網站:http://www.cacheonix.com/

Cacheonix的特點

· 可靠的分佈式 Java 緩存

· 通過複製實現高可用性

· 支持泛型的緩存 API

· 可與 ORM 框架集成

· 使用數據分區實現負載均衡

· 支持非多播網絡

· 高性能計算

· 快速的本地 Java 緩存

· 分佈式鎖機制

Cacheonix的架構圖

 

Cacheonix分佈式緩存XML配置

<?xml version ="1.0"?>

<cacheonix xmlns="http://www.cacheonix.com/schema/configuration"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xsi:schemaLocation="http://www.cacheonix.com/schema/configuration http://www.cacheonix.com/schema/cacheonix-config-2.0.xsd">

 

   <server>

 

      <listener>

         <tcp port="8879" buffer="128k"/>

      </listener>

 

      <broadcast>

         <multicast multicastAddress="225.0.1.2" multicastPort="9998" multicastTTL="0"/>

      </broadcast>

 

      <partitionedCache name="customer.cache">

         <store>

            <lru maxElements="10000" maxBytes="10mb"/>

            <expiration idleTime="120s"/>

         </store>

      </partitionedCache>

 

      <partitionedCache name="invoice.cache">

         <store>

            <lru maxElements="10000" maxBytes="10mb"/>

            <expiration idleTime="120s"/>

         </store>

      </partitionedCache>

 

      <partitionedCache name="search.results.cache">

         <store>

            <lru maxBytes="5mb"/>

         </store>

      </partitionedCache>

   </server>

</cacheonix>

Cacheonix緩存的存取

  從配置中獲取Cacheonix實例

/**

 * Tester for CacheManager.

 */

public final class CacheonixTest extends TestCase {

 

   private Cacheonix cacheonix;

 

   /**

    * Tests getting an instance of CacheManager using a default Cacheonix configuration.

    */

   public void testGetInstance() {

 

      assertNotNull("Cacheonix created in setUp() method should not be null", cacheonix);

   }

 

   /**

    * Sets up the fixture. This method is called before a test is executed.

    * <p/>

    * Cacheonix receives the default configuration from a <code>cacheonix-config.xml</code> found in a class path or

    * using a file that name is defined by system parameter <code>cacheonix.config.xml<code>.

    */

   protected void setUp() throws Exception {

 

      super.setUp();

 

      // Get Cacheonix using a default Cacheonix configuration. The configuration

      // is stored in the conf/cacheonix-config.xml

      cacheonix = Cacheonix.getInstance();

   }

 

   /**

    * Tears down the fixture. This method is called after a test is executed.

    */

   protected void tearDown() throws Exception {

 

      // Cache manager has be be shutdown upon application exit.

      // Note that call to shutdown() here uses unregisterSingleton

      // set to true. This is necessary to support clean restart on setUp()

      cacheonix.shutdown(ShutdownMode.GRACEFUL_SHUTDOWNtrue);

      cacheonix = null;

 

      super.tearDown();

   }

}

讀取緩存

Cacheonix cacheonix = Cacheonix.getInstance();

Cache<StringStringcache = cacheonix.getCache("my.cache");

String cachedValue = cache.get("my.key");

設置緩存

Cacheonix cacheonix = Cacheonix.getInstance();

Cache<StringStringcache = cacheonix.getCache("my.cache");

String replacedValue = cache.put("my.key""my.value");

刪除緩存

Cacheonix cacheonix = Cacheonix.getInstance();

Cache<StringStringcache = cacheonix.getCache("my.cache");

String removedValue = cache.remove("my.key");

Cacheonix作爲一款開源的分佈式緩存框架,可以滿足中型企業規模的系統架構,對提升系統性能有非常棒的作用。

3ASimpleCache – 輕量級Android緩存框架

ASimpleCache是一款基於Android的輕量級緩存框架,它只有一個Java文件,ASimpleCache基本可以緩存常用的Android對象,包括普通字符串、JSON對象、經過序列化的Java對象、字節數組等。

 

  官方網站:https://github.com/yangfuhai/ASimpleCache

ASimpleCache可以緩存哪些東西

ASimpleCache基本可以緩存常用的Android對象,包括但不限於以下幾種類型:

· 普通字符串

· JSON對象

· 經過序列化的Java對象

· 字節數組

ASimpleCache的特點

· 輕量級,只有一個Java文件

· 完整而靈活的配置,可以配置緩存路徑,緩存大小,緩存數量,緩存超時時間等。

· 超時緩存自動失效,並從內存中自動刪除。

· 多進程的支持

  在Android開發中,我們可以用ASimpleCache來替換SharePreference配置文件,特別是如果你的應用經常要從互聯網上讀取數據,那麼利用ASimpleCache可以緩存這些請求數據,等一段時間失效後再去重新讀取,這樣可以減少客戶端流量,同時減少服務器併發量。

ASimpleCache的示例代碼

  設置緩存數據:

ACache mCache = ACache.get(this);

mCache.put("test_key1""test value");

mCache.put("test_key2""test value"10);//保存10秒,如果超過10秒去獲取這個key,將爲null

mCache.put("test_key3""test value"2 * ACache.TIME_DAY);//保存兩天,如果超過兩天去獲取這個key,將爲null

獲取緩存數據:

ACache mCache = ACache.get(this);

String value = mCache.getAsString("test_key1");

ASimpleCache的作者是國人,代碼託管在Github上,也用過ASimpleCache的同學可以分享一下使用心得,爲開源事業貢獻一份力量。

4JBoss Cache – 基於事物的Java緩存框架

JBoss Cache是一款基於Java的事務處理緩存系統,它的目標是構建一個以Java框架爲基礎的集羣解決方案,可以是服務器應用,也可以是Java SE應用。

 

  官方網站:http://jbosscache.jboss.org/

集羣高可用性

JBoss Cache將會自動複製緩存數據,並且在集羣中的服務器之間進行緩存數據的同步,這樣可以保證任何一臺服務器重啓了都不會影響緩存的可用性。

集羣緩存可避免系統瓶頸

JBoss Cache顧名思義是利用緩存來提高系統擴展性的,當我們的WEB系統遇到大量的數據庫讀寫時,系統的瓶頸將會出現在數據庫端,JBoss Cache正好可以解決數據庫的頻繁讀取問題,解決這個瓶頸。

  另外,由於JBoss Cache的緩存是在集羣中的每一個服務器間同步的,因此也不會因爲一臺緩存服務器遇到性能問題而影響整個系統。

JBoss Cachestandalone用法

  首先是初始化TreeCache

TreeCache tree = new TreeCache();

然後是讀進配置文件

PropertyConfigurator config = new PropertyConfigurator();

config.configure("配置文件.xml");

然後開始服務

Tree.startService();

因爲Tree的結構是用NODEAccess的,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 CacheFileCacheLoader示例

  首先創建一個FileCache類封裝JBoss Cache的相關操作,如下:

package com.javaeye.terrencexu.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 TerrenceX

 *

 * @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.javaeye.terrencexu.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("d:\\tmp"));  

 

        Fqn<String> jimmyFqn = Fqn.fromString("/com/manager/jimmy");  

        Fqn<String> hansonFqn = Fqn.fromString("/com/developer/hanson");  

 

        fileCache.addNode(jimmyFqn);  

        fileCache.addNode(hansonFqn);  

 

        fileCache.addNodeInfo(jimmyFqn, "en-name""Jimmy Zhang");  

        fileCache.addNodeInfo(jimmyFqn, "zh-name""Zhang Ji");  

        fileCache.addNodeInfo(hansonFqn, "en-name""Hanson Yang");  

        fileCache.addNodeInfo(hansonFqn, "zh-name""Yang Kuo");  

 

        String enName = fileCache.getNodeInfo(hansonFqn, "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  

Hanson Yang

生成的緩存文件目錄結構如下:

D:/tmp/com.fdb/manage.fdb/jimmy.fdb/data.dat

D:/tmp/com.fdb/developer.fdb/hanson.fdb/data.dat

總結

JBoss Cache還有更多的用法,如果你的系統遇到數據庫瓶頸問題,可以考慮使用JBoss Cache來解決。

5Voldemort – 基於鍵-值(key-value)的緩存框架

Voldemort是一款基於Java開發的分佈式鍵-值緩存系統,像JBoss Cache一樣,Voldemort同樣支持多臺服務器之間的緩存同步,以增強系統的可靠性和讀取性能。

 官方網站:http://www.project-voldemort.com/voldemort/

Voldemort的特點

· 緩存數據可以自動在各個服務器節點之間同步複製。

· 每一個服務器的緩存數據被橫向分割,因此是總緩存的一個子集。

· 嚴格保持緩存的一致性。

· 提供服務器宕機快速恢復方案。

· 可配置的數據存儲引擎。

· 可配置的數據序列化方式。

· 每一個數據項都有版本標識,用來保證數據的完整性和可用性。

· 每一個緩存節點都是獨立的,因此任何一個節點的故障都不會影響系統的正常運行。

Voldemort的配置方式

  集羣配置文件:

<cluster>

    <!-- The name is just to help users identify this cluster from the gui -->

    <name>mycluster</name>

    <zone>

      <zone-id>0</zone-id>

      <proximity-list>1</proximity-list>

    <zone>

    <zone>

      <zone-id>1</zone-id>

      <proximity-list>0</proximity-list>

    <zone>

    <server>

      <!-- The node id is a unique, sequential id beginning with 0 that identifies each server in the cluster-->

      <id>0</id>

      <host>vldmt1.prod.linkedin.com</host>

      <http-port>8081</http-port>

      <socket-port>6666</socket-port>

      <admin-port>6667</admin-port>

      <!-- A list of data partitions assigned to this server -->

      <partitions>0,1,2,3</partitions>

      <zone-id>0</zone-id>

    </server>

    <server>

      <id>1</id>

      <host>vldmt2.prod.linkedin.com</host>

      <http-port>8081</http-port>

      <socket-port>6666</socket-port>

      <admin-port>6667</admin-port>

      <partitions>4,5,6,7</partitions>

      <zone-id>1</zone-id>

    </server>

  </cluster>

數據存儲方式配置文件:

<stores>

      <store>

  <name>test</name>

  <replication-factor>2</replication-factor>

  <preferred-reads>2</preferred-reads>

  <required-reads>1</required-reads>

  <preferred-writes>2</preferred-writes>

  <required-writes>1</required-writes>

  <persistence>bdb</persistence>

  <routing>client</routing>

  <routing-strategy>consistent-routing</routing-strategy>

  <key-serializer>

      <type>string</type>

      <schema-info>utf8</schema-info>

  </key-serializer>

  <value-serializer>

      <type>json</type>

      <schema-info version="1">[{"id":"int32", "name":"string"}]</schema-info>

      <compression>

  <type>gzip<type>

      </compression>

  </value-serializer>

      </store>

  </stores>

Voldemort的使用示例

value = store.get(key)

store.put(key, value)

store.delete(key)

總結

Voldemort是分佈式緩存系統,因此可以應用在中大型的軟件項目中,性能方面也都還不錯。

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