通过前面章节的介绍, 可以明确知道在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
中的数据。
因此完整三级缓存流程图如下: