1 Overview
This article Share Eureka-Client registration information acquisition process increments to-Server Eureka .
Pre-reading: "Eureka parse the source code - Registration discovery application examples (vi) of get the whole amount."
FROM "depth profiling service discovery component Netflix Eureka"
Eureka-Client for registration information, is divided into the total amount of acquisition and incremental acquisition . Under the default configuration, when the Eureka-Client starts, the first to perform a full amount of acquisition were local cache registration information, and then every 30 Miao incremental acquisition refresh the local cache (not " normal " case will get the whole amount).
This article focuses on the incremental acquisition .
Spring Cloud recommended books :
- Please support genuine. Download pirated, equal to write low-level initiative BUG .
- Program ape DD - "Spring Cloud service combat micro"
- Zhou Li - "Spring Cloud and Docker combat micro Services Architecture"
- QI bought two books, Jingdong free shipping.
Recommended Spring Cloud Video :
- Java Micro Services practice - Spring Boot
- Java Micro Services practice - Spring Cloud
- Java Micro Services practice - Spring Boot / Spring Cloud
2. Apply a set of consistent hashing code
Applications.appsHashCode
Application of a set of consistent hashing code .
Incremental fetching set of applications registered (Applications), Eureka-Client will get to:
- Eureka-Server Application recent set of changes (registration, offline) of
- Eureka-Server application consistent set of hash code
Eureka-Client to change the set of applications and local caching application locally calculated hash code of the application set consistent set of merged. If the two hash codes are equal, meaning increment succeed; if not equal, means that the incremental acquisition has failed, Eureka-Client again and Eureka-Server full amount to obtain application set.
Eureka compare a set of consistent application of the hash code, and everyday we compare whether two objects are equal by similar hash code.
2.1 The formula
appsHashCode = ${status}_${count}_
- Examples of the use state of each application (
status
) + Number (count
) splicing the consistent hashing code. If the number is 0, the state of the application instance without splicing. Sort state string size . - For example, eight UP, 0 a DOWN, then
appsHashCode = UP_8_
. 8 UP, 2 th DOWN, thenappsHashCode = DOWN_2_UP_8_
. - Codes are as follows: // Applications.java public String getReconcileHashCode () {// set count key: application examples status TreeMap <String, AtomicInteger> instanceCountMap = new TreeMap <String, AtomicInteger> (); populateInstanceCountMap (instanceCountMap); // calculate hashcode return getReconcileHashCode (instanceCountMap);}
- Piece count code Integer can be used, without using AtomicInteger.
- Call the
#populateInstanceCountMap()
method, calculating the number of instances of each application state. Codes are as follows: // Applications.java public void populateInstanceCountMap (Map <String, AtomicInteger> instanceCountMap) {for (Application app: this.getRegisteredApplications ()) {for (InstanceInfo info: app.getInstancesAsIsFromEureka ()) {// count AtomicInteger instanceCount = instanceCountMap.computeIfAbsent (info.getStatus () name (), k -> new AtomicInteger (0).); instanceCount.incrementAndGet ();}}} public List <Application> getRegisteredApplications () {return new ArrayList <Application> (this.applications);} // Applications.java public List <InstanceInfo> getInstancesAsIsFromEureka () {synchronized (instances) {return new ArrayList <InstanceInfo> (this.instances);}}
- 调用
#getReconcileHashCode()
方法,计算hashcode
。实现代码如下: public static String getReconcileHashCode(Map<String, AtomicInteger> instanceCountMap) { StringBuilder reconcileHashCode = new StringBuilder(75); for (Map.Entry<String, AtomicInteger> mapEntry : instanceCountMap.entrySet()) { reconcileHashCode.append(mapEntry.getKey()).append(STATUS_DELIMITER) // status .append(mapEntry.getValue().get()).append(STATUS_DELIMITER); // count } return reconcileHashCode.toString(); }
2.2 rationality
In this section, we recommend that you completely understand the text, and then come back here in this section, it is recommended that you understand the full text, go back to where sections of this, it is recommended that you understand the full article, come back here
I just read a set of formulas apply consistent hashing algorithm, in the face of force to rip off the state. So streamlined way to really be able to check the consistency of the data it? I do not know how many readers have the same doubts with the author. Let's justify the algorithm (seriousness of nonsense).
Consistency hash value status + number to calculate, it is not likely to be as much as the total number of states, the actual distribution in different applications? So we list the model as follows:
|
UP |
---|---|
Application A |
m |
Application B |
n |
If application A is down at this time the original application example c a, c of the application B registered application instance a letter, then the number is still in the UP state includes m + n.
- Under normal circumstances, Eureka-Client from the Eureka-Server to get the full incremental change and consolidation, this time as the application form, the two are the same, consistent hashing algorithm is reasonable .
|
UP (server) |
UP (client) |
---|---|---|
Application A |
m - c |
m - c |
Application B |
n + c |
n + c |
- [1] Under unusual circumstances, change record queue all expired. Eureka-Client that acquires from the Eureka-Server to the incremental change in the empty and combined, then the table shown in the following applications, both an application is not the same, the hash value is equal consistency, consistency hash algorithm unreasonable .
|
UP (server) |
UP (client) |
---|---|---|
Application A |
m - c |
m |
Application B |
n + c |
n |
- [2] under abnormal conditions, change history queue expires portion, such as application A and the application B are remaining change record w article. Eureka-Client that acquires from the Eureka-Server to increment of change and combined, both an application is not the same, then the application of the following table, the hash value is equal consistency, consistency hash algorithm unreasonable .
|
UP (server) |
UP (client) |
---|---|---|
Application A |
m - c |
m - w |
Application B |
n + c |
n + w |
What? From the abnormal situation [1] [2] can be seen, consistent hashing algorithm turned out to be unreasonable , then we manually do one of the most streamlined experiments. Experiments are as follows:
- Simulation scenarios: [1] abnormalities, m = n = c = 1. Simple and crude.
- Special configuration
eureka.retentionTimeInMSInDeltaQueue = 1
Change record queue length of 1 ms each record when survival. Eureka-Client requests to achieve less than full incremental changes.eureka.deltaRetentionTimerIntervalInMs = 1
, Each record change record expired queue timer task execution frequency of 1 ms. Eureka-Client requests to achieve less than full incremental changes.eureka.shouldUseReadOnlyResponseCache = false
Disable response cache read-only cache. To avoid waiting for the cache refresh.eureka.waitTimeInMsWhenSyncEmpty = 1
,
- experiment procedure
- 00:00 start Eureka-Server
- 00:30 start the application A, registered with the Eureka-Server
- 01:00 start Eureka-Client, obtain registration information to the Eureka-Server, waiting to get application A
- 01:30 Close the application A. Immediately start the application B, register with the Eureka-Server
- Wait 5 minutes, Eureka-Client can not get to the application B
- At this time, the application table shown below, both an application is not the same, the hash value is equal consistency, consistency hash algorithm unreasonable.
|
UP (server) |
UP (client) |
---|---|---|
Application A |
0 |
1 |
Application B |
1 |
0 |
? Conclusion ?
Of course exclude special extreme scenario, Eureka-Client from the Eureka-Server synchronization because the network has not led to an abnormal incremental changes, and just turned off and on the number of applications to meet the state statistics. Further, when changing the recording expired queue record length is 300 seconds, the incremental acquisition frequency of 30 seconds, the number of views acquired around 10. Therefore, the application of a set of consistent hashing code in most scenarios is reasonable . The author of YY , have to solve this tiny scene as follows:
- A first, modified formula
appsHashCode = MD5(${app_name}_${instance_id}_${status}_${count}_)
, increasing the number of sensitive application name and application examples. - The second, conducted once every N minutes to obtain the full amount of registration information.
ps: With disturbed heart I finished this section, if there is unreasonable, or if there are different points of view fat friends, welcome to explore. Thank you.
TODO [0027] [reflect]: The set of consistent hashing algorithms.
3. Eureka-Client initiated incremental acquisition
In "Eureka parse the source code - Registration discovery application examples (vi) the total amount of access", "2.4 launched for registration information", the calling DiscoveryClient#getAndUpdateDelta(...)
method, the incremental obtain registration information, and to refresh the local cache, codes are as follows:
1: private void getAndUpdateDelta(Applications applications) throws Throwable { 2: long currentUpdateGeneration = fetchRegistryGeneration.get(); 3: 4: // increment for registration information 5: Applications delta = null; 6: EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get()); 7: if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) { 8: delta = httpResponse.getEntity(); 9: } 10: 11: if (delta == null) { 12: // increment acquisition is empty, the whole amount of the acquisition 13: logger.warn("The server does not allow the delta revision to be applied because it is not safe. " 14: + "Hence got the full registry."); 15: getAndStoreFullRegistry (); 16: } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) { 17: logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode()); 18: String reconcileHashCode = ""; 19: if (fetchRegistryUpdateLock.tryLock()) { 20: try { 21: // set of applications that will change the application set and the local cache are combined 22: updateDelta(delta); 23: // Calculate the local hash code consistent set of applications 24: reconcileHashCode = getReconcileHashCode(applications); 25: } finally { 26: fetchRegistryUpdateLock.unlock(); 27: } 28: } else { 29: logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta"); 30: } 31: // There is a diff in number of instances for some reason 32:! If (reconcileHashCode.equals (delta.getAppsHashCode ()) // consistent hash values are not equal 33: || clientConfig.shouldLogDeltaDiff()) { // 34: reconcileAndLogDifference(delta, reconcileHashCode); // this makes a remoteCall 35: } 36: } else { 37: logger.warn("Not updating application delta as another thread is updating it already"); 38: logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode()); 39: } 40: }
- 9, line 4 to: request increment for registration information, codes are as follows: // AbstractJerseyEurekaHttpClient.java @Override public EurekaHttpResponse <Applications > getDelta (String ... regions) {return getApplicationsInternal ( "apps / delta", regions); }
- Call the
AbstractJerseyEurekaHttpClient#getApplicationsInternal(...)
method, GET requests Eureka-Server'sapps/detla
interfaces, parametersregions
, return to the format of JSON, achieve incremental obtain registration information.
- Call the
- 15, line 11 to: incremental acquisition fails, call the
#getAndStoreFullRegistry()
method, the full amount for registration information, and set to the local cache. This method "Eureka parse the source code - Registration discovery application examples (vi) the total amount of access", "2.4.1 full amount for registration information, and to set up a local cache," a detailed resolution. - Lines 16 to 35: processing the incremental results obtained.
- Line 33: configuration
eureka.printDeltaFullDiff
, and whether to print the total amount of the incremental difference. Default:false
. From the current code implementation point of view, there is no effect. Note : This parameter will lead to open each incremental acquisition after launch full amount of acquisition, do not open. - Line 16: TODO [0025]: the case of concurrent updates? ? ?
- Line 19: TODO [0025]: the case of concurrent updates? ? ?
- Line 21: calls the
#updateDelta(...)
method, the change set of applications and local caching of application set to merge. - The first 31-35 lines: consistent hash values are not equal, call the
#reconcileAndLogDifference()
method, the full amount for registration information, and to set up a local cache, and#getAndStoreFullRegistry()
essentially similar.
- Line 33: configuration
3.1 The combined set of applications
Call the #updateDelta(...)
method, the change set of applications and local caching of application set to merge. Codes are as follows:
1: private void updateDelta(Applications delta) { 2: int deltaCount = 0; 3: (Application app: delta.getRegisteredApplications ()) for {application set (change) // loop increment 4: for (InstanceInfo instance : app.getInstances()) { 5: Applications applications = getApplications(); 6: // TODO[0009]:RemoteRegionRegistry 7: String instanceRegion = instanceRegionChecker.getInstanceRegion(instance); 8: if (!instanceRegionChecker.isLocalRegion(instanceRegion)) { 9: Applications remoteApps = remoteRegionVsApps.get(instanceRegion); 10: if (null == remoteApps) { 11: remoteApps = new Applications(); 12: remoteRegionVsApps.put(instanceRegion, remoteApps); 13: } 14: applications = remoteApps; 15: } 16: 17: part count ++; 18: if (ActionType.ADDED.equals(instance.getActionType())) { // 添加 19: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 20: if (existingApp == null) { 21: applications.addApplication(app); 22: } 23: logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion); 24: applications.getRegisteredApplications(instance.getAppName()).addInstance(instance); 25: } else if (ActionType.MODIFIED.equals(instance.getActionType())) { // 修改 26: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 27: if (existingApp == null) { 28: applications.addApplication(app); 29: } 30: logger.debug("Modified instance {} to the existing apps ", instance.getId()); 31: 32: applications.getRegisteredApplications(instance.getAppName()).addInstance(instance); 33: } else if (ActionType.DELETED.equals(instance.getActionType())) { // 删除 34: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 35: if (existingApp == null) { 36: applications.addApplication(app); 37: } 38: logger.debug("Deleted instance {} to the existing apps ", instance.getId()); 39: applications.getRegisteredApplications(instance.getAppName()).removeInstance(instance); 40: } 41: } 42: } 43: logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount); 44: 45: getApplications () SETVERSION (delta.getVersion ());. 46: // filtered disrupted set of applications 47: getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances()); 48: 49: // TODO[0009]:RemoteRegionRegistry 50: for (Applications applications : remoteRegionVsApps.values()) { 51: applications.setVersion(delta.getVersion()); 52: applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances()); 53: } 54: }
- Line 15 to 6: TODO [0009]: RemoteRegionRegistry
- Lines 18 to 24: when adding (ADDED) application example, invoke
Application#addInstance(...)
method implementation code as follows: // Application.java public void addInstance (InstanceInfo i) {// add to the application instance mapping instancesMap.put (i.getId () , i); synchronized (instances) {// remove the existing instance instances.remove (i); // Add a new instance instances.add (i); // set isDirty, currently used only for `#toString ()` The method of printing, no business logic isDirty = true;}} // InstanceInfo.java @Override public int hashCode () {// calculation using only ID hashcode String id = getId (); return (id == null) 31:? ( id.hashCode () + 31);} @Override public boolean equals (Object obj) {// only Comparative ID if (this == obj) { return true;} if (obj == null) {return false;} if (! getClass () = obj.getClass ( )) {return false;} InstanceInfo other = (InstanceInfo) obj; String id = getId ();! if (id == null) {if (other.getId () = null ) {return false;}} else if) {return false (id.equals (other.getId ()!);} return true;} - The first 25 to 32 lines: modifying (the MODIFIED) application example, the same call
Application#addInstance(...)
method. - 33 to line 40: delete (DELETED) application example, invoke
Application#removeInstance(...)
methods, codes are as follows: public void removeInstance (InstanceInfo i) {removeInstance (i, true);} private void removeInstance (InstanceInfo i, boolean markAsDirty) {// application examples mapping removed instancesMap.remove (i.getId ()); synchronized (instances) {// remove application examples instances.remove (i); if (markAsDirty ) {// set isDirty, currently used only for `# toString () `method of printing, no business logic isDirty = true;}}} - Line 47: calling
Applications#shuffleInstances(...)
method, according to the configurationeureka.shouldFilterOnlyUpInstances = true
(default:true
) filter retaining only the application instance is ON state (UP), and to randomly disrupt the application example sequence. After the upset, realize randomness calling application services. Relatively easy to understand the code, click on the link to see the methods. - Lines 53 to 49: TODO [0009]: RemoteRegionRegistry
4. Eureka-Server receives the full amount acquisition
The total amount acquisition request receiving 3.1
com.netflix.eureka.resources.ApplicationsResource
Processing all requests application operating Resource (Controller).
Receiving the acquisition request incremental mapping ApplicationsResource#getContainers()
method.
- And "Eureka source code parsing - registration application examples found (six) of the total amount of access" "to receive the full amount of 3.1 acquisition request" is similar to not repeat long-winded friends.
- Click on the link to see the method with Chinese comments code.
3.2 Lease recent change record queue
AbstractInstanceRegistry.recentlyChangedQueue
Recently lease change record queue. Codes are as follows:
// AbstractInstanceRegistry.java /** * Recent lease change record queue */ private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>(); /** * Recent changes recorded lease */ private static final class RecentlyChangedItem { /** * Last updated time stamp */ private long lastUpdateTime; /** * Lease */ private Lease<InstanceInfo> leaseInfo; public RecentlyChangedItem(Lease<InstanceInfo> lease) { this.leaseInfo = lease; lastUpdateTime = System.currentTimeMillis(); } public long getLastUpdateTime() { return this.lastUpdateTime; } public Lease<InstanceInfo> getLeaseInfo() { return this.leaseInfo; } }
- When an application instance is registered, offline, status changes, create a lease recently changed record (RecentlyChangedItem) to the queue.
- Timing background task sequential scan queue, when
lastUpdateTime
carried out to remove more than a certain length of time. Codes are as follows: // AbstractInstanceRegistry.java this.deltaRetentionTimer.schedule (getDeltaRetentionTask () , serverConfig.getDeltaRetentionTimerIntervalInMs (), serverConfig.getDeltaRetentionTimerIntervalInMs ()); private TimerTask getDeltaRetentionTask () {return new TimerTask () {@Override public void run ( ) {Iterator & lt; RecentlyChangedItem & gt; it = recentlyChangedQueue.iterator (); while (it.hasNext ()) {if (it.next () getLastUpdateTime () & lt;. System.currentTimeMillis () - serverConfig.getRetentionTimeInMSInDeltaQueue ()) {it .remove ();} else {break ;}}}};}- Configuration
eureka.deltaRetentionTimerIntervalInMs
, removing the lease expired queue timer task execution frequency change history, unit: ms. Default: 30 * 1000 milliseconds. - Configuration
eureka.retentionTimeInMSInDeltaQueue
, change records expired lease length, unit: milliseconds. Default: 3 * 60 * 1000 milliseconds.
- Configuration
3.3 cache reads
In "Eureka parse the source code - Registration discovery application examples (vi) the total amount of access", "3.3 cache reads", in the #generatePayload()
method, the call AbstractInstanceRegistry#getApplicationDeltas(...)
method to get a collection of recent changes in the application, codes are as follows:
// AbstractInstanceRegistry.java 1: public Applications getApplicationDeltas() { 2: // add an incremental number to get monitor 3: GET_ALL_CACHE_MISS_DELTA.increment(); 4: // initialize set of application changes 5: Applications apps = new Applications(); 6: apps.setVersion(responseCache.getVersionDelta().get()); 7: Map<String, Application> applicationInstancesMap = new HashMap<String, Application>(); 8: try { 9: // Get a write lock 10: write.lock(); 11: // Get the most recent lease change record queue 12: Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator(); 13: logger.debug("The number of elements in the delta queue is :" + this.recentlyChangedQueue.size()); 14: // set of applications assembled change 15: while (iter.hasNext()) { 16: Lease<InstanceInfo> lease = iter.next().getLeaseInfo(); 17: InstanceInfo instanceInfo = lease.getHolder(); 18: Object[] args = {instanceInfo.getId(), instanceInfo.getStatus().name(), instanceInfo.getActionType().name()}; 19: logger.debug("The instance id %s is found with status %s and actiontype %s", args); 20: Application app = applicationInstancesMap.get(instanceInfo.getAppName()); 21: if (app == null) { 22: app = new Application(instanceInfo.getAppName()); 23: applicationInstancesMap.put(instanceInfo.getAppName(), app); 24: apps.addApplication(app); 25: } 26: app.addInstance(decorateInstanceInfo(lease)); 27: } 28: 29: // TODO[0009]:RemoteRegionRegistry 30: boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion(); 31: if (!disableTransparentFallback) { 32: Applications allAppsInLocalRegion = getApplications(false); 33: 34: for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) { 35: Applications applications = remoteRegistry.getApplicationDeltas(); 36: for (Application application : applications.getRegisteredApplications()) { 37: Application appInLocalRegistry = 38: allAppsInLocalRegion.getRegisteredApplications(application.getName()); 39: if (appInLocalRegistry == null) { 40: apps.addApplication(application); 41: } 42: } 43: } 44: } 45: 46: // Get the application sets the full amount by which the hash value is calculated consistency 47: Applications allApps = getApplications(!disableTransparentFallback); 48: apps.setAppsHashCode(allApps.getReconcileHashCode()); 49: return apps; 50: } finally { 51: write.unlock(); 52: } 53: }
- To 3, line 2: Add the number of times to get incremental monitoring. With Netflix Servo implement monitoring information collection.
- Line 4: (incremental) change in set of applications initialize (
apps
). - Line 9: Get a write lock. In "Eureka parse the source code - Registration application examples found (nine) years of Meng Meng is the read-write lock" detailed analysis.
- 13, line 11 to: Get the most recent lease change record queue (
最近租约变更记录队列
). - Lines 14 to 27: The collection assembly changes (
apps
). - Lines 44 to 29: TODO [0009]: RemoteRegionRegistry
- 48, line 46: Calling the
#getApplications(...)
method to obtain the full amount of the set of applications (allApps
), the "Eureka parse the source code - Registration discovery application examples (vi) the total amount of access", "3.3.1 to be registered set of applications" have a detailed analysis. After the adoption ofallApps
computing consistent hashing value. With this set of applications hash value of the whole amount, Eureka-Client after obtaining incremental set of applications and merge, you can compare it. - Line 51: the release write lock.