0
回答
ehcache缓存同步案例心得
终于搞明白,存储TCO原来是这样算的>>>   

文章转自:http://blog.csdn.net/ss300400a/article/details/74566347

本文分享一下ehcache的使用心得,本文主要讲以广播的形式同步缓存。

下面讲述主要分为两个部分,一个是配置文件,一个是Java代码。

1.准备jar包:

slf4j-api-1.7.12.jar,ehcache-core-2.4.3.jar,ehcache-web-2.0.4.jar 
备注:1) 版本可以不同 
2)sl4j可能与JavaEE6.0中的冲突,在JVM中运行时可以不使用。

2.配置文件:

2.1. 完整xml(广播形式)

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
        http://www.springmodules.org/schema/cache/springmodules-cache.xsd 
        http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    updateCheck="false">
    <!-- 默认缓存 -->
    <defaultCache eternal="false" maxElementsInMemory="10000"
        overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="0"
        memoryStoreEvictionPolicy="LFU" />
    <!-- 自定义缓存 -->
    <cache name="myCache" eternal="true" maxElementsInMemory="10000"
        overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="0"
        memoryStoreEvictionPolicy="LFU">
     <!-- 监听RMI同步缓存对象配置 注册相应的的缓存监听类,用于处理缓存事件,如put,remove,update,和expire -->
     <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                properties="replicateAsynchronously=true, replicatePuts=true,
                            replicatePutsViaCopy=true, replicateUpdates=true,
                            replicateUpdatesViaCopy=true, replicateRemovals=true,
                            asynchronousReplicationIntervalMillis=200"/>
        <!-- 用于在初始化缓存,以及自动设置 -->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
    </cache>
    <!--搜索某个网段上的缓存
    timeToLive
        0是限制在同一个服务器
        1是限制在同一个子网
        32是限制在同一个网站
        64是限制在同一个region
        128是限制在同一个大洲
        255是不限制-->
    <cacheManagerPeerProviderFactory  
        class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"  properties="peerDiscovery=automatic,multicastGroupAddress=230.0.0.1,multicastGroupPort=4446,timeToLive=32,hostName=localhost" />  
    <cacheManagerPeerListenerFactory
        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"  />
</ehcache>

2.1. 关键部分 
ehcache 
|——defaultCache (默认cache配置) 
|——cache (自定义cache) 
|——|——cacheEventListenerFactory(同步动作) 
|——|——bootstrapCacheLoaderFactory(工厂是指启动是指一启动就同步数据) 
|——cacheManagerPeerProviderFactory(发布:广播) 
|——cacheManagerPeerListenerFactory(监听) 
同步缓存必须有的几个部分:

1)cacheEventListenerFactory 
每个要进行同步的cache都需要设置一个用来向CacheManagerr的成员复制消息的缓存事件监听器。这个工作要通过为每个cache的配置增加一个cacheEventListenerFactory元素来完成。

 <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                properties="replicateAsynchronously=true, replicatePuts=true,
                            replicatePutsViaCopy=true, replicateUpdates=true,
                            replicateUpdatesViaCopy=true, replicateRemovals=true,
                            asynchronousReplicationIntervalMillis=200"/>

class – 使用net.sf.ehcache.distribution.RMICacheReplicatorFactory

这个工厂支持以下属性: 
replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers. 默认是true。 
replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。 
replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。 
replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时)。默认是true。 
replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。 
replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制(指定为true时为复制),默认是true。

2)cacheManagerPeerProviderFactory(广播方式) 
自动方式:自动发现方式使用tcp广播来建立和包含一个广播组,它的特征是最小配置和对成员组的自动添加和管理。没有那个服务器是有优先级的。对等点每一秒中向广播组发送心跳,如果一个对等点在五秒钟内没发送过来,则此对等点将会被删除,如果有新的,则会被加入集群

<cacheManagerPeerProviderFactory  
        class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"  
        properties="peerDiscovery=automatic,multicastGroupAddress=230.0.0.1,multicastGroupPort=4446,timeToLive=32,hostName=localhost" />  

peerDiscovery 方式:atutomatic 为自动 ;mulicastGroupAddress 广播组地址:230.0.0.1;mulicastGroupPort 广播组端口:40001;timeToLive是指搜索范围:0是同一台服务器,1是同一个子网,32是指同一站点,64是指同一块地域,128是同一块大陆,还有个256,我就不说了;hostName:主机名或者ip,用来接受或者发送信息的接口

3)cacheManagerPeerListenerFactory 
将方式配好之后需要配置listener才会有用,接下来讲讲:Listener 
Listener是用来监听从集群发送过来的信息 
Listenner有两个属性:class和propertis 
class 一个完整的工厂类名 
properties 都好分割的对facotory有用的属性

<cacheManagerPeerListenerFactory
        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"  />

hostName指的是本机,这里注意如果使用的localhost,则只会对本机有效,请使用子网内的ip地址或者主机名,port端口 40001,socketTimeoutMillis是指socket子模块的超时时间,默认是2000ms,注意port两台主机可以相同可以不同。最好相同,个人建议.

参考文档 
1.分布式缓存 
2.实例项目 
3.实例项目(推荐) 
4.详细介绍 
5.IBM版介绍

