十、Spring3.2+Ehcache2.10.2分布式缓存的使用
10.1 Ehcache集群简介
从Ehcache1.2版本开始,Ehcache就可以使用分布式的缓存了,从 1.7版本开始,开始支持共五种集群方案,分别是:
Terracotta
RMI
JMS
JGroups
EhCache Server
其中有三种上最为常用集群方式,分别是 RMI、JGroups 以及 EhCache Server 。
其实我们在使用Ehcache分布式缓存的过程中,主要是以缓存插件的方式使用,如果我们想根据自己的需要使用分布式缓存那就需要自己开发来定制化,在后面我们会发现其实Ehcache提供的分布式缓存并不是非常好用,有不少问题存在,所以对缓存数据一致性比较高的情况下,使用集中式缓存更合适,比如Redis、Memcached等。
10.2 Ehcache集群的基本概念
1、成员发现(Peer Discovery)
Ehcache集群概念中有一个cache组,每个cache都是另一个cache的peer,并不像Redis或者其他分布式组件一样有一个主的存在,Ehcache并没有主Cache,可是那如何知道集群中的其他缓存都有谁呢?这个就是成员发现。
Ehcache提供了二种机制来实现成员发现功能,分别是手动发现和自动发现。
手动发现
在Ehcache的配置文件中指定cacheManagerPeerProviderFactory元素的class属性为 net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory。这就需要自己去配置IP地址和端口号。自动发现
自动的发现方式用TCP广播机制来确定和维持一个广播组。它只需要一个简单的配置可以自动的在组中添加和移除成员。在集群中也不需要什么优化服务器的知识,这是默认推荐的。
成员每秒向群组发送一个“心跳”。如果一个成员 5秒种都没有发出信号它将被群组移除。如果一个新的成员发送了一个“心跳”它将被添加进群组。
任何一个用这个配置安装了复制功能的cache都将被其他的成员发现并标识为可用状态。
要设置自动的成员发现,需要指定ehcache配置文件中
cacheManagerPeerProviderFactory元素的properties属性,就像下面这样: peerDiscovery=automatic multicastGroupAddress=multicast address | multicast host name multicastGroupPort=port timeToLive=0-255 (timeToLive属性详见常见问题部分的描述)
10.3 结合Spring看示例
先看Spring配置文件:
<!-- spring cache 配置 --> <!-- 启用缓存注解功能 --> <cache:annotation-driven cache-manager="cacheManager"/> <!-- cacheManager工厂类,指定ehcache.xml的位置 --> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache/ehcache.xml"/> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehcache"/> <cache:annotation-driven />
Ehcache配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!--EHCache分布式缓存集群环境配置--> <!--rmi手动配置--> <cacheManagerPeerProviderFactory class= "net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual,rmiUrls=//localhost:40000/user"/> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=localhost,port=40001, socketTimeoutMillis=120000"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/> </defaultCache> <cache name="user" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="100000" timeToLiveSeconds="100000" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/> </cache> </ehcache>
以上配置其实就是使用RMI方式在集群的环境进行缓存数据的复制。
十一、Ehcache的使用场景
11.1、Ehcache使用的注意点
1、比较少的更新数据表的情况
2、对并发要求不是很严格的情况
多台应用服务器中的缓存是不能进行实时同步的。
3、对一致性要求不高的情况下
因为Ehcache本地缓存的特性,目前无法很好的解决不同服务器间缓存同步的问题,所以我们在一致性要求非常高的场合下,尽量使用Redis、Memcached等集中式缓存。
11.2、Ehcache在集群、分布式的情况下表现如何
在分布式情况下有二种同步方式:
1、RMI组播方式

Paste_Image.png
示例:
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=automatic, multicastGroupAddress=localhost, multicastGroupPort=4446,timeToLive=255"/>
原理:当缓存改变时,ehcache会向组播IP地址和端口号发送RMI UDP组播包。
缺陷:Ehcache的组播做得比较初级,功能只是基本实现(比如简单的一个HUB,接两台单网卡的服务器,互相之间组播同步就没问题),对一些复杂的环境(比如多台服务器,每台服务器上多地址,尤其是集群,存在一个集群地址带多个物理机,每台物理机又带多个虚拟站的子地址),就容易出现问题。
2、P2P方式
原理:P2P要求每个节点的Ehcache都要指向其他的N-1个节点。
3、JMS消息模式

Paste_Image.png
原理:这种模式的核心就是一个消息队列,每个应用节点都订阅预先定义好的主题,同时,节点有元素更新时,也会发布更新元素到主题中去。各个应用服务器节点通过侦听MQ获取到最新的数据,然后分别更新自己的Ehcache缓存,Ehcache默认支持ActiveMQ,我们也可以通过自定义组件的方式实现类似Kafka,RabbitMQ。
4、Cache Server模式
原理:这种模式会存在主从节点。

Paste_Image.png
缺陷:缓存容易出现数据不一致的问题,
11.3、使用Ehcache的瓶颈是什么
1、缓存漂移(Cache Drift):每个应用节点只管理自己的缓存,在更新某个节点的时候,不会影响到其他的节点,这样数据之间可能就不同步了。
2、数据库瓶颈(Database Bottlenecks ):对于单实例的应用来说,缓存可以保护数据库的读风暴;但是,在集群的环境下,每一个应用节点都要定期保持数据最新,节点越多,要维持这样的情况对数据库的开销也越大。
11.4、实际工作中如何使用Ehcache
在实际工作中,我更多是将Ehcache作为与Redis配合的二级缓存。
第一种方式:

Paste_Image.png
注:
这种方式通过应用服务器的Ehcache定时轮询Redis缓存服务器更同步更新本地缓存,缺点是因为每台服务器定时Ehcache的时间不一样,那么不同服务器刷新最新缓存的时间也不一样,会产生数据不一致问题,对一致性要求不高可以使用。
第二种方式:

Paste_Image.png
注:
通过引入了MQ队列,使每台应用服务器的Ehcache同步侦听MQ消息,这样在一定程度上可以达到准同步更新数据,通过MQ推送或者拉取的方式,但是因为不同服务器之间的网络速度的原因,所以也不能完全达到强一致性。基于此原理使用Zookeeper等分布式协调通知组件也是如此。
总结:
1、使用二级缓存的好处是减少缓存数据的网络传输开销,当集中式缓存出现故障的时候,Ehcache等本地缓存依然能够支撑应用程序正常使用,增加了程序的健壮性。另外使用二级缓存策略可以在一定程度上阻止缓存穿透问题。
2、根据CAP原理我们可以知道,如果要使用强一致性缓存(根据自身业务决定),集中式缓存是最佳选择,如(Redis,Memcached等)。




