Ehcache缓存持久化

因为在项目中需要将Ehcache的缓存数据写入磁盘,搜索一些怎么样持久化Ehcache缓存的资料,根据获得的资料在开发过程中遇到蛮多的坑,逐一记录下来。

根据官网上的配置踩过的坑

在Ehcache的官网上,有关于怎么配置持久化的说明。详情见: Configuring Restartability and Persistence

然后,我按照常规的逻辑添加了一下的配置:

1
2
3
4
5
6
7
<cache name="indexCache"
eternal="true"
maxElementsInMemory="1"
overflowToDisk="true"
diskPersistent="true">
<persistence strategy="localRestartable"/>
</cache>

然后,报错:

1
Caused by: org.xml.sax.SAXException: null:17: Could not finish element <persistence>. Message was: net.sf.ehcache.config.InvalidConfigurationException: Cannot use both <persistence ...> and diskPersistent in a single cache configuration.

说明diskPersistent和persistence不能共存。修改配置后:

1
2
3
<cache name="indexCache">
<persistence strategy="localRestartable"/>
</cache>

启动,报错:

1
2
Caused by: net.sf.ehcache.config.InvalidConfigurationException: There is one error in your configuration:
* Cache 'indexCache' error: If your CacheManager has no maxBytesLocalHeap set, you need to either set maxEntriesLocalHeap or maxBytesLocalHeap at the Cache level

添加 maxEntriesLocalHeap 后如下:

1
2
3
<cache name="indexCache" maxEntriesLocalHeap="1000">
<persistence strategy="localRestartable"/>
</cache>

还是报错:You must use an enterprise version of Ehcache to successfully enable enterprise persistence.

原来需要用到BigMemory,但是BigMemory又是收费的。至此,第一次尝试失败。

根据网上资料踩过的坑

资料显示:我们要在每次使用cache.put()后再调用cache.flush(),这样就能够将索引写入到磁盘。同时在配置中开启eternal(永久有效),overflowToDisk(磁盘缓存), diskPersistent(持久化到磁盘)和预热机制

然后还要在web.xml中加入ShutdownListener的监听,这样可以保证在正常关闭的时候缓存数据成功写入磁盘。

所以有了以下配置:

1
2
3
4
5
<cache name="indexCache" maxElementsInMemory="1" eternal="true" overflowToDisk="true"
diskPersistent="true">
<bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true"/>
</cache>
1
2
3
4
@Bean
public ServletListenerRegistrationBean<ShutdownListener> testListenerRegistration() {
return new ServletListenerRegistrationBean<>(new ShutdownListener());
}

启动试验,发现只生成.data文件并没有生成.index文件。

跳出坑,解决问题

跟踪调试代码,发现cache.flush()调用的是CacheStore中的flush。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void flush() throws IOException {
if (authoritativeTier instanceof DiskStore && cacheConfiguration != null && cacheConfiguration.isClearOnFlush()) {
final Lock lock = daLock.writeLock();
lock.lock();
try {
cachingTier.clear();
((DiskStore)authoritativeTier).clearFaultedBit();
} finally {
lock.unlock();
}
} else {
authoritativeTier.flush();
}
}

程序总是进入if条件中,并没有调用到authoritativeTier.flush()方法。

修改配置:

1
2
3
4
5
<cache name="indexCache" maxElementsInMemory="1" eternal="true" overflowToDisk="true"
diskPersistent="true" clearOnFlush="false">
<bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true"/>
</cache>

重新运行,OK。

以上基于spring boot 1.5.4.RELEASE,ehcache的版本为2.10.4