Netflix Eureka -Eureka Server注册表多级缓存过期机制(主动、定时、被动)

109 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

  1. eureka server启动以及初始化

  2. eureka client启动以及初始化

  3. eureka client发送服务实例的注册

  4. eureka server接收服务实例的注册

  5. eureka client抓取全量注册表

  6. eureka server注册表多级缓存机制

  7. 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(分区容忍性)