1. 程式人生 > >Netflix Eureka原始碼分析(13)——eureka server的登錄檔多級快取過期機制:主動過期+定時過期+被動過期

Netflix Eureka原始碼分析(13)——eureka server的登錄檔多級快取過期機制:主動過期+定時過期+被動過期

(1)主動過期

readWriteCacheMap,讀寫快取

有新的服務例項發生註冊、下線、故障的時候,就會去重新整理readWriteCacheMap

比如說現在有一個服務A,ServiceA,有一個新的服務例項,Instance010來註冊了,註冊完了之後,其實必須是得重新整理這個快取的,然後就會呼叫ResponseCache.invalidate(),將之前快取好的ALL_APPS這個key對應的快取,給它過期掉

將readWriteCacheMap中的ALL_APPS快取key,對應的快取給過期掉,比如新例項註冊時的程式碼邏輯如下:

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
 
    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
            //註冊的一些程式碼邏輯省略。。。
            //有新例項註冊時將之前的ALL_APPS對應的登錄檔快取過期掉
            invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
    }

    private void invalidateCache(String appName, @Nullable String vipAddress, @Nullable String secureVipAddress) {
        // invalidate cache
        responseCache.invalidate(appName, vipAddress, secureVipAddress);
    }
}
public class ResponseCacheImpl implements ResponseCache {
    
    //操作快取過期的實現方法
    @Override
    public void invalidate(String appName, @Nullable String vipAddress, @Nullable String secureVipAddress) {
        for (Key.KeyType type : Key.KeyType.values()) {
            for (Version v : Version.values()) {
                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)定時過期

readWriteCacheMap在構建的時候,指定了一個自動過期的時間,預設值就是180秒,所以你往readWriteCacheMap中放入一個數據過後,自動會等180秒過後,就將這個資料給他過期了

public class ResponseCacheImpl implements ResponseCache {

    private final LoadingCache<Key, Value> readWriteCacheMap;
    
    ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) 
    {
        this.readWriteCacheMap =
                CacheBuilder.newBuilder().initialCapacity(1000)
                        .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;
                            }
                        });
    }

    }
@Singleton
public class DefaultEurekaServerConfig implements EurekaServerConfig {
    
    //返回快取自動過期時間的配置項
    @Override
    public long getResponseCacheAutoExpirationInSeconds() {
        return configInstance.getIntProperty(
                namespace + "responseCacheAutoExpirationInSeconds", 180).get();
    }

}

(3)被動過期

readOnlyCacheMap怎麼過期呢?

預設是每隔30秒,呼叫getCacheUpdateTask方法,執行一個定時排程的執行緒任務,TimerTask,有一個邏輯,會每隔30秒,對readOnlyCacheMap和readWriteCacheMap中的資料進行一個比對,如果兩塊資料是不一致的,那麼就將readWriteCacheMap中的資料放到readOnlyCacheMap中來。

比如說readWriteCacheMap中,ALL_APPS這個key對應的快取沒了,那麼最多30秒過後,就會同步到readOnelyCacheMap中去

public class ResponseCacheImpl implements ResponseCache {

    private final LoadingCache<Key, Value> readWriteCacheMap;
    
    ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) 
    {
        if (shouldUseReadOnlyResponseCache) {
            timer.schedule(getCacheUpdateTask(),
                    new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
                            + responseCacheUpdateIntervalMs),
                    responseCacheUpdateIntervalMs);
        }
    }

     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);
                        //如果兩塊資料是不一致的,那麼就將readWriteCacheMap中的資料放到readOnlyCacheMap中來。
                        if (cacheValue != currentCacheValue) {
                            readOnlyCacheMap.put(key, cacheValue);
                        }
                    } catch (Throwable th) {
                        logger.error("Error while updating the client cache from response cache", th);
                    }
                }
            }
        };
    }
}

(4)很重要的問題

假設有服務例項註冊、下線、故障,要呼叫這個服務的其他服務,可能會過30秒之後才能感知到,為什麼呢?因為這裡在獲取服務登錄檔的時候,有一個多級快取的機制,最多是30秒才會去更新快取

總結一張 eureka-server的多級快取過期機制 流程圖