2.Java代码

客户机1

package src.text;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.management.ManagementService;

public class EHCacheTest {
    private static CacheManager manager = null; // 缓存工厂

    public static void main(String[] args) {
        EHCacheTest ehCacheTest = new EHCacheTest();
        ehCacheTest.init();
    }

    protected void init() {
        // 读入配置
        String path = this.getClass().getClassLoader().getResource("").getPath() + "ehcache.xml";
        manager = CacheManager.create(path);


        // 打印初始缓存
        String[] cacheNames = manager.getCacheNames();
        printNames(cacheNames);
        // 移除缓存
        // cacheManager.removeCache("sampleDistributedCache1");
        cacheNames = manager.getCacheNames();
        printNames(cacheNames);
        // distributed -- rmi同步
        Cache cache = manager.getCache("fhxxCache");
        // 注册被管理的Bean
        // JMX -- jconsole(MBeanServer)
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);
        for (int i = 0; i < 100000; i++) {
            Element temp = cache.get("ehcache");
            System.out.println("cache.getSize()=" + cache.getSize());
            cache.put(new Element(i, i));
            System.out.println("=======================");
            System.out.println("第" + i + "次cache.put");
            if (temp != null) {
                System.out.println(temp.getObjectValue());
            } else {
                System.out.println("NotFound");
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    private static void printNames(String[] names) {
        System.out.println("=======================");
        for (int i = 0; i < names.length; i++) {
            System.out.println(names[i]);
        }
    }
}

客户机2

package src.text;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.Statistics;
import net.sf.ehcache.management.ManagementService;

public class EHCacheTest2 {
    private static CacheManager manager = null; // 缓存工厂

    public static void main(String[] args) {
        EHCacheTest2 ehCacheTest2 = new EHCacheTest2();
        ehCacheTest2.init();
    }

    protected void init() {
        // 读入配置
        String path = this.getClass().getClassLoader().getResource("").getPath() + "ehcache.xml";
        manager = CacheManager.create(path);

        // 打印初始缓存
        String[] cacheNames = manager.getCacheNames();
        // 注册管理Bean
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);
        // distributed
        Cache cache = manager.getCache("fhxxCache");
        // 添加值后另一个虚拟机的缓存通过RMI会同步缓存,并读到这个值
        cache.put(new Element("ehcache", "newaddvalue11"));
        printCache(cache);
        for (int i = 0; i < 10000; i++) {
            try {
                cache.put(new Element("ehcache", "newaddvalue" + i));
                Thread.sleep(3000);
                System.out.println("第" + i + "次加载cache.size=" + cache.getSize());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    private static void printCache(Cache cache) {
        int size = cache.getSize();
        long memSize = cache.getMemoryStoreSize();
        long diskSize = cache.getDiskStoreSize();
        Statistics stat = cache.getStatistics();
        StringBuilder sb = new StringBuilder();
        sb.append("size=" + size + ";memsize=" + memSize);
        sb.append(";diskSize=" + diskSize + ";");
        System.out.println(sb.toString());
    }
}

运行客户机1,开始:

第0次cache.put
NotFound

客户机2,开始后,客户机1

=======================
第29次cache.put
newaddvalue3

组播的缺陷 
原理: 
这样当缓存改变时,ehcache会向230.0.0.1端口4446发RMI UDP组播包 
这种组播方式的缺陷: 
EHCACHE的组播做得比较初级,功能只是基本实现(比如简单的一个HUB,接两台单网卡的服务器,互相之间组播同步就没问题), 
对一些复杂的环境(比如多台服务器,每台服务器上多地址,尤其是集群,存在一个集群地址带多个物理机,每台物理机又带多个虚拟站的子地址),就容易出现问题. 
究其原因, 组播/广播转发是一个很复杂的过程. 简单的说, 一个组播缺省只能在一个网段内传输,不能跨网段. 
举个简单的例子, PC机网卡的自动获取地址,还有WINDOWS里的网上邻居,都属于典型的广播服务,所以这些服务都是不能跨网段(跨路由)的,当然也不是完全不行,借助一些工具,比如CISCO路由器上的udp-broadcast helper,或者微软的netBIOS on Tcp/ip,就可以实现. 
我们自己安装一些软件时,也经常遇到比如”将网卡的广播转发打开”之类的操作.

而在多网卡的主机,或同一网卡多IP的主机上,尽管地址可能是一个网段内的,但其实地址间已经存在跳数了(hop),其实就是从一个地址向另一个地址跳. 这时广播/组播就容易被阻断. 
比如: 我们自己的WINDOWS上装一个VMWARE虚拟机,尽管IP地址是一个网段的,但因为虚拟机采用的桥模式不是标准的网桥模式(也可能是需要配置一下,但说实话懒得研究VMWARE了),所以广播/组播也经常出现不通的情况. 
更何况在一些云计算的环境,集群的分布往往是跨网段的,甚至是跨地域的.这时更难以依赖这种初级的组播同步. 
总之,分布式集群架构,建议EHCACHE改为PEER-2-PEER的同步方式.

举报
登录404
发帖于3个月前 0回/212阅
顶部