我想用Java中的ehcache做一些我认为应该非常简单的事情,但是我已经花了足够的时间来挫败自己的文档......
将值写入磁盘永久缓存。关机。
再次启动并阅读该值。
这是我的Java函数:
private static void testCacheWrite() {
// create the cache manager from our configuration
URL url = TestBed.class.getClass().getResource("/resource/ehcache.xml");
CacheManager manager = CacheManager.create(url);
// check to see if our cache exits, if it doesn't create it
Cache testCache = null;
if (!manager.cacheExists("test")) {
System.out.println("No cache found. Creating cache...");
int maxElements = 50000;
testCache = new Cache("test", maxElements,
MemoryStoreEvictionPolicy.LFU, true, null, true, 60, 30,
true, Cache.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null);
manager.addCache(testCache);
// add an element to persist
Element el = new Element("key", "value");
testCache.put(el);
testCache.flush();
System.out.println("Cache to disk. Cache size on disk: " +
testCache.getDiskStoreSize());
} else {
// cache exists so load it
testCache = manager.getCache("test");
Element el = testCache.get("key");
if (null == el) {
System.out.print("Value was null");
return;
}
String value = (String) el.getObjectValue();
System.out.println("Value is: " + value);
}
manager.shutdown();
}
这是我的缓存配置(ehcache.xml):
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="C:/mycache"/><!-- java.io.tmpdir -->
<defaultCache
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
即使我在第一次运行后在磁盘上看到test.index和test.data文件,但此函数的输出始终如下(它似乎永远不会从磁盘加载缓存):
找不到缓存。创建缓存...
缓存到磁盘。磁盘上的缓存大小:2
我必须在这里做点蠢事,但我不确定是什么!
答案 0 :(得分:16)
好的,我所做的就是使用配置文件配置我的缓存。这是更新的配置:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="C:/mycache" />
<defaultCache
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<cache
name="test"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="1"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
所以基本上我没有使用构造函数来定义缓存。
我认为这样可行,但我仍然想知道为什么编程定义的缓存不能在磁盘上保留(特别是因为它们仍然写入磁盘!)。
感谢你们的评论。
答案 1 :(得分:5)
在调试器上花了一些时间后,我相信我有一个OP的答案。
问题(至少从我所看到的)围绕非群集磁盘缓存文件及其如何重新读回。在文件net.sf.ehcache.store.compound.factories.DiskPersistentStorageFactory.java中,方法:
public DiskPersistentStorageFactory(Ehcache cache, String diskPath) {
super(getDataFile(diskPath, cache), cache.getCacheConfiguration().getDiskExpiryThreadIntervalSeconds(),
cache.getCacheConfiguration().getDiskSpoolBufferSizeMB(), cache.getCacheEventNotificationService(), false);
indexFile = new File(getDataFile().getParentFile(), getIndexFileName(cache));
flushTask = new IndexWriteTask(indexFile, cache.getCacheConfiguration().isClearOnFlush());
if (!getDataFile().exists() || (getDataFile().length() == 0)) {
LOG.debug("Matching data file missing (or empty) for index file. Deleting index file " + indexFile);
indexFile.delete();
} else if (getDataFile().exists() && indexFile.exists()) {
if (getDataFile().lastModified() > (indexFile.lastModified() + TimeUnit.SECONDS.toMillis(1))) {
LOG.warn("The index for data file {} is out of date, probably due to an unclean shutdown. "
+ "Deleting index file {}", getDataFile(), indexFile);
indexFile.delete();
}
}
diskCapacity = cache.getCacheConfiguration().getMaxElementsOnDisk();
memoryCapacity = cache.getCacheConfiguration().getMaxElementsInMemory();
memoryPolicy = determineEvictionPolicy(cache.getCacheConfiguration());
}
检查数据文件的时间戳。我看到的问题是,无论我最终如何关闭缓存/管理器,文件永远不会正确同步。我快速而又肮脏的解决方法是将数据文件的时间调整为刚刚超过索引文件的时间戳:
File index = new File( path, name + ".index" );
File data = new File( path, name + ".data" );
data.setLastModified( index.lastModified() + 1 );
当然,这并不优雅,但它满足了我的需求,因为我们的项目使用了集群缓存,这使我可以使用持久缓存独立调试......而无需在本地运行Terracotta。
有一点需要注意的是,对于非群集缓存,我必须在每次put()和remove()之后刷新()以保持磁盘映像新鲜,特别是在调试时由于缺少关闭支持只是“拉动插头”。
答案 2 :(得分:3)
我花了一些时间来弄清楚,但基本上需要做的是相应地创建CacheManager。
如果您创建缓存管理器和缓存的方式与在xml中创建它的方式相同,那么它将起作用。
net.sf.ehcache.CacheManager manager = net.sf.ehcache.CacheManager
.create(new Configuration().diskStore(
new DiskStoreConfiguration().path("C:/mycache")
)
.cache(new CacheConfiguration()
.name(testName)
.eternal(true)
.maxBytesLocalHeap(10000, MemoryUnit.BYTES)
.maxBytesLocalDisk(1000000, MemoryUnit.BYTES)
.diskExpiryThreadIntervalSeconds(0)
.diskPersistent(true)));
答案 3 :(得分:2)
这可能有点晚了,但我遇到了同样的问题:关闭缓存管理器的原因是什么。
(来自文件:http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration)
关闭单例CacheManager:
CacheManager.getInstance().shutdown();
关闭CacheManager实例,假设您有一个名为CacheManager的引用:
manager.shutdown();
答案 4 :(得分:1)
我认为您应该删除manager.cacheExists(..)
测试,只需使用testCache = manager.getCache("test");
创建缓存,而不是使用new Cache(..)
。即使您的缓存是diskPersistent,它也不会存在,直到您第一次获得它。 (至少那是我的想法,因为我只使用getCache(..)
而且它正是你正在寻找的东西)
注意:
您还可以添加类似的内容以确保缓存存在:
Cache cache = manager.getCache(name);
if (cache == null) {
throw new NullPointerException(String.format("no cache with name %s defined, please configure it in %s", name, url));
}
注2:
如果您的配置文件名为ehcache.xml,则不应使用我认为我使用CacheManager.create(url)
。而是使用CacheManager单例:CacheManager.create(url)
并使用new CacheManager(url)
时感到困惑。不过,你应该使用ehcache.xml
和new CacheManager(url)
的单身人士来做其他任何事情。
// ehcache.xml - shared between different invocations
CacheManager defaultManager = CacheManager.getInstance();
// others - avoid calling twice with same argument
CacheManager manager = CacheManager.create(url);
使用CacheManager.create(..)
是有问题的,因为如果以前调用了create(..)
个方法或getInstance()
,它可能会完全忽略传递的网址:
public static CacheManager create(URL configurationFileURL) throws CacheException {
synchronized (CacheManager.class) {
if (singleton == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new CacheManager with config URL: " + configurationFileURL);
}
singleton = new CacheManager(configurationFileURL);
}
return singleton;
}
}
这就是为什么我不建议使用任何CacheManager.create(..)
方法。使用CacheManager.getInstance()
或new CacheManager(url)
。
答案 5 :(得分:1)
如果磁盘上的缓存保持空白,则提示小提示:确保缓存中的元素是可序列化的。如果不是这种情况,ehcache会记录,但我的日志设置没有打印出这些日志条目。
答案 6 :(得分:1)
我已经解决了类似的问题。
我想配置ehcache以在磁盘上具有给定的缓存持久化元素。
但是我想只在本地环境中使用它(生产环境使用distributed
持久性),所以我在应用程序启动时以编程方式切换配置(在我的情况下是一个Web应用程序)
File configurationFile = new File(event.getServletContext().getRealPath(EHCACHE_CONFIG_PATH));
Configuration configuration = ConfigurationFactory.parseConfiguration(configurationFile);
//...doing other stuff here...
CacheConfiguration cacheConfiguration = configuration.getCacheConfigurations().get("mycachename");
if(localEnvironment){
cacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.DISTRIBUTED));
}else{
//siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE));
//deprecated lines..
siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);
}
我对注释行siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE))
有疑问,事实上,如果你使用ehcache-2.6.11
没有jar的企业版,那么Ehcache代码(我正在使用Strategy.LOCALRESTARTABLE
)会抛出异常:
CacheException: You must use an enterprise version of Ehcache to successfully enable enterprise persistence.
深入研究代码我意识到这两条(已弃用的)行做同样的事情,包括企业版本Exception
siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);
请记得在关闭应用程序时添加CacheManager.getInstance().shutdown()
!
希望这有帮助。
答案 7 :(得分:0)
我认为这样可行,但我仍然想知道为什么编程定义的缓存不能在磁盘上保留(特别是因为它们仍然写入磁盘!)
我的理解是,以编程方式创建的缓存(即未在ehcache.xml
中声明)可以使用本身可以持久的DiskStore
,但这并不意味着此缓存将由CacheManager
重新启动。实际上,我不认为前面提到的文件确实包含缓存参数。
但是,如果您使用相同的参数以编程方式“重新创建”缓存,则可以从DiskStore
找回以前缓存的条目。