深入理解Eureka獲取注冊信息(七)

Eureka-Client獲取信息

啟動獲取

在客戶端應(yīng)用啟動時歪沃,初始化DiscoverClient的時候嗦锐,會主動去獲取一次注冊信息

@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                Provider<BackupRegistry> backupRegistryProvider) {
    // ...省略N多代碼
    // 如果fetch-registry = true , 則去Eureka Server拉取注冊信息
    if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
         // 如果所有的Eureka Server都不可用,那么從備用的服務(wù)里面去取數(shù)據(jù)
         fetchRegistryFromBackup();
    }
    // ...省略N多代碼
    // 設(shè)置定時器
    initScheduledTasks();
}

shouldFetchRegistry : 默認true
fetchRegistry : 獲取注冊信息沪曙,此處傳入的是false奕污, 表面上看是不需要全量獲取,但是應(yīng)用第一次啟動的時候液走,
本地緩存為空碳默,所以還是會全量獲取的。

PS: 啟動時獲取注冊信息為全量缘眶。

定時器獲取

private void initScheduledTasks() {
    if (clientConfig.shouldFetchRegistry()) {
        // registry cache refresh timer
        int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
        int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
        scheduler.schedule(
                new TimedSupervisorTask(
                        "cacheRefresh",
                        scheduler,
                        cacheRefreshExecutor,
                        registryFetchIntervalSeconds,
                        TimeUnit.SECONDS,
                        expBackOffBound,
                        new CacheRefreshThread()
                ),
                registryFetchIntervalSeconds, TimeUnit.SECONDS);
    }
    // ...省略N多代碼
}

registryFetchIntervalSeconds : 默認值為30秒 嘱根,每30秒刷新一次、

定時器初始化巷懈,该抒,直接看CacheRefreshThread()

class CacheRefreshThread implements Runnable {
    public void run() {
        // 刷新注冊信息
        refreshRegistry();
    }
}
void refreshRegistry() {
    try {
        boolean isFetchingRemoteRegionRegistries = isFetchingRemoteRegionRegistries();

        boolean remoteRegionsModified = false;
        
        // 判斷是否需要全量獲取 , remoteRegionsModified  這個值來決定
        String latestRemoteRegions = clientConfig.fetchRegistryForRemoteRegions();
        if (null != latestRemoteRegions) {
            String currentRemoteRegions = remoteRegionsToFetch.get();
            if (!latestRemoteRegions.equals(currentRemoteRegions)) {
                // Both remoteRegionsToFetch and AzToRegionMapper.regionsToFetch need to be in sync
                synchronized (instanceRegionChecker.getAzToRegionMapper()) {
                    if (remoteRegionsToFetch.compareAndSet(currentRemoteRegions, latestRemoteRegions)) {
                        String[] remoteRegions = latestRemoteRegions.split(",");
                        remoteRegionsRef.set(remoteRegions);
                        instanceRegionChecker.getAzToRegionMapper().setRegionsToFetch(remoteRegions);
                        remoteRegionsModified = true;
                    } else {
                        logger.info("Remote regions to fetch modified concurrently," +
                                " ignoring change from {} to {}", currentRemoteRegions, latestRemoteRegions);
                    }
                }
            } else {
                // Just refresh mapping to reflect any DNS/Property change
                instanceRegionChecker.getAzToRegionMapper().refreshMapping();
            }
        }
        // 獲取注冊信息
        boolean success = fetchRegistry(remoteRegionsModified);
        if (success) {
            registrySize = localRegionApps.get().size();
            lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
        }
        // 日志輸出 顶燕, 省略凑保。。
        
    } catch (Throwable e) {
        logger.error("Cannot fetch registry from server", e);
    }        
}

由上可以看到涌攻,系統(tǒng)在啟動的時候欧引,初始化了一個定時器,每30秒一次恳谎,用來刷新本地緩存信息芝此。

獲取注冊信息

