跳到主要内容

05、源码解析 Eureka 注册表源码解析

Eureka注册表源码解析

1、Eureka注册表整体流程图

 

1、Eureka注册表二级缓存源码解析

1.1 获取注册表流程源码入口

@GET
public Response getApplication(@PathParam("version") String version,
                               @HeaderParam("Accept") final String acceptHeader,
                               @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept) {
   
     
    if (!registry.shouldAllowAccess(false)) {
   
     
        return Response.status(Status.FORBIDDEN).build();
    }

    EurekaMonitors.GET_APPLICATION.increment();

    CurrentRequestVersion.set(Version.toEnum(version));
    KeyType keyType = Key.KeyType.JSON;
    if (acceptHeader == null || !acceptHeader.contains("json")) {
   
     
        keyType = Key.KeyType.XML;
    }

    Key cacheKey = new Key(
            Key.EntityType.Application,
            appName,
            keyType,
            CurrentRequestVersion.get(),
            EurekaAccept.fromString(eurekaAccept)
    );
	
    
    // 这个就是获取的方法,主要看一下这里
    String payLoad = responseCache.get(cacheKey);

    if (payLoad != null) {
   
     
        logger.debug("Found: {}", appName);
        return Response.ok(payLoad).build();
    } else {
   
     
        logger.debug("Not Found: {}", appName);
        return Response.status(Status.NOT_FOUND).build();
    }
}

1.2 getValue(获取缓存信息的方法)

@VisibleForTesting
Value getValue(final Key key, boolean useReadOnlyCache) {
   
     
    Value payload = null;
    try {
   
     
        //1、判断是否开启使用只读缓存
        if (useReadOnlyCache) {
   
     
            // 1.1 为真从 readOnlyCacehMap 中获取
            final Value currentPayload = readOnlyCacheMap.get(key);
            // 1.2 判断不为空,直接返回 
            if (currentPayload != null) {
   
     
                payload = currentPayload;
            } else {
   
     
                // 1.3 如果为空,则从 readWriteCacheMap 中获取,并写入到 readOnlyCacheMap 中
                payload = readWriteCacheMap.get(key);
                readOnlyCacheMap.put(key, payload);
            }
        } else {
   
     
            //2、没有开户则直接从 readWriteCacheMap 中获取
            payload = readWriteCacheMap.get(key);
        }
    } catch (Throwable t) {
   
     
        logger.error("Cannot get value for key :" + key, t);
    }
    return payload;
}

1.3 readOnlyCacheMap (只读缓存,Map,没什么特殊的逻辑,简单)

就是一个 ConcurrentHashMap,没有什么要说的。但是他有一个定时任务在实例化的时候就开启了

// ResponseCache 实例化的时候开启
// 开启更新 readOnlyCacheMap 的定时任务
if (shouldUseReadOnlyResponseCache) {
   
     
    timer.schedule(getCacheUpdateTask(),
                   // 30s 之后触发
                   new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
                            + responseCacheUpdateIntervalMs),
                   // 每隔 30s 触发一次
                   responseCacheUpdateIntervalMs);
}

1.4 readWriteCacheMap (Guava 实现的 Cache 组件,需要看一下具体的逻辑)

//ResponseCache 实例化的时候构造的

this.readWriteCacheMap =
    // 1、 初始大小是 1000
    CacheBuilder.newBuilder().initialCapacity(1000)
    // 2、180s 过期
    .expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
    // 3、注册了监听器
    .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;
        }
    });

1.5 AbstractInstanceRegistry(注册表,其实也是一个 Map,没什么特别的逻辑,比如简单)

2、Eureka注册表缓存过期机制源码解析

2.1 缓存主动过期

在com.netflix.eureka.registry.AbstractInstanceRegistry 中搜索 invalidateCache,可以发现有 4 个地方调用了失效缓存的方法

1、cancel

2、register

3、statusUpdate

4、deleteStatusOverride

所以这 4 种情况下都会去失效缓存,因为实例信息发生了变化,所以要把缓存失效掉

好,那我们来看一下失效缓存的方法

@Override
public void invalidate(String appName, @Nullable String vipAddress, @Nullable String secureVipAddress) {
   
     
    for (Key.KeyType type : Key.KeyType.values()) {
   
     
        for (Version v : Version.values()) {
   
     
            // 可以看到去失效 appName 对应的缓存,还有 ALL_APPS、ALL_APPS_DELTA 对应的缓存
            invalidate(
                    new Key(Key.EntityType.Application, appName, type, v, EurekaAccept.full),
                    new Key(Key.EntityType.Application, appName, type, v, EurekaAccept.compact),
                    new Key(Key.EntityType.Application, ALL_APPS, type, v, EurekaAccept.full),
                    new Key(Key.EntityType.Application, ALL_APPS, type, v, EurekaAccept.compact),
                    new Key(Key.EntityType.Application, ALL_APPS_DELTA, type, v, EurekaAccept.full),
                    new Key(Key.EntityType.Application, ALL_APPS_DELTA, type, v, EurekaAccept.compact)
            );
            if (null != vipAddress) {
   
     
                invalidate(new Key(Key.EntityType.VIP, vipAddress, type, v, EurekaAccept.full));
            }
            if (null != secureVipAddress) {
   
     
                invalidate(new Key(Key.EntityType.SVIP, secureVipAddress, type, v, EurekaAccept.full));
            }
        }
    }
}

2.2 缓存被动过期(定时任务)

readOnlyCacheMap 有一个定时任务,会去不断的去与 readWriteCacheMap 对比,不同的话则需要去覆盖自身的值

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 {
   
     
                    // 对比 readOnlyCacheMap 中的信息和 readWriteCacheMap 中的信息,如果不一致,则更新 readOnlyCacheMap 中的信息
                    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);
                }
            }
        }
    };
}

2.3 缓存定时过期(缓存声明时设置)

从readWriteCacheMap 的声明中我们知道有一个过期的控制选项

.expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)

版权声明:「DDKK.COM 弟弟快看,程序员编程资料站」本站文章,版权归原作者所有