本文已参与「新人创作礼」活动,一起开启掘金创作之路。
-
eureka server启动以及初始化
-
eureka client启动以及初始化
-
eureka client发送服务实例的注册
-
eureka server接收服务实例的注册
-
eureka client抓取全量注册表
-
eureka server注册表多级缓存机制
-
eureka server注册表多级缓存过期机制(主动、定时、被动)
eureka server注册表多级缓存过期机制
主动过期(readWriteCacheMap)
eureka server注册表多级缓存-读写缓存-主动过期。
存在服务实例发生注册、下线、故障的时候,会更新eureka server注册表,导致readWriteCacheMap缓存与注册表数据不一致。需要主动过期读写缓存。
注册服务实例导致主动过期
服务ServiceA,增加一个新的服务实例(Instance02),eureka client 发起注册请求,eureka server接收到之后将InstanceInfo 加入到注册表(registry)中,此时注册表(registry)与读写缓存(readWriteCacheMap)已经不一致了,必须调用ResponseCache.invalidate()刷新缓存,将之前缓存好的数据过期。
// AbstractInstanceRegistry.java
// 注册表多级缓存-读写缓存-主动过期
invalidateCache(registrant.getAppName(), registrant.getVIPAddress(),
registrant.getSecureVipAddress());
/**
* 调用 responseCache.invalidate 主动过期 cache
* @param appName
* @param vipAddress
* @param secureVipAddress
*/
private void invalidateCache(String appName, @Nullable String vipAddress,
@Nullable String secureVipAddress) {
// invalidate cache
responseCache.invalidate(appName, vipAddress, secureVipAddress);
}
下线服务实例导致主动过期
...
服务实例故障导致主动过期
...
定时过期(readWriteCacheMap)
读写缓存(readWriteCacheMap)使用的是LoadingCache,在构建的时候,可以通过expireAfterWrite()方法指定一个过期的时间。
eureka server 默认设置的是180秒。
expireAfterWrite()方法:当缓存项在指定的时间段内没有更新就会被回收
// ResponseCacheImpl.java
// 读写缓存
this.readWriteCacheMap =
CacheBuilder
.newBuilder()
.initialCapacity(1000)
// 当缓存项在指定的时间段内没有更新就会被回收
.expireAfterWrite(
// 注册表多级缓存-读写缓存-定时过期
// 180秒
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);
}
// 根据key获取payload
Value value = generatePayload(key);
return value;
}
});
被动过期(readOnlyCacheMap)
readOnlyCacheMap通过定时调度任务,默认每隔30秒执行一次getCacheUpdateTask() 的TimerTask任务。
对比readOnlyCacheMap和readWriteCacheMap中的数据,如果两块数据不一致,将readWriteCacheMap中的数据放到readOnlyCacheMap中。
比如readWriteCacheMap中,ALL_APPS这个key对应的缓存过期了,最多30秒过后,就会由readWriteCacheMap同步到readOnelyCacheMap中去
// ResponseCacheImpl.java
// 允许使用只读缓存(默认是true)
if (shouldUseReadOnlyResponseCache) {
// 注册表多级缓存-只读缓存-被动过期
// Timer调度器30秒执行一次只读缓存的过期逻辑`getCacheUpdateTask()`
timer.schedule(getCacheUpdateTask(),
// date 取30秒的整
new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
+ responseCacheUpdateIntervalMs),
// 30秒
responseCacheUpdateIntervalMs);
}
// ResponseCacheImpl.java
/**
* 只读缓存的过期逻辑
* 如果与读写缓存的数据不一致,以读写缓存数据为准更新只读缓存
* @return
*/
private TimerTask getCacheUpdateTask() {
return new TimerTask() {
@Override
public void run() {
logger.debug("Updating the client cache from response cache");
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());
Value cacheValue = readWriteCacheMap.get(key);
Value currentCacheValue = readOnlyCacheMap.get(key);
// 如果与读写缓存的数据不一致,以读写缓存数据为准更新只读缓存
if (cacheValue != currentCacheValue) {
// 更新只读缓存
readOnlyCacheMap.put(key, cacheValue);
}
} catch (Throwable th) {
logger.error("Error while updating the client cache from response cache",
th);
}
}
}
};
}
CAP理论的AP模型
由于多级缓存机制的存在,最多30秒才会去同步缓存。
当有服务实例发生注册、下线、故障等事件时,无法保证强一致性,只能保证最终一致性(可能会过30秒之后才能感知到)。
Consistency(一致性)
Availability(可用性)
Partition tolerance(分区容忍性)