private boolean fetchRegistry(boolean forceFullRegistryFetch) {
    Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

    try {
        // 獲取本地的緩存信息 , 也就是客戶端注冊信息
        Applications applications = getApplications();
        
        // 判斷是否需要全量獲取
        if (clientConfig.shouldDisableDelta()
                || (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
                || forceFullRegistryFetch
                || (applications == null)
                || (applications.getRegisteredApplications().size() == 0)
                || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
        {
            logger.info("Disable delta property : {}", clientConfig.shouldDisableDelta());
            logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
            logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
            logger.info("Application is null : {}", (applications == null));
            logger.info("Registered Applications size is zero : {}",
                    (applications.getRegisteredApplications().size() == 0));
            logger.info("Application version is -1: {}", (applications.getVersion() == -1));
            getAndStoreFullRegistry();
        } else {
            // 增量獲取
            getAndUpdateDelta(applications);
        }
        applications.setAppsHashCode(applications.getReconcileHashCode());
        logTotalInstances();
    } catch (Throwable e) {
        logger.error(PREFIX + appPathIdentifier + " - was unable to refresh its cache! status = " + e.getMessage(), e);
        return false;
    } finally {
        if (tracer != null) {
            tracer.stop();
        }
    }

    // 發(fā)布緩存刷新事件。
    onCacheRefreshed();

    // 更新本地應(yīng)用的狀態(tài)
    updateInstanceRemoteStatus();

    // registry was fetched successfully, so return true
    return true;
}

clientConfig.shouldDisableDelta() : 是否禁用增量獲取癌蓖, 默認為false , 如果禁用了的話婚肆,那就只能是全量獲取了租副,總要獲取一下不是。
clientConfig.getRegistryRefreshSingleVipAddress() : 當(dāng)這個屬性不為空的時候较性,則全量獲取用僧。具體作用不是很清楚(苦笑)
forceFullRegistryFetch : 傳入的參數(shù),表示是否需要全量獲取
applications : 本地注冊信息的緩存赞咙,如果本地緩存為空责循,或者里面的版本號為-1,那么就需要全量獲取攀操,表示首次加載時院仿。
onCacheRefreshed() : 發(fā)布緩存刷新的事件,用戶可以自定義是否監(jiān)聽這個事件速和,比如需要將注冊信息的變化落庫歹垫。

全量獲取
private void getAndStoreFullRegistry() throws Throwable {
    long currentUpdateGeneration = fetchRegistryGeneration.get();

    logger.info("Getting all instance registry info from the eureka server");

    Applications apps = null;
    // 發(fā)送HTTP請求,去服務(wù)端獲取注冊信息
    EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
            ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
            : eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        apps = httpResponse.getEntity();
    }
    logger.info("The response status is {}", httpResponse.getStatusCode());

    if (apps == null) {
        logger.error("The application is null for some reason. Not storing this information");
    } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
        // 設(shè)置到本地緩存里面去
        localRegionApps.set(this.filterAndShuffle(apps));
        logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
    } else {
        logger.warn("Not updating applications as another thread is updating it already");
    }
}

在發(fā)送HTTP請求去服務(wù)端獲取注冊信息之前颠放,做了一個判斷排惨, 判斷registryRefreshSingleVipAddress是否為空, 這個字段

表示的意思是 “此客戶端只對一個單一的VIP注冊表的信息感興趣”碰凶,默認為null , 也就是說如果客戶端只對其中一個VIP 感興趣
那么就只獲取這一個暮芭, 否則全部獲取

this.filterAndShuffle(apps) : 是否需要過濾客戶端信息的狀態(tài),如果設(shè)置了eureka.shouldFilterOnlyUpInstances = true 這個屬性的話欲低,

客戶端獲取到注冊信息之后辕宏,會剔除非UP狀態(tài)的客戶端信息。

localRegionApps.set(this.filterAndShuffle(apps)) : 將注冊信息設(shè)置到本地內(nèi)存里面去伸头,使用AtomicReference類型做存儲匾效、

private final AtomicReference<Applications> localRegionApps = new AtomicReference<Applications>();
增量獲取
private void getAndUpdateDelta(Applications applications) throws Throwable {
    long currentUpdateGeneration = fetchRegistryGeneration.get();

    Applications delta = null;
    // 增量獲取信息
    EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        delta = httpResponse.getEntity();
    }

    if (delta == null) {
        // 增量獲取為空,則全量返回
        logger.warn("The server does not allow the delta revision to be applied because it is not safe. "
                + "Hence got the full registry.");
        getAndStoreFullRegistry();
    } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
        logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
        String reconcileHashCode = "";
        //這里設(shè)置原子鎖的原因是怕某次調(diào)度網(wǎng)絡(luò)請求時間過長恤磷,導(dǎo)致同一時間有多線程拉取到增量信息并發(fā)修改
        if (fetchRegistryUpdateLock.tryLock()) {
            try {
                // 將獲取到的增量信息和本地緩存信息合并面哼。 
                updateDelta(delta);
                reconcileHashCode = getReconcileHashCode(applications);
            } finally {
                // 釋放鎖
                fetchRegistryUpdateLock.unlock();
            }
        } else {
            logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
        }
        // ( HashCode 不一致|| 打印增量和全量的差異 )= true 重新去全量獲取
        if (!reconcileHashCode.equals(delta.getAppsHashCode()) || clientConfig.shouldLogDeltaDiff()) {
            reconcileAndLogDifference(delta, reconcileHashCode);  // this makes a remoteCall
        }
    } else {
        logger.warn("Not updating application delta as another thread is updating it already");
        logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode());
    }
}

步驟說明:

1.發(fā)起http請求,將服務(wù)端的客戶端變化的信息拉取過來扫步,如: register魔策, cancle, modify 有過這些操作的數(shù)據(jù)

2.上鎖,防止某次調(diào)度網(wǎng)絡(luò)請求時間過長河胎,導(dǎo)致同一時間有多線程拉取到增量信息并發(fā)修改

3.將請求過來的增量數(shù)據(jù)和本地的數(shù)據(jù)做合并

4.計算hashCode

5.如果hashCode不一致闯袒,或者clientConfig.shouldLogDeltaDiff() = true 的話,則又會去服務(wù)端發(fā)起一次全量獲取

合并數(shù)據(jù)

private void updateDelta(Applications delta) {
    int deltaCount = 0;
    // 循環(huán)拉取過來的應(yīng)用列表
    for (Application app : delta.getRegisteredApplications()) {
        // 循環(huán)這個應(yīng)用里面的實例(有多個實例代表是集群的。)
        for (InstanceInfo instance : app.getInstances()) {
            // 獲取本地的注冊應(yīng)用列表
            Applications applications = getApplications();
            String instanceRegion = instanceRegionChecker.getInstanceRegion(instance);
            if (!instanceRegionChecker.isLocalRegion(instanceRegion)) {
                Applications remoteApps = remoteRegionVsApps.get(instanceRegion);
                if (null == remoteApps) {
                    remoteApps = new Applications();
                    remoteRegionVsApps.put(instanceRegion, remoteApps);
                }
                applications = remoteApps;
            }
            
            ++deltaCount;
            if (ActionType.ADDED.equals(instance.getActionType())) {// 添加事件
                //根據(jù)AppName 獲取本地的數(shù)據(jù)政敢,看這個應(yīng)用是否存在
                Application existingApp = applications.getRegisteredApplications(instance.getAppName());
                if (existingApp == null) {
                    // 不存在其徙,則加到本地的應(yīng)用里面去
                    applications.addApplication(app);
                }
                logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion);
                // 為本地這個應(yīng)用添加這個實例
                applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
            } else if (ActionType.MODIFIED.equals(instance.getActionType())) { // 修改事件
                //根據(jù)AppName 獲取本地的數(shù)據(jù),看這個應(yīng)用是否存在
                Application existingApp = applications.getRegisteredApplications(instance.getAppName());
                if (existingApp == null) {
                    // 不存在喷户,則加到本地的應(yīng)用里面去
                    applications.addApplication(app);
                }
                logger.debug("Modified instance {} to the existing apps ", instance.getId());
                // 為本地這個應(yīng)用添加這個實例
                applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);

            } else if (ActionType.DELETED.equals(instance.getActionType())) {  // 刪除事件
                Application existingApp = applications.getRegisteredApplications(instance.getAppName());
                if (existingApp == null) {
                    applications.addApplication(app);
                }
                logger.debug("Deleted instance {} to the existing apps ", instance.getId());
                // 移除這個實例
                applications.getRegisteredApplications(instance.getAppName()).removeInstance(instance);
            }
        }
    }
    logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount);

    getApplications().setVersion(delta.getVersion());
    getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());

    for (Applications applications : remoteRegionVsApps.values()) {
        applications.setVersion(delta.getVersion());
        applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
    }
}

步驟說明:

1.從服務(wù)端獲取了最近這段時間唾那,新注冊新來的客戶端信息,有過修改的褪尝,被刪除的闹获, 這三大類的實例信息

然后通過覆蓋本地的數(shù)據(jù),移除數(shù)據(jù)河哑,來達到數(shù)據(jù)合并的需求避诽。

Eureka-Server接收請求

控制器接收請求

com.netflix.eureka.resources.ApplicationsResource , 程序入口

全量獲取

@GET
public Response getContainers(@PathParam("version") String version,
                              @HeaderParam(HEADER_ACCEPT) String acceptHeader,
                              @HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
                              @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
                              @Context UriInfo uriInfo,
                              @Nullable @QueryParam("regions") String regionsStr) {
    
    // 獲取注冊列表的區(qū)域
    boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();
    String[] regions = null;
    if (!isRemoteRegionRequested) {
        EurekaMonitors.GET_ALL.increment();
    } else {
        regions = regionsStr.toLowerCase().split(",");
        Arrays.sort(regions); // So we don't have different caches for same regions queried in different order.
        EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();
    }

    // 判斷是否可以訪問
    if (!registry.shouldAllowAccess(isRemoteRegionRequested)) {
        return Response.status(Status.FORBIDDEN).build();
    }
    // 設(shè)置API版本
    CurrentRequestVersion.set(Version.toEnum(version));
    // 默認key的類型為JSON
    KeyType keyType = Key.KeyType.JSON;
    // 默認設(shè)置返回類型為JSON
    String returnMediaType = MediaType.APPLICATION_JSON;
    // 如果Accept為空,或者不包含JSON字符串(表示客戶端可能不接收JSON類型)璃谨,則設(shè)置返回XML類型的
    if (acceptHeader == null || !acceptHeader.contains(HEADER_JSON_VALUE)) {
        keyType = Key.KeyType.XML;
        returnMediaType = MediaType.APPLICATION_XML;
    }
    // 構(gòu)建緩存KEY 
    Key cacheKey = new Key(Key.EntityType.Application,
            ResponseCacheImpl.ALL_APPS,
            keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
    );
    // 獲取緩存信息沙庐,返回給客戶端
    Response response;
    // 判斷請求接收類型是否是gzip ,如果是,則返回gzip的流出去
    if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
        response = Response.ok(responseCache.getGZIP(cacheKey))
                .header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
                .header(HEADER_CONTENT_TYPE, returnMediaType)
                .build();
    } else {
        response = Response.ok(responseCache.get(cacheKey))
                .build();
    }
    return response;
}

PS: 從Controller中可以看到佳吞,獲取注冊信息都是調(diào)用緩存操作類來最終獲取到的轨功,此處暫時先不講緩存機制的實現(xiàn)

后面會單獨開一篇文章來寫Eureka的緩存機制

獲取全量信息,主要就是從AbstractInstanceRegistry類getApplications中的registry中獲取的,代碼如下

public Applications getApplications() {
    boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
    if (disableTransparentFallback) {
        return getApplicationsFromLocalRegionOnly();
    } else {
        return getApplicationsFromAllRemoteRegions();  // Behavior of falling back to remote region can be disabled.
    }
}

disableTransparentFallback : 官網(wǎng)解釋是 容达, 如果在遠程區(qū)域本地沒有實例運行古涧,對于應(yīng)用程序回退的舊行為是否被禁用, 默認為false花盐,所以此處僅

詳細講getApplicationsFromAllRemoteRegions() 羡滑;

public Applications getApplicationsFromAllRemoteRegions() {
    return getApplicationsFromMultipleRegions(allKnownRemoteRegions);
}
public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {

    boolean includeRemoteRegion = null != remoteRegions && remoteRegions.length != 0;

    logger.debug("Fetching applications registry with remote regions: {}, Regions argument {}",
            includeRemoteRegion, Arrays.toString(remoteRegions));
    // 默認為false
    if (includeRemoteRegion) {
        GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS.increment();
    } else {
        GET_ALL_CACHE_MISS.increment();
    }
    Applications apps = new Applications();
    apps.setVersion(1L);
    // 循環(huán)該類中的CurrentHashMap, 這個MAP中,存儲的是所有的客戶端注冊的實例信息
    // KEY 為客戶端的名稱算芯,value為客戶端的集群機器信息柒昏。
    for (Entry<String, Map<String, Lease<InstanceInfo>>> entry : registry.entrySet()) {
        Application app = null;
        // 
        if (entry.getValue() != null) {
            // 獲取Lease信息,里面有每個實例的instance信息熙揍,分裝成Application實體
            for (Entry<String, Lease<InstanceInfo>> stringLeaseEntry : entry.getValue().entrySet()) {
                Lease<InstanceInfo> lease = stringLeaseEntry.getValue();
                if (app == null) {
                    app = new Application(lease.getHolder().getAppName());
                }
                app.addInstance(decorateInstanceInfo(lease));
            }
        }
        if (app != null) {
            //放入 Applications里面去
            apps.addApplication(app);
        }
    }
   // 职祷。。届囚。有梆。省略N多代碼
    apps.setAppsHashCode(apps.getReconcileHashCode());
    return apps;
}

總結(jié): 從上面的代碼上來看,全量獲取的機制很簡單意系,主要是把服務(wù)端本地的CurrentHashMap里面存儲的客戶端信息泥耀,封裝成

Application實體,然后返回蛔添。

增量獲取

@Path("delta")
@GET
public Response getContainerDifferential(
        @PathParam("version") String version,
        @HeaderParam(HEADER_ACCEPT) String acceptHeader,
        @HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
        @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
        @Context UriInfo uriInfo, @Nullable @QueryParam("regions") String regionsStr) {
    // ..... 省略N多代碼
    Key cacheKey = new Key(Key.EntityType.Application,
        ResponseCacheImpl.ALL_APPS_DELTA,
        keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
    );
    // ..... 省略N多代碼
    if (acceptEncoding != null
        && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
     return Response.ok(responseCache.getGZIP(cacheKey))
            .header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
            .header(HEADER_CONTENT_TYPE, returnMediaType)
            .build();
    } else {
        return Response.ok(responseCache.get(cacheKey))
            .build();
    }
}

獲取增量信息的代碼在AbstractInstanceRegistry類中痰催,代碼如下

public Applications getApplicationDeltas() {
    GET_ALL_CACHE_MISS_DELTA.increment();
    // 最近變化過的應(yīng)用兜辞,初始化一個實體
    Applications apps = new Applications();
    // 增量獲取的版本號
    apps.setVersion(responseCache.getVersionDelta().get());
    Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
    try {
        // 上寫鎖
        write.lock();
        // 最近產(chǎn)生過變化的客戶端,都在這個隊列里面
        Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
        logger.debug("The number of elements in the delta queue is :"
                + this.recentlyChangedQueue.size());
        // 循環(huán)隊列
        while (iter.hasNext()) {
            // 獲取隊列中的lease信息夸溶,這里面封裝的就是客戶端的實例信息
            Lease<InstanceInfo> lease = iter.next().getLeaseInfo();
            InstanceInfo instanceInfo = lease.getHolder();
            Object[] args = {instanceInfo.getId(),
                    instanceInfo.getStatus().name(),
                    instanceInfo.getActionType().name()};
            logger.debug(
                    "The instance id %s is found with status %s and actiontype %s",
                    args);
            Application app = applicationInstancesMap.get(instanceInfo
                    .getAppName());
            if (app == null) {
                // 組裝成一個Application實體逸吵,同時放入Applications里面去
                app = new Application(instanceInfo.getAppName());
                applicationInstancesMap.put(instanceInfo.getAppName(), app);
                apps.addApplication(app);
            }
            app.addInstance(decorateInstanceInfo(lease));
        }

        boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
        // 暫時沒看明白這里的作用(苦笑。缝裁。)
        if (!disableTransparentFallback) {
            Applications allAppsInLocalRegion = getApplications(false);

            for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) {
                Applications applications = remoteRegistry.getApplicationDeltas();
                for (Application application : applications.getRegisteredApplications()) {
                    Application appInLocalRegistry =
                            allAppsInLocalRegion.getRegisteredApplications(application.getName());
                    if (appInLocalRegistry == null) {
                        apps.addApplication(application);
                    }
                }
            }
        }
        // 獲取全量的注冊信息
        Applications allApps = getApplications(!disableTransparentFallback);
        // 設(shè)置HashCode 
        apps.setAppsHashCode(allApps.getReconcileHashCode());
        return apps;
    } finally {
        write.unlock();
    }
}

上面主要用到了一個租約變化的隊列胁塞, 這里面在客戶端發(fā)生變化時,都會在這里面加入一條信息压语, 如: 注冊,下線编检,過期

等操作胎食,租約變化隊列里面的數(shù)據(jù)默認保存3分鐘,會有一個定時器沒30秒清理一次允懂。

retentionTimeInMSInDeltaQueue : 客戶端保持增量信息緩存的時間厕怜,從而保證不會丟失這些信息,單位為毫秒蕾总,默認為3 * 60 * 1000

private TimerTask getDeltaRetentionTask() {
    return new TimerTask() {

        @Override
        public void run() {
            Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator();
            while (it.hasNext()) {
                // 最后更新時間小于當(dāng)前時間-3分鐘粥航,那么就會被移除
                if (it.next().getLastUpdateTime() <
                        System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {
                    it.remove();
                } else {
                    break;
                }
            }
        }

    };
}

獲取到了這些變化的客戶端信息,返回Eureka Clien 之后生百,通過集合合并递雀,就可以得到最新的緩存數(shù)據(jù)了。

對于服務(wù)端來說蚀浆, 接收全量獲取和增量獲取的請求缀程,區(qū)別在于,構(gòu)成的KEY不同市俊, 全量獲取的KEY 為ALL_APPS杨凑,

增量獲取的KEY是ALL_APPS_DELTA , 然后都是通過緩存操作類去獲取數(shù)據(jù)摆昧,因此最重要的是緩存類的功能實現(xiàn)

接下來會單獨開一篇講Eureka Server 服務(wù)端的緩存機制撩满。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绅你,隨后出現(xiàn)的幾起案子伺帘,更是在濱河造成了極大的恐慌,老刑警劉巖忌锯,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曼追,死亡現(xiàn)場離奇詭異,居然都是意外死亡汉规,警方通過查閱死者的電腦和手機礼殊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門驹吮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晶伦,你說我怎么就攤上這事碟狞。” “怎么了婚陪?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵族沃,是天一觀的道長。 經(jīng)常有香客問我泌参,道長脆淹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任沽一,我火速辦了婚禮盖溺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铣缠。我一直安慰自己烘嘱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布蝗蛙。 她就那樣靜靜地躺著蝇庭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捡硅。 梳的紋絲不亂的頭發(fā)上哮内,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音壮韭,去河邊找鬼牍蜂。 笑死,一個胖子當(dāng)著我的面吹牛泰涂,可吹牛的內(nèi)容都是我干的鲫竞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼逼蒙,長吁一口氣:“原來是場噩夢啊……” “哼从绘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起是牢,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤僵井,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驳棱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體批什,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年社搅,在試婚紗的時候發(fā)現(xiàn)自己被綠了驻债。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乳规。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖合呐,靈堂內(nèi)的尸體忽然破棺而出暮的,到底是詐尸還是另有隱情,我是刑警寧澤淌实,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布冻辩,位于F島的核電站,受9級特大地震影響拆祈,放射性物質(zhì)發(fā)生泄漏恨闪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一放坏、第九天 我趴在偏房一處隱蔽的房頂上張望咙咽。 院中可真熱鬧,春花似錦轻姿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至余素,卻和暖如春豹休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桨吊。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工威根, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人视乐。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓洛搀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親佑淀。 傳聞我的和親對象是個殘疾皇子留美,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容