catalogue
Create a registry of application instance information
Initialize EurekaServerContext
EurekaBootStrap
Eureka server # startup entry: this class implements ServletContextListener. When the Servlet container (such as Tomcat and Jetty) starts, it calls #contextInitialized() method.
@Override public void contextInitialized(ServletContextEvent event) { try { // Initialize Eureka server configuration environment initEurekaEnvironment(); // Initialize Eureka server context initEurekaServerContext(); ServletContext sc = event.getServletContext(); sc.setAttribute(EurekaServerContext.class.getName(), serverContext); } catch (Throwable e) { logger.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } }
Let's take a look at the specific operations of these two steps in turn
initEurekaEnvironment
protected void initEurekaEnvironment() throws Exception { logger.info("Setting the eureka configuration.."); // Get data center String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER); if (dataCenter == null) { logger.info("Eureka data center value eureka.datacenter is not set, defaulting to default"); ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT); } else { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter); } // Obtain environmental information String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT); if (environment == null) { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST); logger.info("Eureka environment value eureka.environment is not set, defaulting to test"); } }
It mainly initializes the environment information. There is not much content. Let's take a look at the steps of initializing the context
initEurekaServerContext
protected void initEurekaServerContext() throws Exception { EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig(); // For backward compatibility JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); // Create a service decoder according to the server configuration logger.info("Initializing the eureka client..."); logger.info(eurekaServerConfig.getJsonCodecName()); ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig); ApplicationInfoManager applicationInfoManager = null; // Eureka client is embedded in Eureka server to communicate with other nodes in Eureka server cluster if (eurekaClient == null) { EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext()) ? new CloudInstanceConfig() : new MyDataCenterInstanceConfig(); applicationInfoManager = new ApplicationInfoManager( instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get()); EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig(); /** * eureka server Eureka client is also a discovery client */ eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig); } else { applicationInfoManager = eurekaClient.getApplicationInfoManager(); } // Create a registry of application instance information PeerAwareInstanceRegistry registry; if (isAws(applicationInfoManager.getInfo())) { registry = new AwsInstanceRegistry( eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient ); awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager); awsBinder.start(); } else { registry = new PeerAwareInstanceRegistryImpl( eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient ); } // Create Eureka server cluster node collection PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes( registry, eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, applicationInfoManager ); // Create Eureka server context (provide initialization, shutdown, acquisition and other methods) serverContext = new DefaultEurekaServerContext( eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, applicationInfoManager ); // Initialize EurekaServerContextHolder and use it to easily obtain the context of the server EurekaServerContextHolder.initialize(serverContext); // During initialization, the client instance information registered by other server s is obtained through the http request initiated by the creation thread when creating the remoteRegionRegistry (which should also be related to other mechanisms) serverContext.initialize(); logger.info("Initialized server context"); // Copy registry from neighboring eureka node pull registration information from other Eureka servers int registryCount = registry.syncUp(); registry.openForTraffic(applicationInfoManager, registryCount); // Register all monitoring statistics. Registration monitoring EurekaMonitors.registerAllStats(); }
The whole process and details of initializing EurekaServer are many, which are analyzed little by little:
ApplicationInfoManager
Note 1 ⃣ ️:
First, create application management classes according to EurekaInstanceConfig and InstanceInfo. These two classes constitute ApplicationInfoManager as attributes. Therefore, mainly look at the creation code of InstanceInfo through eurekainconfigbasedinstanceinfoprovider (instanceconfig) Get() to get instance information:
Firstly, the builder of lease class is created according to the heartbeat interval (30 seconds by default) and renewal expiration time (90 seconds by default) of the configuration file. Lease class is the basis for service renewal and server expiration application. Subsequently, the instance attribute is assigned through the setting data of the configuration file. If the instance ID is configured, the configuration is read. Otherwise, the host name is used as the instance ID.
@Override public synchronized InstanceInfo get() { if (instanceInfo == null) { // Build the lease information to be passed to the server based on config build the lease information to be passed to the server according to eureka's Client configuration LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder() .setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds()) .setDurationInSecs(config.getLeaseExpirationDurationInSeconds()); if (vipAddressResolver == null) { vipAddressResolver = new Archaius1VipAddressResolver(); } // Builder the instance information to be registered with eureka server InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(vipAddressResolver); // set the appropriate id for the InstanceInfo, falling back to datacenter Id if applicable, else hostname set the instanceId, if configured, read the configuration, otherwise take the current hostname as the instance ID String instanceId = config.getInstanceId(); if (instanceId == null || instanceId.isEmpty()) { DataCenterInfo dataCenterInfo = config.getDataCenterInfo(); if (dataCenterInfo instanceof UniqueIdentifier) { instanceId = ((UniqueIdentifier) dataCenterInfo).getId(); } else { instanceId = config.getHostName(false); } } // Set the client's default address, hostname or ID address String defaultAddress; if (config instanceof RefreshableInstanceConfig) { // Refresh AWS data center info, and return up to date address defaultAddress = ((RefreshableInstanceConfig) config).resolveDefaultAddress(false); } else { defaultAddress = config.getHostName(false); } // fail safe if (defaultAddress == null || defaultAddress.isEmpty()) { defaultAddress = config.getIpAddress(); } builder.setNamespace(config.getNamespace()) .setInstanceId(instanceId) .setAppName(config.getAppname()) .setAppGroupName(config.getAppGroupName()) .setDataCenterInfo(config.getDataCenterInfo()) .setIPAddr(config.getIpAddress()) .setHostName(defaultAddress) .setPort(config.getNonSecurePort()) .enablePort(PortType.UNSECURE, config.isNonSecurePortEnabled()) .setSecurePort(config.getSecurePort()) .enablePort(PortType.SECURE, config.getSecurePortEnabled()) .setVIPAddress(config.getVirtualHostName()) .setSecureVIPAddress(config.getSecureVirtualHostName()) .setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl()) .setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl()) .setASGName(config.getASGName()) .setHealthCheckUrls(config.getHealthCheckUrlPath(), config.getHealthCheckUrl(), config.getSecureHealthCheckUrl()); // Start off with the STARTING state to avoid traffic if (!config.isInstanceEnabledOnit()) { InstanceStatus initialStatus = InstanceStatus.STARTING; LOG.info("Setting initial instance status as: {}", initialStatus); builder.setStatus(initialStatus); } else { LOG.info("Setting initial instance status as: {}. This may be too early for the instance to advertise " + "itself as available. You would instead want to control this via a healthcheck handler.", InstanceStatus.UP); } // Add any user specific metadata information for (Map.Entry<String, String> mapEntry : config.getMetadataMap().entrySet()) { String key = mapEntry.getKey(); String value = mapEntry.getValue(); // only add the metadata if the value is present if (value != null && !value.isEmpty()) { builder.add(key, value); } } instanceInfo = builder.build(); instanceInfo.setLeaseInfo(leaseInfoBuilder.build()); } return instanceInfo; }
Create EurekaClient
Then use eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig); Using the configuration of management class and eurekaclient to create eurekaclient, you can see that, like eurekaclient, the current server instance is registered and renewed in the registration center by creating the DiscoveryClient class. It can also be seen from here that there is no primary or secondary server instance, and each server also registers itself in the registration center as a client.
Create a registry of application instance information
Subsequently, create the registry through PeerAwareInstanceRegistryImpl
public PeerAwareInstanceRegistryImpl( EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, ServerCodecs serverCodecs, EurekaClient eurekaClient ) { super(serverConfig, clientConfig, serverCodecs); this.eurekaClient = eurekaClient; this.numberOfReplicationsLastMin = new MeasuredRate(1000 * 60 * 1); // We first check if the instance is STARTING or DOWN, then we check explicit overrides, // then we check the status of a potentially existing lease. this.instanceStatusOverrideRule = new FirstMatchWinsCompositeRule(new DownOrStartingRule(), new OverrideExistsRule(overriddenInstanceStatusMap), new LeaseExistsRule()); }
Initialize the attribute by calling the implementation of the parent class, and then create an instance state override rule. During initialization, three override rules are given. When none is satisfied, the default override rule execution result is returned.
Role of coverage status:
Call Eureka Server HTTP restful interface app / ${app_name} / ${install_id} / status to overwrite the change of application instance status, so as to actively and forcibly change the application instance status. Note that the status of the Eureka client application instance is not really modified, but the status of the application instance registered with Eureka server.
In this way, when Eureka client obtains the registration information, it configures Eureka Shouldfilteronlyuppinstances = true to filter out non {instancestatus Up the application instance of the application instance, so as to avoid transferring the instance, so as to suspend the service of the application instance (InstanceStatus.OUT_OF_SERVICE) without closing the application instance.
Therefore, in most cases, the purpose of calling this interface is to switch the application instance status between (InstanceStatus.UP) and (InstanceStatus.OUT_OF_SERVICE).
1, Override status rule:
Downorstarting rule: when the current instance state is not UP or out_ OF_ During service, execute the current rule and directly return the status of the current instance as the coverage status,
public class DownOrStartingRule implements InstanceStatusOverrideRule { @Override public StatusOverrideResult apply(InstanceInfo instanceInfo, Lease<InstanceInfo> existingLease, boolean isReplication) { /** * If the instance status is not running or suspended (starting or offline), it is not suitable for providing services and does not match */ if ((!InstanceInfo.InstanceStatus.UP.equals(instanceInfo.getStatus())) && (!InstanceInfo.InstanceStatus.OUT_OF_SERVICE.equals(instanceInfo.getStatus()))) { logger.debug("Trusting the instance status {} from replica or instance for instance {}", instanceInfo.getStatus(), instanceInfo.getId()); return StatusOverrideResult.matchingStatus(instanceInfo.getStatus()); } return StatusOverrideResult.NO_MATCH; }
2, Overrideexistsrule: if the current instance state overrides the data, the existing override state is used as the current override state
3, Leaseexistsrule: when a non Server request is made, the nstancestatus of the application instance that matches the existing lease OUT_ OF_ Service or instanceinfo InstanceStatus. Up state
4, Default rule (AlwaysMatchInstanceStatusRule): always return the status of the current instance as the override status.
super(serverConfig, clientConfig, serverCodecs)
Next, let's look at what has been done in super. First, assign the attribute, create the recently cancelled and registered queue, and set the thread of getDeltaRetentionTask() to execute regularly at the configured time interval (30 seconds by default). The run() method of getDeltaRetentionTask is to traverse the recently changed queue information. If the instance update time in the queue exceeds a certain period of time (three minutes by default), it will be removed from the recently changed queue. When the client side initiates incremental acquisition of registration information, the recentlyChangedQueue is used to calculate the increment of the latest time and return it to the client side.
protected AbstractInstanceRegistry(EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, ServerCodecs serverCodecs) { this.serverConfig = serverConfig; this.clientConfig = clientConfig; this.serverCodecs = serverCodecs; this.recentCanceledQueue = new CircularQueue<Pair<Long, String>>(1000); this.recentRegisteredQueue = new CircularQueue<Pair<Long, String>>(1000); this.renewsLastMin = new MeasuredRate(1000 * 60 * 1); /** * 30 Perform cleanup once per second */ this.deltaRetentionTimer.schedule(getDeltaRetentionTask(), serverConfig.getDeltaRetentionTimerIntervalInMs(), serverConfig.getDeltaRetentionTimerIntervalInMs()); } private TimerTask getDeltaRetentionTask() { return new TimerTask() { @Override public void run() { Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator(); while (it.hasNext()) { // Remove the data whose update time exceeds the current 3 minutes from the queue if (it.next().getLastUpdateTime() < System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) { it.remove(); } else { break; } } } }; }
The following steps are to create peereeurekanodes (Eureka server cluster node set) and Eureka server context (Eureka server context), put the context into the holder for easy access, and then go to the node that initializes the context. Focus on this
Initialize EurekaServerContext
The initialization code mainly does two things: start the newly created server cluster node set and initialize the application instance information registry.
public void initialize() { logger.info("Initializing ..."); // Start Eureka server cluster node collection (cluster replication) peerEurekaNodes.start(); try { // Initializes the registry of application instance information registry.init(peerEurekaNodes); } catch (Exception e) { throw new RuntimeException(e); } logger.info("Initialized"); }
Start the Server cluster node collection
start(): first, create a scheduled task, then update the cluster node information, and create a class that implements the run method. The code of run() mainly updates the cluster node information, and then hand over the class to the scheduled task to execute every certain time. This ensures that the cluster information of the server is obtained during initialization, and a request is initiated every interval to update the purpose of the local registry server instance.
public void start() { // Create scheduled task taskExecutor = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "Eureka-PeerNodesUpdater"); thread.setDaemon(true); return thread; } } ); try { // Initialize cluster node information updatePeerEurekaNodes(resolvePeerUrls()); Runnable peersUpdateTask = new Runnable() { @Override public void run() { try { // Update cluster node information updatePeerEurekaNodes(resolvePeerUrls()); } catch (Throwable e) { logger.error("Cannot update the replica Nodes", e); } } }; /** * Scheduled task setting execution interval (default 10 minutes) */ taskExecutor.scheduleWithFixedDelay( peersUpdateTask, serverConfig.getPeerEurekaNodesUpdateIntervalMs(), serverConfig.getPeerEurekaNodesUpdateIntervalMs(), TimeUnit.MILLISECONDS ); } catch (Exception e) { throw new IllegalStateException(e); } for (PeerEurekaNode node : peerEurekaNodes) { logger.info("Replica node URL: {}", node.getServiceUrl()); } }
Here's how the server initializes and updates cluster node information
resolvePeerUrls(): get the current instance information and visible area, and then resolve all server service URLS according to DNS or configuration information through getDiscoveryServiceUrls(). All other server services except the current server instance information.
protected List<String> resolvePeerUrls() { /** * Get current instance information and visible area */ InstanceInfo myInfo = applicationInfoManager.getInfo(); String zone = InstanceInfo.getZone(clientConfig.getAvailabilityZones(clientConfig.getRegion()), myInfo); // Gets a list of all eureka service URLs that the eureka client talks to List<String> replicaUrls = EndpointUtils .getDiscoveryServiceUrls(clientConfig, zone, new EndpointUtils.InstanceInfoBasedUrlRandomizer(myInfo)); /** * Remove the URL itself, and the rest needs to be synchronized */ int idx = 0; while (idx < replicaUrls.size()) { if (isThisMyUrl(replicaUrls.get(idx))) { replicaUrls.remove(idx); } else { idx++; } } return replicaUrls; } public static List<String> getDiscoveryServiceUrls(EurekaClientConfig clientConfig, String zone, ServiceUrlRandomizer randomizer) { /** * Resolve all server services urls according to DNS or configuration information */ boolean shouldUseDns = clientConfig.shouldUseDnsForFetchingServiceUrls(); if (shouldUseDns) { return getServiceUrlsFromDNS(clientConfig, zone, clientConfig.shouldPreferSameZoneEureka(), randomizer); } return getServiceUrlsFromConfig(clientConfig, zone, clientConfig.shouldPreferSameZoneEureka()); }
Update cluster node collection
The latest server node set has been obtained above. All new servers are removed from the previous server node, and the remaining servers are the servers to be offline. The latest server removes all previous server node sets, which is the new server node set this time.
If no new or offline server node is added this time, no operation will be performed. If the offline node set exists, shut down the offline eurekaNode and close the node's thread pool (cluster synchronization, explained below); If the new node set exists, create a node and add it to the temporary variable newNodeList, and then assign a value to the current server cluster node set. During the first initialization, the parsed servers are all new nodes to complete the property filling of the peereeurekanodes collection.
protected void updatePeerEurekaNodes(List<String> newPeerUrls) { if (newPeerUrls.isEmpty()) { logger.warn("The replica size seems to be empty. Check the route 53 DNS Registry"); return; } /** * Calculate the list of services to be offline and added */ Set<String> toShutdown = new HashSet<>(peerEurekaNodeUrls); toShutdown.removeAll(newPeerUrls); Set<String> toAdd = new HashSet<>(newPeerUrls); toAdd.removeAll(peerEurekaNodeUrls); /** * If you don't need to go offline or add, you can return directly */ if (toShutdown.isEmpty() && toAdd.isEmpty()) { // No change return; } // Remove peers no long available List<PeerEurekaNode> newNodeList = new ArrayList<>(peerEurekaNodes); /** * Offline deletion */ if (!toShutdown.isEmpty()) { logger.info("Removing no longer available peer nodes {}", toShutdown); int i = 0; while (i < newNodeList.size()) { PeerEurekaNode eurekaNode = newNodeList.get(i); if (toShutdown.contains(eurekaNode.getServiceUrl())) { newNodeList.remove(i); eurekaNode.shutDown(); } else { i++; } } } /** * Add to peereeurekanodes */ // Add new peers if (!toAdd.isEmpty()) { logger.info("Adding new peer nodes {}", toAdd); for (String peerUrl : toAdd) { newNodeList.add(createPeerEurekaNode(peerUrl)); } } this.peerEurekaNodes = newNodeList; this.peerEurekaNodeUrls = new HashSet<>(newPeerUrls); }
Initializes the registry of application instance information
Initialize the registration information according to the newly created cluster node set: the role of each step has been commented below, and the main codes are analyzed below
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception { // Start the timing task of speed measurement and empty it every minute to realize speed measurement this.numberOfReplicationsLastMin.start(); // Attribute assignment this.peerEurekaNodes = peerEurekaNodes; // Initialize response cache initializedResponseCache(); // Update the renewal threshold through scheduled tasks at regular intervals scheduleRenewalThresholdUpdateTask(); // Initialize remote server registration information initRemoteRegionRegistry(); try { // Monitor registration Monitors.registerObject(this); } catch (Throwable e) { logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", e); } }
Initialize response cache
1. Create cache data with default size of 1000 and expiration of 180 seconds through CacheBuilder
2. If the current instance allows the use of readonly cache, start the scheduled task to update the data of readonlycahemap every 30 seconds. The implementation is to traverse readonlycahemap. If the value value is different from that of readWriteCacheMap, assign the latest value in readWriteCacheMap to readonly cache, so as to ensure that readonly data will not exist dirty data for too long.
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) { this.serverConfig = serverConfig; this.serverCodecs = serverCodecs; this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache(); this.registry = registry; long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs(); // guava cache default capacity 1000180 seconds expired this.readWriteCacheMap = CacheBuilder.newBuilder().initialCapacity(serverConfig.getInitialCapacityOfResponseCache()) .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; } }); if (shouldUseReadOnlyResponseCache) { // Initialize scheduled tasks. Configure Eureka Responsecacheupdateintervalms: sets the task execution frequency. The default value is 30 * 1000 milliseconds // Responsible for updating the data in readwriterMap 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); } }
The filling of readOnly data is to read the data through the response cache when other services initiate full, incremental and application information acquisition. If there is a cache in readOnlyMap, it will be returned directly. Otherwise, read readWriterCacheMap and assign the return value to readOnlyMap to avoid frequent calls to readWriterCacheMap.
Value getValue(final Key key, boolean useReadOnlyCache) { Value payload = null; try { if (useReadOnlyCache) { // Read readonlycahemap first. Cannot read. Read readWriteCacheMap and set it to readOnlyCacheMap final Value currentPayload = readOnlyCacheMap.get(key); if (currentPayload != null) { payload = currentPayload; } else { // Reset after expiration (the default is 180 seconds) payload = readWriteCacheMap.get(key); readOnlyCacheMap.put(key, payload); } } else { // Read readWriteCacheMap payload = readWriteCacheMap.get(key); } } catch (Throwable t) { logger.error("Cannot get value for key : {}", key, t); } return payload; }
So why do you do this to avoid frequent calls to readWriterCacheMap? readWriterCacheMap has been a cache with a capacity of 1000 for recording three minutes. When it misses the cache, you need to obtain specific data through the generatePayload() method. See the following code for the specific code:
.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; } }); // Service payload private Value generatePayload(Key key) { Stopwatch tracer = null; try { String payload; switch (key.getEntityType()) { case Application: boolean isRemoteRegionRequested = key.hasRegions(); if (ALL_APPS.equals(key.getName())) { if (isRemoteRegionRequested) { tracer = serializeAllAppsWithRemoteRegionTimer.start(); payload = getPayLoad(key, registry.getApplicationsFromMultipleRegions(key.getRegions())); } else { // Full acquisition tracer = serializeAllAppsTimer.start(); // Construct the instance data to be cached according to the registered instance collection and key payload = getPayLoad(key, registry.getApplications()); } } else if (ALL_APPS_DELTA.equals(key.getName())) { // Increment (to be completed) if (isRemoteRegionRequested) { tracer = serializeDeltaAppsWithRemoteRegionTimer.start(); versionDeltaWithRegions.incrementAndGet(); versionDeltaWithRegionsLegacy.incrementAndGet(); payload = getPayLoad(key, registry.getApplicationDeltasFromMultipleRegions(key.getRegions())); } else { tracer = serializeDeltaAppsTimer.start(); versionDelta.incrementAndGet(); versionDeltaLegacy.incrementAndGet(); 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(); } } }
The corresponding operations are completed according to different request types. There are many types. Only the full quantity is used as an example for parsing. The other implementations are similar: getPayLoad(): decode the data into a String through the server decoder, return it to the client, and ignore it. getApplicationsFromMultipleRegions(): add monitoring data, which can be ignored. Then read the registry information of the current server and add the lease instance information to the temporary variable apps. At this time, all the instance information of the current server has been obtained, but the local registry of the current server may not be the latest registration information due to network reasons, Therefore, the locally cached registry information of other servers (cached when the server cluster is synchronized, as explained below) is also added to the apps. The remote registry cache data is circulated. The apps obtains the app name. If it does not exist, the app information is created and all instances are added. Because the map and set data structures receive the instance information, there will be no multiple instances after repeated registration, Data duplication. After all apps are obtained, all instance States and state corresponding data are put into the map, and the hashcode of the application set is generated through the natural sorting of hashmap. Hashcode example: DOWN_2_UP_8_.
public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) { boolean includeRemoteRegion = null != remoteRegions && remoteRegions.length != 0; logger.debug("Fetching applications registry with remote regions: {}, Regions argument {}", includeRemoteRegion, remoteRegions); // Add monitoring data of corresponding instruction if (includeRemoteRegion) { GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS.increment(); } else { GET_ALL_CACHE_MISS.increment(); } // Gets the collection of all application instances of the current server Applications apps = new Applications(); apps.setVersion(1L); 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) { app = new Application(lease.getHolder().getAppName()); } app.addInstance(decorateInstanceInfo(lease)); } } if (app != null) { apps.addApplication(app); } } // Add other active server registration data to the collection in turn if (includeRemoteRegion) { for (String remoteRegion : remoteRegions) { // Guess: should there be instance information cache of other clusters during cluster synchronization? RemoteRegionRegistry remoteRegistry = regionNameVSRemoteRegistry.get(remoteRegion); if (null != remoteRegistry) { Applications remoteApps = remoteRegistry.getApplications(); for (Application application : remoteApps.getRegisteredApplications()) { if (shouldFetchFromRemoteRegistry(application.getName(), remoteRegion)) { logger.info("Application {} fetched from the remote region {}", application.getName(), remoteRegion); Application appInstanceTillNow = apps.getRegisteredApplications(application.getName()); if (appInstanceTillNow == null) { appInstanceTillNow = new Application(application.getName()); apps.addApplication(appInstanceTillNow); } for (InstanceInfo instanceInfo : application.getInstances()) { appInstanceTillNow.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); } } } // Set the application set hashcode, which can be used for subsequent matching to verify whether it has been changed (this variable is used to verify whether the registration information obtained incrementally is consistent (complete) with the full registration information of Eureka server) apps.setAppsHashCode(apps.getReconcileHashCode()); return apps; }
Update renewal threshold:
Jump out and then initialize the registry. Next, the renewal threshold will be updated through a scheduled task.
The self-protection threshold of the current server is reset by executing a scheduled task every 15 minutes. First, obtain the values of all instances of the current server. When the data is greater than the expected number of clients * 0.85, or the self-protection mechanism is not enabled, update the expected client values and change the minimum threshold for renewal per minute. When the protection mechanism is enabled, if the running active instance data is less than the expected client * 0.85, no operation will be carried out. This is also the implementation of the server self-protection mechanism. If too many services are offline during this time period of the server task, the server will automatically carry out the self-protection mechanism. If the expected client value and the renewal threshold per minute are not modified, when the eviction instance timing task runs, the renewal of other services expires. The server judges that the number of instances is too small to protect the current registry information and will not evict (analyzed below).
private void scheduleRenewalThresholdUpdateTask() { timer.schedule(new TimerTask() { @Override public void run() { updateRenewalThreshold(); } }, serverConfig.getRenewalThresholdUpdateIntervalMs(), serverConfig.getRenewalThresholdUpdateIntervalMs()); } private void updateRenewalThreshold() { try { // Calculate the number of application instances Applications apps = eurekaClient.getApplications(); int count = 0; for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { if (this.isRegisterable(instance)) { ++count; } } } // If count > last instance count * 0.85, (the server does not enter the self-protection mechanism) or automatic protection is not enabled, update the instance count // After entering the self-protection mechanism, the current registry instance will be protected synchronized (lock) { // Update threshold only if the threshold is greater than the // current expected threshold or if self preservation is disabled. if ((count) > (serverConfig.getRenewalPercentThreshold() * expectedNumberOfClientsSendingRenews) // Self protection mechanism configuration is not enabled || (!this.isSelfPreservationModeEnabled())) { this.expectedNumberOfClientsSendingRenews = count; updateRenewsPerMinThreshold(); } } logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold); } catch (Throwable e) { logger.error("Cannot update renewal threshold", e); } }
Unfinished update tomorrow