EhCache 分布式缓存对象的同步
为了提升目前开发产品的性能,项目组内考虑将一些常用的数据放入缓存,并且今后要将系统分布式部署,以达到负载均衡的目的,因此缓存同步问题就不得不需要考虑,该项目中主要用EhCache产品,EhCache的优势和劣势这里就不做介绍,网上可以搜索,单从这次项目出发,说说他在项目中的应用,Hibernate和Spring都集成了EhCache,如果您的项目中用到这两个框架,那将会大大降低开发复杂度,如果没有使用此类框架,Ehcache还提供在项目中独立使用。
先大概了解一下EhCache的原理吧,见下图:
主要为三层,最上层的是CacheManager,它是操作Ehcache的入口。我们可以通过CacheManager.getInstance()(还有别的方法,可查看API)获得一个单例的CacheManger;每个CacheManager都管理着多个Cache;而每个Cache都以Hash的方式,关联着多个 Element;Element则是我们用于存放要缓存对象的地方。
先从EhCache在项目中独立使用的Demo说起:
开发环境:Eclipse4.0(不受限制)、JDK1.5;
引用Jar包:ehcache-core-2.5.2.jar
junit-3.8.1.jar
slf4j-api-1.6.1.jar
slf4j-jdk14-1.6.1.jar
新建一个Java Project,引入以上jar包,创建一个测试类TestPutElement,代码如下:
- package ehcache;
- import java.net.URL;
- import test.User;
- import junit.framework.TestCase;
- import net.sf.ehcache.Cache;
- import net.sf.ehcache.CacheManager;
- import net.sf.ehcache.Element;
- public class TestPutElement extends TestCase {
- public void testPut() throws Exception {
- URL url = TestPutCache.class.getClassLoader().getResource(
- "conf/ehcache.xml");
- CacheManager manager = new CacheManager(url);
- Cache cache = manager.getCache("metaCache");
- User user = new User();
- user.setName("张三");
- Element element = new Element("key",user);
- cache.put(element);
- manager.shutdown();
- System.out.println("已放入缓存!");
- }
- }
package ehcache; import java.net.URL; import test.User; import junit.framework.TestCase; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; public class TestPutElement extends TestCase { public void testPut() throws Exception { URL url = TestPutCache.class.getClassLoader().getResource( "conf/ehcache.xml"); CacheManager manager = new CacheManager(url); Cache cache = manager.getCache("metaCache"); User user = new User(); user.setName("张三"); Element element = new Element("key",user); cache.put(element); manager.shutdown(); System.out.println("已放入缓存!"); } }
创建一个POJO对象User,一定要经过序列化,并保证序列化UID的存在:
- import java.io.Serializable;
- public class User implements Serializable {
- private static final long serialVersionUID = -4402392412217726278L;
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = -4402392412217726278L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
在ClassPath目录下加入ehcache.xml文件,这也可以在Ehcachejar包中找到,有两个注意事项,不要删除defaultcache,如果是两台不同的电脑,那么把cacheManagerPeerProviderFactory中的第二个localhost换成另一台机器IP即可,端口号是可以自定义的,不过要注意不要和系统使用的端口冲突,不然会报错;
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="ehcache.xsd"
- updateCheck="true" monitoring="autodetect"
- dynamicConfig="true">
- <diskStore path="java.io.tmpdir" />
- <!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 -->
- <cacheManagerPeerProviderFactory
- class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
- properties="peerDiscovery=manual,rmiUrls=//localhost:40004/metaCache|//localhost:60000/metaCache" />
- <!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 -->
- <cacheManagerPeerListenerFactory
- class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
- properties="port=40004,socketTimeoutMillis=120000" />
- <!-- 默认缓存 -->
- <defaultCache maxElementsInMemory="10000" eternal="false"
- timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
- diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
- diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU">
- </defaultCache>
- <!-- 缓存 -->
- <cache name="metaCache"
- maxElementsInMemory="1000"
- eternal="false"
- timeToIdleSeconds="2000"
- timeToLiveSeconds="1000"
- overflowToDisk="false">
- <cacheEventListenerFactory
- class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
- <!-- <bootstrapCacheLoaderFactory
- class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
- properties="bootstrapAsynchronously=false" /> -->
- </cache>
- </ehcache>
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true"> <diskStore path="java.io.tmpdir" /> <!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 --> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual,rmiUrls=//localhost:40004/metaCache|//localhost:60000/metaCache" /> <!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 --> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="port=40004,socketTimeoutMillis=120000" /> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> <!-- 缓存 --> <cache name="metaCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="2000" timeToLiveSeconds="1000" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" /> <!-- <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" properties="bootstrapAsynchronously=false" /> --> </cache> </ehcache>
到此为止,放入缓存,也就是发送端配置好了,下面开始创建另个Java Project(),User类不变,必须要注意的是序列化UID和上一个Java Project要一致,否则会报ClassNotFound的错误,下面是测试类:
- package ehcache;
- import java.net.URL;
- import test.User;
- import junit.framework.TestCase;
- import net.sf.ehcache.Cache;
- import net.sf.ehcache.CacheManager;
- import net.sf.ehcache.Element;
- public class TestGetCache extends TestCase {
- public void testGet() throws Exception {
- URL url = TestGetCache.class.getClassLoader().getResource(
- "conf/ehcache.xml");
- CacheManager manager = new CacheManager(url);
- Cache cache = manager.getCache("metaCache");
- while (true) {
- System.out.println("搜索中...");
- System.out.println("当前资源数:" + cache.getSize());
- Element element = cache.get("key");
- if (element != null) {
- User user = (User)element.getValue();
- System.out.println(user.getName());
- break;
- }
- Thread.sleep(1000);
- }
- }
- }
package ehcache; import java.net.URL; import test.User; import junit.framework.TestCase; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; public class TestGetCache extends TestCase { public void testGet() throws Exception { URL url = TestGetCache.class.getClassLoader().getResource( "conf/ehcache.xml"); CacheManager manager = new CacheManager(url); Cache cache = manager.getCache("metaCache"); while (true) { System.out.println("搜索中..."); System.out.println("当前资源数:" + cache.getSize()); Element element = cache.get("key"); if (element != null) { User user = (User)element.getValue(); System.out.println(user.getName()); break; } Thread.sleep(1000); } } }ehcache.xml配置文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="ehcache.xsd"
- updateCheck="true" monitoring="autodetect"
- dynamicConfig="true">
- <diskStore path="java.io.tmpdir" />
- <!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 -->
- <cacheManagerPeerProviderFactory
- class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
- properties="peerDiscovery=manual,rmiUrls=//localhost:40004/metaCache|//localhost:60000/metaCache" />
- <!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 -->
- <cacheManagerPeerListenerFactory
- class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
- properties="port=40004,socketTimeoutMillis=120000" />
- <!-- 默认缓存 -->
- <defaultCache maxElementsInMemory="10000" eternal="false"
- timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
- diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
- diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU">
- </defaultCache>
- <!-- 缓存 -->
- <cache name="metaCache"
- maxElementsInMemory="1000"
- eternal="false"
- timeToIdleSeconds="2000"
- timeToLiveSeconds="1000"
- overflowToDisk="false">
- <cacheEventListenerFactory
- class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
- <!-- <bootstrapCacheLoaderFactory
- class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
- properties="bootstrapAsynchronously=true" /> -->
- </cache>
- </ehcache>
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true"> <diskStore path="java.io.tmpdir" /> <!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 --> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual,rmiUrls=//localhost:40004/metaCache|//localhost:60000/metaCache" /> <!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 --> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="port=40004,socketTimeoutMillis=120000" /> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> <!-- 缓存 --> <cache name="metaCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="2000" timeToLiveSeconds="1000" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" /> <!-- <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" properties="bootstrapAsynchronously=true" /> --> </cache> </ehcache>下面就可以测试了,首先运行后一个java Project项目,然后运行前一个java Project,运行结果为:1,张三。