Spring Cloud Eleven: Eureka self-protection source code analysis

Eureka self-protection

默认情况下,当EurekaServer在一定时间内(默认90秒)没有接收到某个客户端实例的心跳,EurekaServer将会注销该实例。
但是当网络分区故障发生时,客户端与EurekaServer之间无法正常通信,此时不应该注销客户端。
Eureka通过“自我保护机制”来解决这个问题:当EurekaServer短时间内丢失过多客户端时,这个节点就会进入自我保护模式。
在自我保护模式下,EurekaServer不会剔除任何客户端。当网络故障恢复后,该节点会自动退出自我保护模式

The realization of the self-protection mechanism is maintained based on AbstractInstanceRegistrytwo variables in the class that maintains the service registry

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    
    
    // 每分钟最小续约阈值
    protected volatile int numberOfRenewsPerMinThreshold;
    // 预期中发送续约消息的客户端数量
    protected volatile int expectedNumberOfClientsSendingRenews;
}

Eureka Server starts

You can read this article for the source code of Eureka Server startup: Spring Cloud 3: Detailed analysis of Eureka Server source code

public class EurekaServerBootstrap {
    
    

	// 省略部分代码

	protected void initEurekaServerContext() throws Exception {
    
    

		// 集群同步
		int registryCount = this.registry.syncUp();
        // 自我保护初始化
        // 服务定时自动剔除。
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);
	}
    
    // 省略部分代码
}

After starting the server and synchronizing information from other clusters, a method is executed:openForTraffic

@Override
    public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
    
    
        // 初始化期望client发送过来的服务数量,即上面获取到的服务数量
        this.expectedNumberOfClientsSendingRenews = count;
        //计算自我保护的统计参数
        updateRenewsPerMinThreshold();
        this.startupTime = System.currentTimeMillis();
        //   如果count=0,没有拉取到注册表信息,将此值设为true,表示其他peer来取空的实例信息,意味着,将不允许client从此server获取注册表信息。如果count>0,将此值设置为false,允许client来获取注册表。
        if (count > 0) {
    
    
            this.peerInstancesTransferEmptyOnStartup = false;
        }
        // 服务置为上线
        applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
        // 开启剔除的定时任务
        super.postInit();
    }

The expected number of clients sending renewal messages (expectedNumberOfClientsSendingRenews) is equal to the number of services obtained by syncUp() cluster synchronization. Use the following updateRenewsPerMinThreshold() method to calculate the minimum renewal threshold per minute :

	//每分钟最小续约阈值
	//包含所有客户端,不分服务提供方和消费方
	protected volatile int numberOfRenewsPerMinThreshold;

	protected void updateRenewsPerMinThreshold() {
    
    
        this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
                * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
                * serverConfig.getRenewalPercentThreshold());
    }

The translation is as follows:

每分钟最小续约阈值 = 预期中发送续约消息的客户端数量 * (60s / 每分钟客户端预期续约间隔) * 续约百分比阈值
  • Minimum renewal threshold per minute: Eureka Server must receive at least this number of renewal messages (heartbeats) every minute.
  • The number of messages sent by the client to renew expected: Eureka Server to register the number of clients .
  • 60s / Expected client renewal interval per minute: The number of renewals of a client in one minute . The client's expected renewal interval every minute, the default is 30s. So in one minute, the default number of renewals for a client is 2 times .
  • Renewal percentage threshold: 85% by default.

Self-protection timed tasks

DefaultEurekaServerContextThere is a initializemethod in the class , this method will start a timing task during execution

