以ehcache为例
spring boot和ehcache结合后,ehcache的配置文件是写死超时时间的。
举例如下:
<diskStore path="/tmp/dcep"/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=1"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="socketTimeoutMillis=2000"/>
<cache name="properties"
maxBytesLocalHeap="100M"
eternal="false"
overflowToDisk="false"
timeToLiveSeconds="100000"
copyOnRead="true"
copyOnWrite="true">
<!-- 以下配置ehcache cluster,集群具有逐出、互相通信等功能,
仅能做有限的分布式一致性,作为一级缓存,不建议配置集群功能,ehcache 不建议使用在对一致性要求高的地方 -->
<!-- <cacheEventListenerFactory -->
<!-- class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" -->
<!-- properties="replicateAsynchronously=false, -->
<!-- replicatePuts=false, -->
<!-- replicateUpdates=false, -->
<!-- replicateUpdatesViaCopy=false, -->
<!-- replicateRemovals=true "/> -->
</cache>
文中timeToLiveSeconds就是超时时间。代码中都配置使用这个properties的cache的时候,就会同时在timeToLiveSeconds后失效,同时访问数据库,造成缓存雪崩。
解决方案:
@Bean
public CacheManagerCustomizers cacheManagerCustomizers(EhCachePropertiesConfig ehCachePropertiesConfig) {
return new CacheManagerCustomizers(
Arrays.asList(ehCacheCacheManager->{
if(ehCacheCacheManager instanceof EhCacheCacheManager){
net.sf.ehcache.CacheManager nativeEhCacheManager = ((EhCacheCacheManager)ehCacheCacheManager).getCacheManager();
ehCachePropertiesConfig.reSetConfig(nativeEhCacheManager);
}
})
);
}
生成一个CacheManagerCustomizers的bean。spring boot会判断该bean是否存在,如果存在,则使用用户生成的这个bean。
在生成ehcachecachemanager后,可以通过这个bean对cache进行修改。在上面代码中,通过这个类,对ehcachecachemanager进行cache配置的修改。
resetconfig代码如下:
@Component
public class EhCachePropertiesReset implements EhCachePropertiesConfig {
//向上波动最大值
int maxVariation = 10;
Random rand = new Random();
@Override
public void reSetConfig(CacheManager cacheManager) {
// TODO Auto-generated method stub
Map<String, CacheConfiguration> configByCacheName = cacheManager.getConfiguration().getCacheConfigurations();
configByCacheName.forEach((k,v)->{
long timeToLiveSeconds = v.getTimeToLiveSeconds();
if(timeToLiveSeconds != 0){
int variation = rand.ints(0, maxVariation).findFirst().orElse(0);
long newTimeLiveSeconds = timeToLiveSeconds + variation;
v.setTimeToLiveSeconds(newTimeLiveSeconds);
//这一步真正设置每一个cache的过期时间
cacheManager.getCache(k).getCacheConfiguration().setTimeToLiveSeconds(newTimeLiveSeconds);
}
});
}
}
通过随机化方法,对在配置文件中配置的数值进行随机化扰动。