新网Logo
首页>域名资讯>

Ehcache 一台服务器多个Tomcat组成的集群间的缓存同步实践

登录 注册

Ehcache 一台服务器多个Tomcat组成的集群间的缓存同步实践

摘要:摘要: EhCache是一个开源的,基于标准实现的高性能缓存框架。减轻系统对数据库的压力。作为一个强大的、成熟的、功能齐全的解决方案,它是当今最广泛使用的基于java的缓存。你可以使用它作为一个通用的缓存或为Hibernate提供二级缓存,还可以将它与第三方产品集成,如:ColdFusion,googleApp Engine和Spring。 实际工作中,为节约硬件成本,时常一台服务器部署多个tom

Tomcat集群部署情况说明

两台Web服务器,每台服务器部署两个tomcat

47R58PIC873_1024.jpg

server1 192.168.178.101

tomcat1 tomcat2

server2 192.168.178.102

tomcat3 tomcat4

采用Ehcache的RMI方式来实现缓存同步复制

方式一:自动发现集群成员

ehcache.xml配置如下:


<?xmlversion="1.0"encoding="UTF-8"?>

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

xsi:noNamespaceSchemaLocation="ehcache.xsd"

updateCheck="true"monitoring="autodetect"

dynamicConfig="true">

<cacheManagerPeerProviderFactory

class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"

properties="peerDiscovery=automatic,

multicastGroupAddress=230.0.0.1,

multicastGroupPort=4446

timeToLive=1"/>

<cacheManagerPeerListenerFactory

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

<cachename="Cache1"

maxElementsInMemory="100"

eternal="true"

overflowToDisk="false"

memoryStoreEvictionPolicy="LFU">

<cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>

</cache>

</ehcache>


PS:

a、配置简单,每个tomcat使用完全相同的ehcache配置;

b、通过多播( multicast )来维护集群中的所有有效节点。这也是最为简单而且灵活的方式,与手工模式不同的是,每个节点上的配置信息都相同,大大方便了节点的部署,避免人为的错漏出现。

c、timeToLive的值指的是数据包可以传递的域或是范围。约定如下:

0是限制在同一个服务器

1是限制在同一个子网

32是限制在同一个网站

64是限制在同一个region

128是限制在同一个大洲

255是不限制

在Java实现中默认值是1,也就是在同一个子网中传播。改变timeToLive属性可以限制或是扩展传播的范围。

d、自动的peer discovery与广播息息相关。广播可能被路由阻拦,像Xen和VMWare这种虚拟化的技术也可以阻拦广播(阿里云主机好像也不提供广播,阿里云服务器上部署时可采用手动配置成员发现)。

如果这些都打开了,你可能还在要将你的网卡的相关配置打开。一个简单的办法可以告诉广播是否有效,那就是使用ehcache remote debugger来看“心跳”是否可用。

 

方式二:手动配置发现集群成员

ehcache.xml配置如下:


<?xmlversion="1.0"encoding="UTF-8"?>

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

xsi:noNamespaceSchemaLocation="ehcache.xsd"

updateCheck="true"monitoring="autodetect"

dynamicConfig="true">

<!--RMI方式二:手动成员发现配置:ip+端口号,手动指定需要同步的server和cachename-->

<cacheManagerPeerProviderFactory

class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"

properties="peerDiscovery=manual,

rmiUrls=

//192.168.178.101:50001/Cache1|

//192.168.178.101:50001/Cache2|

//192.168.178.102:40001/Cache1|

//192.168.178.102:40001/Cache2|

//192.168.178.102:50001/Cache1|

//192.168.178.102:50001/Cache2"/>

<cacheManagerPeerListenerFactory

class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"

properties="hostName=192.168.178.101,port=40001,socketTimeoutMillis=2000"/>

<cachename="Cache1"

maxElementsInMemory="1000"

eternal="true"

overflowToDisk="false"

memoryStoreEvictionPolicy="LFU">

<cacheEventListenerFactory

class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"

properties="

replicateAsynchronously=true,

replicatePuts=true,

replicateUpdates=true,

replicateUpdatesViaCopy=false,

replicateRemovals=true"/>

<!--用于在初始化缓存,以及自动设置-->

<bootstrapCacheLoaderFactoryclass="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>

</cache>

<cachename="Cache2"

