通过前面章节的介绍, 可以明确知道在InstanceRegistry初始化的时候, 会初始化ResponseCacheImpl的类,而这个类就是对于三级缓存的重要实现. 这章节主要介绍三级缓存的工作原理,以代码的形式明确三级缓存的实现。
三级缓存工作模式
缓存初始化
缓存对象初始化中, 对缓存初始化,具体源码如下:
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
// eureka server配置信息, 主要以eureka.server开头, 在spring中,主要以EurekaServerConfigBean实现
this.serverConfig = serverConfig;
this.serverCodecs = serverCodecs;
// 当前配置定义了是否开启readOnlyResponseCache, 默认值为true
// 配置信息为:eureka.server.read-only-response-cache = true
this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
// 当前InstanceRegistry对象, 内部存储instance信息采用的ConccurentHashMap
this.registry = registry;
// 更新responseCache缓存的时间, 该事件默认值为: 30秒
long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
// readWriteCacheMap二级缓存初始化, 采用的guava的是LoadingCache的实现
// 在build方法中,定义了CacheLoader对象,用于在通过Key信息获取实例失败时,将会从InstanceRegistry中获取注册的实例信息
this.readWriteCacheMap =
CacheBuilder.newBuilder().initialCapacity(serverConfig.getInitialCapacityOfResponseCache())
.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);
}
}
})
.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;
}
});
// 该处判断是否启用readOnlyResponseCache, 如果启用,通过定时任务的方式更新readOnlyCache缓存信息, 每隔30秒执行一次
if (shouldUseReadOnlyResponseCache) {
timer.schedule(getCacheUpdateTask(),
new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
+ responseCacheUpdateIntervalMs),
responseCacheUpdateIntervalMs);
}
try {
// 注册JMX监控信息
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register the JMX monitor for the InstanceRegistry", e);
}
}
readWriteCacheMap
readWriteCacheMap作为二级缓存实现, 在构建时,主要包含了两个部分的定义:
- 数据过期时间: 通过
eureka.server.response-cache-auto-expireation-in-seconds, 默认值为180秒 - 加载Registry数据到
readWriteCacheMap: key的加载触发时机主要在于通过Key获取Value时,如果Key不存在,则从InstanceRegistry中加载 - 通过Guava的
LoadingCache实现, 缓存Key过期通过expireAfterWrite方式设置, 则从Key写入写入之后, 180秒后过期
readOnlyCacheMap
ReadOnlyCache本身是一个ConcurrentHashMap,存储内容为Key -> Value. readOnlyCacheMap中本身包含了定时任务清理缓存中的内容. UpdateTask有以下几点:
ReadOnlyCacheMap中的数据过期为30秒ReadOnlyCacheMap可以通过配置文件关闭:eureka.server.read-only-response-cache = true.关闭之后,则不开启定时任务
this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
this.registry = registry;
long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
...
if (shouldUseReadOnlyResponseCache) {
// 如果开启了readOnlyResponseCache, 则创建定时任务,每30秒执行一次
timer.schedule(getCacheUpdateTask(),
new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
+ responseCacheUpdateIntervalMs),
responseCacheUpdateIntervalMs);
}
updateTask
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()) {
logger.debug("Updating the client cache from response cache for key : {} {} {} {}",
key.getEntityType(), key.getName(), key.getVersion(), key.getType());
}
try {
CurrentRequestVersion.set(key.getVersion());
// 从readWriteCacheMap中获取对应key的value值
Value cacheValue = readWriteCacheMap.get(key);
// 从readOnlyCacheMap中获取value值
Value currentCacheValue = readOnlyCacheMap.get(key);
// 两者Value不相等,说明readWriteCacheMap中的值已被更新,以readWriteCacheMap为准
if (cacheValue != currentCacheValue) {
// 更新readOnlyCacheMap
readOnlyCacheMap.put(key, cacheValue);
}
} catch (Throwable th) {
logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th);
}
}
}
};
}
更新任务中,只是将当前readOnlyCacheMap中的数据与readWriteCacheMap中的数据进行比对, 不相等,就更新readOnlyCacheMap中的数据。
因此完整三级缓存流程图如下:



