The foreword 0.

  • Springboot version: 2.1.9.RELEASE
  • Springcloud version: Greenwich.SR4

1. Process that the client pulls the full registry

The server receives the client’s request to pull the full registry in the getContainers() method of the ApplicationsResource class

// ApplicationsResource.class
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) {
    IsRemoteRegionRequested = true when regionsStr is not an empty string
    // Indicates that the registry to be pulled contains the remote region
    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();
    }

    // Check if the server allows the access to the registry. The server can
    // restrict access if it is not
    // ready to serve traffic depending on various reasons.
    1.1 Determine whether the server allows access to the registry
    if(! registry.shouldAllowAccess(isRemoteRegionRequested)) {Return 403 is not allowed and the request is denied
        return Response.status(Status.FORBIDDEN).build();
    }
    CurrentRequestVersion.set(Version.toEnum(version));
    KeyType keyType = Key.KeyType.JSON;
    String returnMediaType = MediaType.APPLICATION_JSON;
    if (acceptHeader == null| |! acceptHeader.contains(HEADER_JSON_VALUE)) { keyType = Key.KeyType.XML; returnMediaType = MediaType.APPLICATION_XML; }// Define the cache key
    / / Key. EntityType. Application: Application said pull the registry
    // ResponseCacheImpl.ALL_APPS: ALL_APPS indicates full pull
    // keyType: key type. The default value is JSON
    / / CurrentRequestVersion. The get () : the version number of the request
    // EurekaAccept.fromString(EurekaAccept) : Returns the data format, default is full
    // Regions: List of remote regions, not empty to pull the registry from these remote regions
    Key cacheKey = new Key(Key.EntityType.Application,
            ResponseCacheImpl.ALL_APPS,
            keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
    );

    Response response;
    if(acceptEncoding ! =null && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
        AcceptEncoding = gzip indicates that the data in the response body needs to be compressed and returned
        // 2 obtain the registry from the responseCache responseCache
        response = Response.ok(responseCache.getGZIP(cacheKey))
                .header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
                .header(HEADER_CONTENT_TYPE, returnMediaType)
                .build();
    } else {
        // The data in the response body is not compressed and is returned normally
        response = Response.ok(responseCache.get(cacheKey))
                .build();
    }
    return response;
}
Copy the code

1.1 registry. ShouldAllowAccess ()

// PeerAwareInstanceRegistryImpl.class
public boolean shouldAllowAccess(boolean remoteRegionRequired) {
    if (this.peerInstancesTransferEmptyOnStartup) {
        / / peerInstancesTransferEmptyOnStartup = true, said the current server is first launched in the cluster nodes
        // At this time, the registry is empty and access is not allowed
        // The server can be accessed after a certain period of time. By default, the current server starts 5 minutes later
        if(! (System.currentTimeMillis() >this.startupTime + serverConfig.getWaitTimeInMsWhenSyncEmpty())) {
            return false; }}if (remoteRegionRequired) {
        for (RemoteRegionRegistry remoteRegionRegistry : this.regionNameVSRemoteRegistry.values()) {
            // If the registry needs to be pulled from a remote region, the access is not allowed as long as one of the registered remote regions is not ready
            if(! remoteRegionRegistry.isReadyForServingData()) {return false; }}}return true;
}
Copy the code

2. responseCache.getGZIP()

// ResponseCacheImpl.class
public byte[] getGZIP(Key key) {
    // Get registry information from the cache
    / / shouldUseReadOnlyResponseCache: whether to allow use read-only cache, the default is true
    Value payload = getValue(key, shouldUseReadOnlyResponseCache);
    if (payload == null) {
        return null;
    }
    // Return compressed data
    return payload.getGzipped();
}

Value getValue(final Key key, boolean useReadOnlyCache) {
    Value payload = null;
    try {
        if (useReadOnlyCache) {
            // If the registry information is allowed to be retrieved from the read-only cache
            If the read-only cache is empty, the read/write cache is fetched and stored in the read-only cache
            final Value currentPayload = readOnlyCacheMap.get(key);
            if(currentPayload ! =null) {
                payload = currentPayload;
            } else{ payload = readWriteCacheMap.get(key); readOnlyCacheMap.put(key, payload); }}else {
            If the registry information is not allowed to be retrieved from the read-only cache, it is retrieved directly from the read/write cachepayload = readWriteCacheMap.get(key); }}catch (Throwable t) {
        logger.error("Cannot get value for key : {}", key, t);
    }
    return payload;
}
Copy the code

3. ResponseCache cache mechanism

Before the analysis, simply familiarize yourself with the pre-knowledge:

  • LoadingCache:

    • The local cache class provided by Guava ensures that only one thread will load cached data in multi-threaded scenarios

    • The ability to specify a listener to listen for cache removal and to implement a cache expiration method that is triggered when the cache expires

    • Can implement load method, when the cache can not get the specified cache entry triggered load method

    • There are three ways to clean or flush cached entries based on a specified time

      • ExpireAfterAccess: Cache entries are removed when they have not been read or written within a specified period of time
      • ExpireAfterWrite: Cache entries are removed if they are not refreshed within a specified period of time
      • RefreshAfterWrite: A cache entry written within the specified time is not removed. If the cache entry is written within the specified time, the loading method is used to refresh the cache entry
    • Can specify cache initialization size and maximum capacity, when the maximum capacity is exceeded using the LRU algorithm to remove cache entries

      • InitialCapacity: specifies the initialCapacity

      • MaximumSize: Specifies the maximum capacity

public class ResponseCacheImpl implements ResponseCache {
  
  	/ /...

    // 3.1 Read-only cache
    private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();

    // 3.2 Read/write cache
    private final LoadingCache<Key, Value> readWriteCacheMap;
  
    / /...

    ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
        / /...
      
        // Build read/write cache, specify the initial size of cache (default 1000), expiration time (default 180s), implement cache expiration method, load method
        this.readWriteCacheMap =
                CacheBuilder.newBuilder().initialCapacity(serverConfig.getInitialCapacityOfResponseCache())
                        .expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
          		// Specifies the listener to listen on when the cache is removed
                        .removalListener(new RemovalListener<Key, Value>() {
                            @Override
                            // When listening for cache removal, the expiration method is executed
                            public void onRemoval(RemovalNotification<Key, Value> notification) {
                                 Key removedKey = notification.getKey();
                                if (removedKey.hasRegions()) {
                                    // removedKey refers to the cache key above
                                    // Remote Regions in the cache key is not null
                                    // Remove the regionSpecificKeys value.
                                    Key cloneWithNoRegions = removedKey.cloneWithoutRegions();
                                    regionSpecificKeys.remove(cloneWithNoRegions, removedKey);
                                }
                            }
                        })
                        .build(new CacheLoader<Key, Value>() {
                            @Override
                            // If the specified cache cannot be retrieved from the read/write cache, the load method is triggered
                            public Value load(Key key) throws Exception {
                                if (key.hasRegions()) {
                                    // If remote Regions in the cache key is not empty
                                    RegionSpecificKeys = regionSpecificKeys; regionSpecificKeys = regionSpecificKeys; regionSpecificKeys = regionSpecificKeys
                                    Key cloneWithNoRegions = key.cloneWithoutRegions();
                                    regionSpecificKeys.put(cloneWithNoRegions, key);
                                }
                                // 4 Load the value of the specified cache key.
                                Value value = generatePayload(key);
                                returnvalue; }});if (shouldUseReadOnlyResponseCache) {
            // If read-only caches are allowed, a scheduled task is enabled to handle data synchronization between read-only caches and read-write caches
            // The scheduled task is executed repeatedly at a fixed time. By default, the task starts 30 seconds later and is executed every 30 seconds
            // 3.3 getCacheUpdateTask() : indicates a scheduled cache synchronization task
            timer.schedule(getCacheUpdateTask(),
                    new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
                            + responseCacheUpdateIntervalMs),
                    responseCacheUpdateIntervalMs);
        }

        try {
            Monitors.registerObject(this);
        } catch (Throwable e) {
            logger.warn("Cannot register the JMX monitor for the InstanceRegistry", e); }}/ /...
  
}
Copy the code

3.1 readOnlyCacheMap and readWriteCacheMap

  • ReadOnlyCacheMap: read-only cache on the server Add or update cache entries using the PUT method when:

    • If readOnlyCacheMap is allowed to be used to obtain the registry information in the response cache but readOnlyCacheMap cannot be used to obtain the cache entry, obtain the readWriteCacheMap and add it to readOnlyCacheMap
    • If cache entries are inconsistent between readOnlyCacheMap and readWriteCacheMap during scheduled cache synchronization, update readWriteCacheMap to readOnlyCacheMap
  • ReadWriteCacheMap: To add or update cached entries to the server using the get method:

    • During the scheduled cache synchronization task, if the readWriteCacheMap fails to obtain the corresponding cache entry, the load method is triggered and the generatePayload() method is called to obtain and add the cache entry
    • When obtaining the registry information in the response cache, if the corresponding cache entry cannot be obtained from readWriteCacheMap, the load method is triggered, and the generatePayload() method is called to obtain and add
  • ReadWriteCacheMap: Indicates that the read/write cache is stored on the server. The invalidate() method is used to clear the cache entries

3.3 Cache Synchronization Scheduled Tasks

// ResponseCacheImpl.class
private TimerTask getCacheUpdateTask(a) {
    return new TimerTask() {
        @Override
        public void run(a) {
            logger.debug("Updating the client cache from response cache");
            // Iterate over the cache key of the read-only cache
            for (Key key : readOnlyCacheMap.keySet()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Updating the client cache from response cache for key : {} {} {} {}",
                            key.getEntityType(), key.getName(), key.getVersion(), key.getType());
                }
                try {
                    // Set the version number
                    CurrentRequestVersion.set(key.getVersion());
                    // Retrieve the value from the read/write cache based on the cache key.
                    // If inconsistent with the value in the read-only cache, the read/write cache is assigned to the read-only cache
                    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 for key {}", key.toStringCompact(), th); }}}}; }Copy the code

4. Obtain the registry

// ResponseCacheImpl.class
private Value generatePayload(Key key) {
    Stopwatch tracer = null;
    try {
        String payload;
        switch (key.getEntityType()) {
            // Focus on this type
            case Application:
                boolean isRemoteRegionRequested = key.hasRegions();

                // ALL_APPS: full pull
                if (ALL_APPS.equals(key.getName())) {
                    if (isRemoteRegionRequested) {
                        tracer = serializeAllAppsWithRemoteRegionTimer.start();
                        // To obtain the registry containing the remote regions specified by the local server and the client request
                        / / 4.1 call getApplicationsFromMultipleRegions () method
                        // getPayLoad() : Converts the payload before returning the result. The default is JSON
                        payload = getPayLoad(key, registry.getApplicationsFromMultipleRegions(key.getRegions()));
                    } else {
                        tracer = serializeAllAppsTimer.start();
                        Call getApplications() to get the registry
                        payload = getPayLoad(key, registry.getApplications());
                    }
                // 5 ALL_APPS_DELTA: incremental pull
                } else if (ALL_APPS_DELTA.equals(key.getName())) {
                    if (isRemoteRegionRequested) {
                        tracer = serializeDeltaAppsWithRemoteRegionTimer.start();
                        versionDeltaWithRegions.incrementAndGet();
                        versionDeltaWithRegionsLegacy.incrementAndGet();
                        / / 5.1 call getApplicationDeltasFromMultipleRegions () method
                        payload = getPayLoad(key,
                                registry.getApplicationDeltasFromMultipleRegions(key.getRegions()));
                    } else {
                        tracer = serializeDeltaAppsTimer.start();
                        versionDelta.incrementAndGet();
                        versionDeltaLegacy.incrementAndGet();
                        // 5.2 To obtain the registry, call getApplicationDeltas()payload = getPayLoad(key, registry.getApplicationDeltas()); }}else {
                    tracer = serializeOneApptimer.start();
                    payload = getPayLoad(key, registry.getApplication(key.getName()));
                }
                break;
            case VIP:
            case SVIP:
                tracer = serializeViptimer.start();
                payload = getPayLoad(key, getApplicationsForVip(key, registry));
                break;
            default:
                logger.error("Unidentified entity type: {} found in the cache key.", key.getEntityType());
                payload = "";
                break;
        }
        return new Value(payload);
    } finally {
        if(tracer ! =null) { tracer.stop(); }}}Copy the code

4.1 registry. GetApplicationsFromMultipleRegions ()

// AbstractInstanceRegistry.class
public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {

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

    logger.debug("Fetching applications registry with remote regions: {}, Regions argument {}",
            includeRemoteRegion, remoteRegions);

    if (includeRemoteRegion) {
        GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS.increment();
    } else {
        GET_ALL_CACHE_MISS.increment();
    }
    // Create a registry object
    // This object is both the registry returned to the client request and a value in the readWriteCacheMap read-only cache
    Applications apps = new Applications();
    apps.setVersion(1L);
    // Iterate through the server local registry to add the service instance information to apps
    for (Entry<String, Map<String, Lease<InstanceInfo>>> entry : registry.entrySet()) {
        Application app = null;

        if(entry.getValue() ! =null) {
            for (Entry<String, Lease<InstanceInfo>> stringLeaseEntry : entry.getValue().entrySet()) {
                Lease<InstanceInfo> lease = stringLeaseEntry.getValue();
                if (app == null) {
                    // Create a service information object app by retrieving the service name from the lease information of the corresponding instance
                    // Triggered when the current for loop is first entered
                    app = new Application(lease.getHolder().getAppName());
                }
                // The lease information of the corresponding instance is converted into instance information and added to appapp.addInstance(decorateInstanceInfo(lease)); }}if(app ! =null) {
            // Add service instance information to appsapps.addApplication(app); }}if (includeRemoteRegion) {
        // Iterate over the remote regions that need to be fetched
        for (String remoteRegion : remoteRegions) {
            // Obtain information about the remote region that has been registered locally
            RemoteRegionRegistry remoteRegistry = regionNameVSRemoteRegistry.get(remoteRegion);
            if (null! = remoteRegistry) {// Get registry information
                Applications remoteApps = remoteRegistry.getApplications();
                for (Application application : remoteApps.getRegisteredApplications()) {
                    // Determine whether to allow the service to be fetched from the registry
                    // Whitelist mechanism
                    if (shouldFetchFromRemoteRegistry(application.getName(), remoteRegion)) {
                        logger.info("Application {} fetched from the remote region {}",
                                application.getName(), remoteRegion);

                        // Get service information from apps based on the instance name
                        Application appInstanceTillNow = apps.getRegisteredApplications(application.getName());
                        if (appInstanceTillNow == null) {
                            // If the app does not have the corresponding service information after the local registry is processed, but the remote regions registered locally have the corresponding service information in the registry
                            // Create a new service and add it to the apps
                            appInstanceTillNow = new Application(application.getName());
                            apps.addApplication(appInstanceTillNow);
                        }
                        for (InstanceInfo instanceInfo : application.getInstances()) {
                            // Add instance information to service informationappInstanceTillNow.addInstance(instanceInfo); }}else {
                        logger.debug("Application {} not fetched from the remote region {} as there exists a "
                                        + "whitelist and this app is not in the whitelist.", application.getName(), remoteRegion); }}}else {
                logger.warn("No remote registry available for the remote region {}", remoteRegion);
            }
        }
    }
    
    apps.setAppsHashCode(apps.getReconcileHashCode());
    return apps;
}
Copy the code

4.2 registry. GetApplications ()

// AbstractInstanceRegistry.class
public Applications getApplications(a) {
    boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
    if (disableTransparentFallback) {
        return getApplicationsFromLocalRegionOnly();
    } else {
        return getApplicationsFromAllRemoteRegions();  // Behavior of falling back to remote region can be disabled.}}public Applications getApplicationsFromLocalRegionOnly(a) {
    / / getApplicationsFromMultipleRegions () method has been analyzed above
    The EMPTY_STR_ARRAY parameter is used to fetch only the local registry
    return getApplicationsFromMultipleRegions(EMPTY_STR_ARRAY);
}

public Applications getApplicationsFromAllRemoteRegions(a) {
    / / getApplicationsFromMultipleRegions () method has been analyzed above
    // The allKnownRemoteRegions parameter is used to obtain the local registry of the server and all registries registered with the local remote region
    return getApplicationsFromMultipleRegions(allKnownRemoteRegions);
}
Copy the code

5. Process the client to pull the delta registry

The server receives the client’s request to pull the delta registry in the getContainers() method of the ApplicationsResource class

// ApplicationsResource.class
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) {
    // Only the following two points are different from the full code logic, other basic same
    
    / /...
    
    if((serverConfig.shouldDisableDelta()) || (! registry.shouldAllowAccess(isRemoteRegionRequested))) {// If the local server configuration file is configured to prohibit fetching incremental registries or denying access to registries
        // return 403, reject the request
        return Response.status(Status.FORBIDDEN).build();
    }
    
	/ /...
    
    // Define the cache key
    // ResponseCacheImpl.ALL_APPS_DELTA: indicates an incremental pull
    Key cacheKey = new Key(Key.EntityType.Application,
            ResponseCacheImpl.ALL_APPS_DELTA,
            keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
    );
    
	/ /...
    
}
Copy the code

5.1 getApplicationDeltasFromMultipleRegions ()

// AbstractInstanceRegistry.class
public Applications getApplicationDeltasFromMultipleRegions(String[] remoteRegions) {
    if (null == remoteRegions) {
        // null: all remote Registries registered with the local region need to be pulled
        remoteRegions = allKnownRemoteRegions; // null means all remote regions.
    }

    booleanincludeRemoteRegion = remoteRegions.length ! =0;

    if (includeRemoteRegion) {
        GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS_DELTA.increment();
    } else {
        GET_ALL_CACHE_MISS_DELTA.increment();
    }

    // Create a registry object
    Applications apps = new Applications();
    apps.setVersion(responseCache.getVersionDeltaWithRegions().get());
    // Create a service instance map
    Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
    try {
        // Enable write lock
        write.lock();
        Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
        logger.debug("The number of elements in the delta queue is :{}".this.recentlyChangedQueue.size());
        // Iterate through the recent change queue
        while (iter.hasNext()) {
            // Get instance lease information
            Lease<InstanceInfo> lease = iter.next().getLeaseInfo();
            // Get instance information
            InstanceInfo instanceInfo = lease.getHolder();
            logger.debug("The instance id {} is found with status {} and actiontype {}",
                    instanceInfo.getId(), instanceInfo.getStatus().name(), instanceInfo.getActionType().name());
            Application app = applicationInstancesMap.get(instanceInfo.getAppName());
            if (app == null) {
                If no service information is found in applicationInstancesMap, create a new one and add it
                // Triggered when service information is created for the first time
                app = new Application(instanceInfo.getAppName());
                applicationInstancesMap.put(instanceInfo.getAppName(), app);
                // Add service information to apps
                apps.addApplication(app);
            }
            // The lease information of the corresponding instance is converted into instance information and added to app
            app.addInstance(new InstanceInfo(decorateInstanceInfo(lease)));
        }

        if (includeRemoteRegion) {
            // Iterate over the remote regions that need to be fetched
            for (String remoteRegion : remoteRegions) {
                // Obtain information about the remote region that has been registered locally
                RemoteRegionRegistry remoteRegistry = regionNameVSRemoteRegistry.get(remoteRegion);
                if (null! = remoteRegistry) {// Get registry delta information
                    Applications remoteAppsDelta = remoteRegistry.getApplicationDeltas();
                    if (null! = remoteAppsDelta) {for (Application application : remoteAppsDelta.getRegisteredApplications()) {
                            // Determine whether to allow the service to be fetched from the registry
                    	      // Whitelist mechanism
                            if (shouldFetchFromRemoteRegistry(application.getName(), remoteRegion)) {
                                // Get service information from apps based on the instance name
                                Application appInstanceTillNow =
                                        apps.getRegisteredApplications(application.getName());
                                if (appInstanceTillNow == null) {
                                    // If the app does not have the corresponding service information after the local registry is processed, but the remote regions registered locally have the corresponding service information in the registry
                            	    // Create a new service and add it to the apps
                                    appInstanceTillNow = new Application(application.getName());
                                    apps.addApplication(appInstanceTillNow);
                                }
                                for (InstanceInfo instanceInfo : application.getInstances()) {
                                    // Add instance information to service information
                                    appInstanceTillNow.addInstance(new InstanceInfo(instanceInfo));
                                }
                            }
                        }
                    }
                }
            }
        }

        // Calculates the hashCode value of the local full registry (containing remote regions registered locally) and returns it to the client
        // The client also computes the hashCode value of its own registry upon receiving the registry update returned by the server
        // If the two hashcodes are inconsistent, it indicates that the registries at the two ends are inconsistent. The client then initiates a full registry pull request
        Applications allApps = getApplicationsFromMultipleRegions(remoteRegions);
        apps.setAppsHashCode(allApps.getReconcileHashCode());
        return apps;
    } finally {
        // Close the write lockwrite.unlock(); }}Copy the code

5.2 registry. GetApplicationDeltas ()

// AbstractInstanceRegistry.class
public Applications getApplicationDeltas(a) {
    GET_ALL_CACHE_MISS_DELTA.increment();
    // Create a registry object
    Applications apps = new Applications();
    apps.setVersion(responseCache.getVersionDelta().get());
    
    Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
    try {
        // Enable write lock
        write.lock();
        Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
        logger.debug("The number of elements in the delta queue is : {}".this.recentlyChangedQueue.size());
        // Iterate through the queue of recent changes
        while (iter.hasNext()) {
            // Get instance lease information
            Lease<InstanceInfo> lease = iter.next().getLeaseInfo();
            // Get instance information
            InstanceInfo instanceInfo = lease.getHolder();
            logger.debug(
                    "The instance id {} is found with status {} and actiontype {}",
                    instanceInfo.getId(), instanceInfo.getStatus().name(), instanceInfo.getActionType().name());
            Application app = applicationInstancesMap.get(instanceInfo
                    .getAppName());
            if (app == null) {
                If no service information is found in applicationInstancesMap, create a new one and add it
                // Triggered when service information is created for the first time
                app = new Application(instanceInfo.getAppName());
                applicationInstancesMap.put(instanceInfo.getAppName(), app);
                // Add service information to apps
                apps.addApplication(app);
            }
            // The lease information of the corresponding instance is converted into instance information and added to app
            app.addInstance(new InstanceInfo(decorateInstanceInfo(lease)));
        }

        boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();

        if(! disableTransparentFallback) {// Obtain all local registry information, excluding remote regions
            Applications allAppsInLocalRegion = getApplications(false);

            // Iterate over information about all remote regions registered locally
            for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) {
                // Get registry delta information
                Applications applications = remoteRegistry.getApplicationDeltas();
                // Iterate over the registry delta information
                for (Application application : applications.getRegisteredApplications()) {
                    Application appInLocalRegistry =
                            allAppsInLocalRegion.getRegisteredApplications(application.getName());
                    if (appInLocalRegistry == null) {
                        // If a service instance registered with the local remote Region delta registry exists in the local registry information
                        // Add the service instance information to appsapps.addApplication(application); }}}}// Calculates the hashCode value of the local full registry (containing remote regions registered locally) and returns it to the client
        // The client also computes the hashCode value of its own registry upon receiving the registry update returned by the server
        // If the two hashcodes are inconsistent, it indicates that the registries at the two ends are inconsistent. The client then initiates a full registry pull requestApplications allApps = getApplications(! disableTransparentFallback); apps.setAppsHashCode(allApps.getReconcileHashCode());return apps;
    } finally {
        // Close the write lockwrite.unlock(); }}Copy the code

6. Summary

6.1 Main Process

  • First, when the server receives a client request to pull the registry, it determines whether to allow access to the registry. If not, it returns 403 to reject the request. If so, it continues processing
  • Then, the cache key is defined, and the registry is retrieved from the response cache (read-only cache, read/write cache) based on the cache key, compressed and returned to the client

6.2 Obtaining the Registry

  • Full amount:

    • From the local registry or, if necessary, from the remote Region registry registered to the local registry and merged back and forth
  • Increment:

    • From the local recent change queue and, if necessary, from the remote Region delta registry registered to the local area, merged back and forth