maxElementsInMemory="2000"

eternal="true"

overflowToDisk="false"

memoryStoreEvictionPolicy="LFU">

<cacheEventListenerFactory

class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"

properties="

replicateAsynchronously=true,

replicatePuts=true,

replicateUpdates=true,

replicateUpdatesViaCopy=false,

replicateRemovals=true"/>

<!--用于在初始化缓存,以及自动设置-->

<bootstrapCacheLoaderFactoryclass="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>

</cache>

</ehcache>


PS:

a、每个tomcat的配置文件都不一样,部署集群时有点繁琐;

b、配置cacheManagerPeerProviderFactory,rmiUrls配置需要同步的各个集群节点列表(不包括本服务器节点);

上面示例配置文件是tomcat1的配置,rmiUrls列表配置tomcat2,tomcat3,tomcat4需要同步的缓存项,每个缓存项的配置格式://server:port/cacheName

同理,tomcat2的配置中,rmiUrls列表就需要配置tomcat1,tomcat3,tomcat4的缓存项;tomcat3,tomcat4以此类推;

c、配置cacheManagerPeerListenerFactory,本节点的缓存监听配置,属性中需指定本节点IP或域名、监听端口号、socket通信超时时间

hostName=192.168.178.101,port=40001,socketTimeoutMillis=2000

同理:tomcat2配置:hostName=192.168.178.101,port=50001,socketTimeoutMillis=2000

tomcat3配置:hostName=192.168.178.102,port=40001,socketTimeoutMillis=2000

tomcat4配置:hostName=192.168.178.102,port=50001,socketTimeoutMillis=2000

d、配置具体的cache,需要配置cacheEventListenerFactory,指定哪些操作时需要replicate cache(同步复制缓存)

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

Ehcahce官方文档中的描述:

Thefactoryrecognisesthefollowingproperties:

replicatePuts=true|false-whethernewelementsplacedinacachearereplicatedtoothers.Defaultstotrue.

replicateUpdates=true|false-whethernewelementswhichoverrideanelementalreadyexistingwiththesamekeyarereplicated.Defaultstotrue.

replicateRemovals=true-whetherelementremovalsarereplicated.Defaultstotrue.

replicateAsynchronously=true|false-whetherreplicationsareasyncrhonous(true)orsynchronous(false).Defaultstotrue.

replicateUpdatesViaCopy=true|false-whetherthenewelementsarecopiedtoothercaches(true),orwhetheraremovemessageissent.Defaultstotrue.

 

调用Ehcahe提供的API,编码缓存的加载及获取,更新

引入jar包:ehcache.jar

放入缓存的对象必须是可序列化的,即必须实现接口Serializable

 

示例代码:


publicclassImeiWhiteListCacheHelper{

privatestaticfinalStringIMEI_CACHE="IMEICache";

privatestaticImeiWhiteListDAOimeiDao=newImeiWhiteListDAO();

privatestaticCachecache;

static{

cache=CacheManager.getInstance().getCache(IMEI_CACHE);

}

/**

*重新加载IMEI白名单到缓存中

*

*@throwsSQLException

*/

publicstaticvoidreloadImeiWhiteListToCache()throwsSQLException{

synchronized(cache){

cache.removeAll();

List<ImeiWhiteListDTO>list=imeiDao.getAllImeiWhiteList();

for(ImeiWhiteListDTOimeiWhiteListDTO:list){

Elemente=newElement(imeiWhiteListDTO.getImei(),imeiWhiteListDTO);

cache.put(e);

}

}

}

/**

*从缓存中获取某个IMEI的信息。如缓存获取失败,从DB中读取,再放入缓存

*

*@paramimei

*@return

*@throwsSQLException

*/

publicstaticImeiWhiteListDTOgetImeiInfo(Stringimei)throwsSQLException{

ImeiWhiteListDTOimeiInfo=null;

synchronized(cache){

Elementelement=cache.get(imei);

if(element!=null){

//System.out.println("hitcount:"+element.getHitCount());

imeiInfo=(ImeiWhiteListDTO)element.getObjectValue();

}else{

imeiInfo=imeiDao.getModelByIMEI(imei);

if(imeiInfo!=null){

cache.put(newElement(imeiInfo.getImei(),imeiInfo));

}

}

}

returnimeiInfo;

}