最近在做grails的项目中用到了缓存,遇到了一些问题,在这里记录一下啊。
首先,创建的grails工程默认会带有cache plugin。我用的是grails2.1.1,新建工程后会带一个插件:cache 1.0.0。这个插件是Spring提供的。具体如何使用在这http://grails-plugins.github.io/grails-cache/docs/manual/guide/introduction.html。这个插件的作者是Jeff Brown和Burt Beckwith
另外,Burt Beckwith还对cache 1.0.0进行了扩展,使用ehcache作为缓存的提供者。ehcache的使用和cahche plugin基本相同,具体请看这里:http://grails-plugins.github.io/grails-cache-ehcache/docs/manual/guide/introduction.html。
而我在做项目时,开始并没有注意这两个插件。而是自己引入ehcache的jar包来实现的。开始时我这样做的,遇到了一些问题:
1、grails2.1.1项目本身带有ehcache-core-2.4.6.jar,无需自己再引入。
2、复制配置文件ehcache.xml和ehcache.xsd。第一次我复制到了conf目录下
ehcache.xml中配置了一个cahche:
<cache name="advertisementCache"
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU" />
3、在conf/spring/resource.groovy中配置缓存bean
beans = {
//----------------------EhCache配置---------------------------begin
//EhCacheManagerFactoryBean创建的cacheManager是单例模式的
cacheManager (EhCacheManagerFactoryBean){bean->
shared=true
}//EhCacheManagerFactoryBean是一个FactoryBean,用来产生CacheManager
advertismentCache(EhCacheFactoryBean){//EhCacheFactoryBean是一个FactoryBean,用来产生Cache
cacheManager = ref("cacheManager")
cacheName = "advertisementCache"
}
clientVersionCache(EhCacheFactoryBean){//EhCacheFactoryBean是一个FactoryBean,用来产生Cache
cacheManager = ref("cacheManager")
cacheName = "clientVersionCache"
}
//---------------------EhCache配置---------------------------end
}
可以看到我对advertisementCache中的eternal设为true,即缓存中的元素用不过期。
可程序跑起来后,在执行下边的代码时出现了问题:
advertismentCache.keys.each {
Element element=advertismentCache.get(it)
if(element){
Advertisement advertisement=element.getObjectValue()
if(advertisement.photoStudioCode.equals(photoStudioCode)){
results.add(advertisement)
}
}
}
advertismentCache在程序启动时会存入一组数据。在程序启动后的一段时间里,上边的代码正常。一段时间后,advertismentCache.keys中有值,但进入循环Element element=advertismentCache.get(it)获取的element为null。再次执行代码,advertismentCache.keys中也没有了数据。
仔细查看ehcache的文档,得到了解释advertismentCache.keys获取的是缓存中所有的key,而不管这个元素没有过期(expired)。缓存中可能存在过期的元素。当遇到这种元素时,advertismentCache.get(it)返回的是null。同时会把过期的元素从缓存中清理掉。所有再次调用代码,advertismentCache.keys中也没有了数据。
那么看来是缓存中的数据过期了。可是我设置了eternal="true",数据应该永不过期才对。为啥没起作用?
继续google,在EhCacheManagerFactoryBean的源代码中看到了这样一段注释:
Set the location of the EHCache config file. A typical value is "/WEB-INF/ehcache.xml".
Default is "ehcache.xml" in the root of the class path, or if not found, "ehcache-failsafe.xml" in the EHCache jar (default EHCache initialization).
看来是ehcache.xml位置没有放对,默认使用了ehcache-failsafe.xml。后来的测试验证了我的猜想。于是我把ehcache.xml挪到了/web-app/WEB-INF下。同时修改resource.groovy,如下://EhCacheManagerFactoryBean创建的cacheManager是单例模式的
cacheManager (EhCacheManagerFactoryBean){bean->
shared=true
configLocation="/WEB-INF/ehcache.xml"
}//EhCacheManagerFactoryBean是一个FactoryBean,用来产生CacheManager
advertismentCache(EhCacheFactoryBean){//EhCacheFactoryBean是一个FactoryBean,用来产生Cache
cacheManager = ref("cacheManager")
cacheName = "advertisementCache"
}
clientVersionCache(EhCacheFactoryBean){//EhCacheFactoryBean是一个FactoryBean,用来产生Cache
cacheManager = ref("cacheManager")
cacheName = "clientVersionCache"
}
再次运行一切正常。之所以没方队class path下,是因为grails工程中,往classpath下放不太好放。没去研究。有知道的可以告诉我!
(PS:写的挺简单有条理,可这个问题整整折腾了我1天半。做程序员,如何准确的定位问题,是个难题啊)