public class DefaultEurekaServerContext implements EurekaServerContext {
    
    
    @PostConstruct
    @Override
    public void initialize() {
    
    
        logger.info("Initializing ...");
        peerEurekaNodes.start();
        try {
    
    
            registry.init(peerEurekaNodes);
        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
        logger.info("Initialized");
    }
}
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
    
    
    @Override
    public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
    
    
        this.numberOfReplicationsLastMin.start();
        this.peerEurekaNodes = peerEurekaNodes;
        initializedResponseCache();
        scheduleRenewalThresholdUpdateTask();
        initRemoteRegionRegistry();

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

scheduleRenewalThresholdUpdateTaskThis timed task is related to the self-protection mode

private void scheduleRenewalThresholdUpdateTask() {
    
    
        timer.schedule(new TimerTask() {
    
    
                           @Override
                           public void run() {
    
    
                               updateRenewalThreshold();
                           }
                       }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
                serverConfig.getRenewalThresholdUpdateIntervalMs());
    }

The getRenewalThresholdUpdateIntervalMsdefault value is 15 minutes

private void updateRenewalThreshold() {
    
    
        try {
    
    
            // 计算服务个数
            Applications apps = eurekaClient.getApplications();
            int count = 0;
            for (Application app : apps.getRegisteredApplications()) {
    
    
                for (InstanceInfo instance : app.getInstances()) {
    
    
                    if (this.isRegisterable(instance)) {
    
    
                        ++count;
                    }
                }
            }
            // 
            synchronized (lock) {
    
    
                // 仅当阈值大于当前的预期阈值或禁用自我保护时才更新阈值。
                // 更新 预期中发送续约消息的客户端数量 expectedNumberOfClientsSendingRenews
                // 更新 每分钟最小续约阈值numberOfRenewsPerMinThreshold
                if ((count) > (serverConfig.getRenewalPercentThreshold() * expectedNumberOfClientsSendingRenews)
                        || (!this.isSelfPreservationModeEnabled())) {
    
    
                    // 更新 预期中发送续约消息的客户端数量
                    this.expectedNumberOfClientsSendingRenews = count;
                    // 更新 每分钟最小续约阈值(numberOfRenewsPerMinThreshold)
                    updateRenewsPerMinThreshold();
                }
            }
        }
    }

Service registration

Whenever an instance is registered, the expected number of clients sending renewal messages (expectedNumberOfClientsSendingRenews) and the minimum renewal threshold per minute (numberOfRenewsPerMinThreshold) are updated.

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    
    
    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
    
    
        
        // 省略部分代码...
        
        if (this.expectedNumberOfClientsSendingRenews > 0) {
    
    
            // 新注册上来的,所以原值+1
            this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
             updateRenewsPerMinThreshold();
        }
    }
}

Service offline

Whenever an instance goes offline, the expected number of clients sending renewal messages (expectedNumberOfClientsSendingRenews) and the minimum renewal threshold per minute (numberOfRenewsPerMinThreshold) are updated.

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    
    
    protected boolean internalCancel(String appName, String id, boolean isReplication) {
    
    
        
        // 省略部分代码...
        
        if (this.expectedNumberOfClientsSendingRenews > 0) {
    
    
           // 一个服务下线了,所以要减去一个
           this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
           updateRenewsPerMinThreshold();
        }
    }
}

Turn on self-protection mode

When the server periodically removes expired services, it will determine whether the service is allowed to be removed. If self-protection is turned on or the number of current service renewals> the minimum number of renewals , service removal is allowed

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    
    
    public void evict(long additionalLeaseMs) {
    
    
        // 是否允许服务剔除,如果允许服务剔除则走下面的服务剔除逻辑,如果不允许服务剔除则返回
        if (!isLeaseExpirationEnabled()) {
    
    
            logger.debug("DS: lease expiration is currently disabled.");
            return;
        }
        
        // 省略部分代码...
    }
}
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
    
    
    /**
     * 是否允许服务剔除,没开自我保护,或者开启了自我保护,然而实际续约个数>最小续约个数,所以允许服务剔除。
     */
    @Override
    public boolean isLeaseExpirationEnabled() {
    
    
        // 检查是否启用了自我保护
        if (!isSelfPreservationModeEnabled()) {
    
    
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }
}

Guess you like

Origin blog.csdn.net/u013277209/article/details/111373281