转自:https://www.jianshu.com/p/ae4f0c8b8135
第一层缓存:readOnlyCacheMap,本质上是ConcurrentHashMap
第二层缓存:readWriteCacheMap,本质上是Guava缓存
缓存用途
Eureka Client获取注册列表时,首先判断是否开启了二级缓存,如果开启了则从readOnlyCacheMap中获取,否则从readWriteCacheMap中获取。
Eureka Server的缓存机制依赖于谷歌的gauva cache , 在Eureka中通过
com.netflix.eureka.registry.ResponseCacheImpl , 这个操作类来实现缓存的机制。
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
this.serverConfig = serverConfig;
this.serverCodecs = serverCodecs;
// 是否使用只读缓存
this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
this.registry = registry;
// 缓存更新的时间间隔(用定时器更新,定时器的时间默认30秒执行一次)
long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
// 构建读写缓存
this.readWriteCacheMap =
CacheBuilder.newBuilder().initialCapacity(1000)
.expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
.removalListener(new RemovalListener<Key, Value>() {
@Override
public void onRemoval(RemovalNotification<Key, Value> notification) {
Key removedKey = notification.getKey();
if (removedKey.hasRegions()) {
Key cloneWithNoRegions = removedKey.cloneWithoutRegions();
regionSpecificKeys.remove(cloneWithNoRegions, removedKey);
}
}
})
// 缓存加载器,当缓存不存在时,会自动执行load方法,进行缓存加载。同时返回缓存数据
.build(new CacheLoader<Key, Value>() {
@Override
public Value load(Key key) throws Exception {
if (key.hasRegions()) {
Key cloneWithNoRegions = key.cloneWithoutRegions();
regionSpecificKeys.put(cloneWithNoRegions, key);
}
Value value = generatePayload(key);
return value;
}
});
// 是否使用只读缓存,如果使用,此处则启动一个定时器,用来复制readWriteCacheMap 的数据至readOnlyCacheMap
if (shouldUseReadOnlyResponseCache) {
timer.schedule(getCacheUpdateTask(),
new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
+ responseCacheUpdateIntervalMs),
responseCacheUpdateIntervalMs);
}
try {
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register the JMX monitor for the InstanceRegistry", e);
}
}
通过上面可以很简单的看出, Eureka Server的缓存是通过一个只读,一个读写缓存来实现的。
readWriteCacheMap : 此处存放的是最终的缓存, 当服务下线,过期,注册,状态变更,都会来清除这个缓存里面的数据。 然后通过CacheLoader进行缓存加载,在进行readWriteCacheMap.get(key)的时候,首先看这个缓存里面有没有该数据,如果没有则通过CacheLoader的load方法去加载,加载成功之后将数据放入缓存,同时返回数据
readOnlyCacheMap : 这是一个JVM的CurrentHashMap只读缓存,这个主要是为了供客户端获取注册信息时使用,其缓存更新,依赖于定时器的更新,通过和readWriteCacheMap 的值做对比,如果数据不一致,则以readWriteCacheMap 的数据为准。
responseCacheUpdateIntervalMs : readOnlyCacheMap 缓存更新的定时器时间间隔,默认为30秒
responseCacheAutoExpirationInSeconds : readWriteCacheMap 缓存过期时间,默认为 180 秒 。
CacheUpdateTask
readOnlyCacheMap 定时器的任务执行类。
private TimerTask getCacheUpdateTask() {
return new TimerTask() {
@Override
public void run() {
logger.debug("Updating the client cache from response cache");
// 循环readOnlyCacheMap里面的KEY
for (Key key : readOnlyCacheMap.keySet()) {
if (logger.isDebugEnabled()) {
Object[] args = {key.getEntityType(), key.getName(), key.getVersion(), key.getType()};
logger.debug("Updating the client cache from response cache for key : {} {} {} {}", args);
}
try {
// 版本号
CurrentRequestVersion.set(key.getVersion());
// 从readWriteCacheMap获取数据
Value cacheValue = readWriteCacheMap.get(key);
// 当前的只读数据
Value currentCacheValue = readOnlyCacheMap.get(key);
// 判断数据是否一致
if (cacheValue != currentCacheValue) {
// 如果不一致,覆盖只读缓存里面的数据,以readWriteCacheMap为准
readOnlyCacheMap.put(key, cacheValue);
}
} catch (Throwable th) {
logger.error("Error while updating the client cache from response cache", th);
}
}
}
};
}
Eureka Client缓存机制
Eureka Client缓存机制很简单,设置了一个每30秒执行一次的定时任务,定时去服务端获取注册信息。获取之后,存入本地内存。