Eureka source code analysis Part 2 - cluster synchronization and service elimination

Eureka source code analysis Part 2

Cluster synchronization

How to say cluster synchronization? I think before looking at the source code, we must learn to think about when cluster synchronization will be executed?

Once the cluster is synchronized, it makes sense that we must have modified the cluster. When did we modify the cluster, such as service registration and heartbeat renewal? Is it necessary to synchronize the cluster once the service is registered and heartbeat renewal.

So let's go through the code again according to Eureka source code analysis part I,

First, we need to enter a class instanceregistry in Eureka and find it in this class

In this class, we still need to find the method of service registration. Why do we find it? We have to verify the above guess. After the service is registered, is cluster synchronization followed.

  //In fact, in the end, you will find that after the service registration, the cluster synchronization method is followed
//PeerAwareInstanceRegistryImpl. The Java class performs the function of cluster synchronization and the replicateToPeers() method
//This method has two important parameters, action Register   isReplication
//The first parameter: whether the cluster synchronization is service registration or heartbeat renewal. Action is an enumeration
//The second parameter: isreployment is a boolean type. It is very important to check whether the cluster is synchronized

//The following picture is the process of Eureka cluster synchronization

//When configuring Eureka cluster, we use the application How is it written in YML?
//server1: http://localhost:3002/eureka,http://localhost:3002/eureka
//server2: http://localhost:3001/eureka,http://localhost:3003/eureka
//server3: http://localhost:3001/eureka,http://localhost:3002/eureka

I have to do another wave of code. I'm tired, but I must finish it this year and won't look at the spring source code in the future.

Let's review how we register the service and where is the entrance in Chapter 1 of Eureka source code analysis?

We said at the beginning that we initialized a Jersey, similar to the spring MVC framework, to intercept requests

If we are writing the spring MVC project, after the initialization is completed, we have to write the Controller layer. I don't know if you think of anything. If you still don't, we need to take a look at the first chapter of Eureka source code analysis

At this time, I want to find the code block of Eureka source code analysis Part 1. There is a place where the heartbeat renewal of 90S is passed,

I marked a method this Replicatetopeers() is a method for cluster synchronization,

Now I'll paste this method directly:

private void replicateToPeers(
    							PeerAwareInstanceRegistryImpl.Action action,
    							String appName, 
    							String id, 
    							InstanceInfo info, 
    							InstanceStatus newStatus, 
    							boolean isReplication){
    
    //Let's talk about the role of this judgment. This is the most important place for Eureka cluster synchronization
    //As we said earlier, all micro service registrations will be the same as the above, and will call a class in Eureka
    //There is a register method in the instanceregistry class to register the service
    //isReplication as I said earlier, this flag is to judge whether cluster synchronization is synchronization between server s
    //Or the synchronization at the time of client registration
    
    //If it's client registration, you should understand that all micro services will go through the above registration,
    //Will all follow the same cluster synchronization? Will this be a problem? Of course
    //Therefore, a judgment is added here. If peer eurekanodes is in application When configuring the cluster with YML
    //If it is empty, it is proved to be stand-alone. If isReplication is fasle, it is registered
    //Direct return, do not allow cluster synchronization
    
    //If the synchronization is between server s, the isReplication must be true
    if (this.peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
        return;
    }
    
    //Here is another knowledge point
    server1: http://localhost:3002/eureka,http://localhost:3002/eureka
    //For example, when configuring server1, why not add it http://localhost:3001/eureka
    //It is the following code to judge
    if (this.peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
        //Judge the address. If it is the current address, continue directly. Therefore, there is no need to configure the current address http://localhost:3001/eureka
        continue;
    }
    //This is the real place for cluster synchronization,
    //The point I want to get has been got. There will be a template calling HTTP, such as template
    //This method has not been studied in depth for the time being
    this.replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
}
                              

Service rejection

There are two concepts: one is active and the other is passive

Service rejection: my server has not received the heartbeat renewal of the client for a long time. It proves that it has hung up. I will take the initiative to reject it

‚Äč Regularly clean up microservices (timers) that haven't renewed their contracts for a long time

Service off the shelf: it can be understood that the client doesn't need it directly and takes the initiative to off the shelf the service center

//EurekaServerInitializerConfiguration.java
//Remember this class? To initialize this class of Eureka context environment, refer to the beginning of Eureka source code analysis Part 1

//In this class, why does the start() method start a thread to start
//What I want to say is that this code is great. When I write Middleware in the future, if I have the same business, I should also write it like this
//Explain: remember how spring cloud was born? Spring cloud integrates a lot of plug-ins to solve this problem
//The call between microservices, such as avalanche, degradation, etc. in fact, springcloud did nothing but put these mature technologies on the market
//It is integrated into the springboot and assembled into a springcloud. Like Lenovo, it is an assembly based gadget

//Well, we can't make the whole spring boot fail because of a problem with one of the modules, can we
//So, we need to start a thread to start this thing. What does it matter if we can't afford it? It's asynchronous anyway. It doesn't affect my springcloud startup

//That is, even if Eureka has problems, spring cloud can start normally, because the component is not just Eureka

//Let's also talk about the two interfaces implemented by this class, which will be explained in detail according to the case in Spring source code analysis
//ServletContextAware
	// ServletContextAware is very important. To put it bluntly, any Aware is to get the container under the Spring environment
	//After Spring is initialized, the purpose of implementing such an interface is to get the Spring environment

//SmartLifecycle
	//There are many methods in this interface, such as the start() stop() method
	//start() is the method that Spring calls when it starts
	//The method that the stop() container calls when it stops	
	//What's the use of this? For example, if you want to load some hot data when Spring starts, you can use this interface
	
    public class EurekaServerInitializerConfiguration implements ServletContextAware,  		 	SmartLifecycle, Ordered {
    
    //Implement the interface ServletContextAware and get the servletContext container
    private ServletContext servletContext;
    public void setServletContext(ServletContext servletContext){
        this.servletContext = servletContext;
    }
    
    public void start() {
        (new Thread(new Runnable() {
            //The environment was initialized and two listeners were published
            this.eurekaServerBootstrap.contextInitialized(this.servletContext);
            this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));
            this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));

        })).start();

        }
    }
}

contextInitialized(this.servletContext){
    
    //This is the initialization of Eureka local configuration, which is what is in the yml file
    this.initEurekaEnvironment();
    //This is some information about the initialization of EurekaServer
    this.initEurekaServerContext();
}

protected PeerAwareInstanceRegistry registry;

initEurekaServerContext(){
    
    //Where to synchronize clusters
    int registryCount = this.registry.syncUp();
    this.registry.openForTraffic(this.applicationInfoManager, registryCount);
}

public int syncUp() {
    
    int count = 0;//This count will be used to record how many microservices have been synchronized, which will be used in the self-protection mechanism
    
    //private int registrySyncRetries = 0;  The number of retries is 0 by default
    //private long registrySyncRetryWaitMs = 30000L;  Retry wait time
    //This can be configured in the configuration file
    for(int i = 0; i < this.serverConfig.getRegistrySyncRetries() && count == 0; ++i) {
        
         if (i > 0) {
             Thread.sleep(this.serverConfig.getRegistrySyncRetryWaitMs());
         }
    }
}


public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
    
    //When EurekaServer is initialized, the self-protection threshold is updated
    //In Chapter 3 of Eureka source code analysis, we will talk about several places where Eureka updates its self-protection threshold
    
    //Why is this multiplied by two?
    //When count Eureka is initialized, count is the number of successful service registration. 30S once
    //syncUp() please take a closer look at the code of the above method. This thing here is difficult to explain, so you can only rely on your own understanding
    //On my side, the statistics are per minute, so * 2 is required
    this.expectedNumberOfRenewsPerMin = count * 2;
    //This is the threshold of self-protection mechanism
    this.numberOfRenewsPerMinThreshold = 
        (int)((double)this.expectedNumberOfRenewsPerMin * 				
              this.serverConfig.getRenewalPercentThreshold());
    
    super.postInit(); //In this, a timer is started to do service elimination
    
    //It encapsulates a Timer to clean the Timer timer of the micro service JDK regularly, which is not coupled with any framework
    //Since it's a timer, there must be time. In the yml configuration file, the time of occurrence interval timer in MS can be configured
}

//This is the method in the timer
public void evict(long additionalLeaseMs) {
    
    //This is what triggers the self-protection mechanism
    //Once triggered, it will not be cleaned up
    if (!this.isLeaseExpirationEnabled()) {
        logger.debug("DS: lease expiration is currently disabled.");
    }else{
        
        //Then, it's time to take a look and see how Eureka does service elimination
        
        //Firstly, a set of services that need to be eliminated is defined
        List<Lease<InstanceInfo>> expiredLeases = new ArrayList();
        
        //Remember the map collection when the service was registered?
        //ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
        //As I said above, the service is eliminated. The micro services that have not received heartbeat renewal are excluded
        //Well, do I have to traverse the registry? Remember the Lease object I said in Eureka source code analysis Part 1?
        //There is also a way to judge whether the micro service has expired
        
        //Traverse my cluster objects
        Iterator var4 = this.registry.entrySet().iterator();
        while(true) {
            
            int registrySize = (int)this.getLocalRegistrySize();//Calculate how many microservices there are currently
            //The default value of RenewalPercentThreshold is 0.85D
            //Calculate such a threshold
            int registrySizeThreshold = (int)((double)registrySize * 
            	this.serverConfig.getRenewalPercentThreshold());
            //Total quantity - threshold = limit of service rejection
            int evictionLimit = registrySize - registrySizeThreshold;
            //How many services have expired and the limit of service rejection is taken as the small value
            int toEvict = Math.min(expiredLeases.size(), evictionLimit);
            //This involves the self-protection mechanism. Why take the smaller value of the two? See the beginning of Eureka source code analysis Part 3
            //In order to prevent the number of micro services rejected from exceeding 15% of the total number, this value can be configured, which is 0.85D above
            
            //The high availability of Eureka cluster is shown here, with a cluster high availability rate of 85%
            //Anyway, this elimination is timed!, For example, I need to eliminate 30 this time. In fact, I can only eliminate 15
            //Next time, eliminate the remaining 15. Who knows whether it is caused by network fluctuations, Eureka or something
            //The remaining 15 can be eliminated in the next cycle. Maybe these services can be used again when they are eliminated next time!
            //In this case, there is no need to eliminate it. Everyone is happy
            if (toEvict > 0) {
                
                //This is where the service is removed
                //It is absolutely not allowed to eliminate the elements in the set in turn. It is definitely not possible to sort and eliminate the elements in the set directly
                //1. Reduce pressure and eliminate micro services on different services
                	//A "shuffle algorithm" is used here
                	//Next generates such a random number, collections swap(expiredLeases, i, next);
                	//The swap() method is used to exchange the next element of the set expiredleaves with the i position element
                	//In that case, according to the for loop, what I eliminate is the element at the random next position
                //2. The second reason is the most important. For example, I have a user micro service cluster, which has hung up. If the round robin is eliminated, all at once
                	//If you eliminate them all, it's a bad thing. Then you can't use the whole project
                	//If I say so, you may have questions. It's impossible to call. Why keep it? What's the use?
                	//What is the nature of service availability? It can be adjusted, not based on whether the correct value can be returned
                	//When using spring cloud, you will definitely use the Hystrix service fault tolerance mechanism
              		//We will all write fault-tolerant mechanisms in microservices to ensure that microservices can be invoked
                for(int i = 0; i < toEvict; ++i) {
                    int next = i + random.nextInt(expiredLeases.size() - i);
                    Collections.swap(expiredLeases, i, next);
                    Lease<InstanceInfo> lease = (Lease)expiredLeases.get(i);
                    String appName = ((InstanceInfo)lease.getHolder()).getAppName();
                    String id = ((InstanceInfo)lease.getHolder()).getId();
                    EurekaMonitors.EXPIRED.increment();
                    //Eureka thief JB is lazy. This internalCancel method is the method of taking the service off the shelf
                    //This code is service off the shelf, which can also be said to be service rejection. Anyway, it is the logic of this code
                    //The threshold of self-protection mechanism will also be updated here
                    this.internalCancel(appName, id, false);
                }
            }
            
            //Anyway, this is final. Judge whether the micro service is overdue
            if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {
            	expiredLeases.add(lease);
            }
        }
    }
    
}




Tags: Spring

Posted by adelinewss on Fri, 20 May 2022 06:25:01 +0300