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.

    *

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

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

    */

   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;  

 

/**

 *

 * 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.

 *

 *

 * 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.

 *

 *  

 * @author TerrenceX

 *

 * @param

 */  

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>

    

    <name>myclustername>

    <zone>

      <zone-id>0zone-id>

      <proximity-list>1proximity-list>

    <zone>

    <zone>

      <zone-id>1zone-id>

      <proximity-list>0proximity-list>

    <zone>

    <server>

      

      <id>0id>

      <host>vldmt1.prod.linkedin.comhost>

      <http-port>8081http-port>

      <socket-port>6666socket-port>

      <admin-port>6667admin-port>

      

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

      <zone-id>0zone-id>

    server>

    <server>

      <id>1id>

      <host>vldmt2.prod.linkedin.comhost>

      <http-port>8081http-port>

      <socket-port>6666socket-port>

      <admin-port>6667admin-port>

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

      <zone-id>1zone-id>

    server>

  cluster>

数据存储方式配置文件:

<stores>

      <store>

  <name>testname>

  <replication-factor>2replication-factor>

  <preferred-reads>2preferred-reads>

  <required-reads>1required-reads>

  <preferred-writes>2preferred-writes>

  <required-writes>1required-writes>

  <persistence>bdbpersistence>

  <routing>clientrouting>

  <routing-strategy>consistent-routingrouting-strategy>

  <key-serializer>

      <type>stringtype>

      <schema-info>utf8schema-info>

  key-serializer>

  <value-serializer>

      <type>jsontype>

      <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是分布式缓存系统,因此可以应用在中大型的软件项目中,性能方面也都还不错。

你可能感兴趣的:(框架和架构)