【SpringCloud】Eureka源码解析 下

发布于:2024-07-01 ⋅ 阅读:(15) ⋅ 点赞:(0)

e246a1dda09849a5a89535a62441565d.png

eurkea是一个服务发现与注册组件,它包含客户端和服务端,服务端负责管理服务的注册信息,客户端用于简化与服务端的交互。上一章分析了eureka的服务注册,这一章来分析eureka的心跳机制

参考源码:<spring-cloud.version>Hoxton.SR9</spring-cloud.version>

上一章:【SpringCloud】Eureka源码解析 上-CSDN博客

1、心跳机制

eureka的客户端会持续向服务端发送心跳,这个定时动作就是心跳机制。根据上一章的分析,我们合理推测定时任务和initScheduledTasks方法有关。查看DiscoveryClient类的initScheduledTasks方法,详情如下

private void initScheduledTasks() {
	...
	// 心跳定时任务,具体任务在HeartbeatThread内
	this.heartbeatTask = new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, 
	new HeartbeatThread());
		this.scheduler.schedule(this.heartbeatTask, (long)renewalIntervalInSecs, TimeUnit.SECONDS);
	...  
}

private class HeartbeatThread implements Runnable {
	private HeartbeatThread() {
	}

	public void run() {
		// 续约
		if (DiscoveryClient.this.renew()) {
			DiscoveryClient.this.lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
		}
	}
}

boolean renew() {
	...
	// 发送心跳
	EurekaHttpResponse<InstanceInfo> httpResponse = this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, (InstanceInfo.InstanceStatus)null);
	...
}

2、剔除服务

注册列表中的服务超过一定时间没有续约,eureka服务端就会将该服务从注册列表中剔除,剔除服务同样用到了定时任务

调用链:EurekaServerInitializerConfiguration.start
-> EurekaServerBootstrap.contextInitialized
-> initEurekaServerContext
-> PeerAwareInstanceRegistryImpl.openForTraffic
-> AbstractInstanceRegistry.postInit
protected void postInit() {
	...
	// 定时剔除任务,具体任务在EvictionTask内
	this.evictionTaskRef.set(new EvictionTask());
	this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), this.serverConfig.getEvictionIntervalTimerInMs(), this.serverConfig.getEvictionIntervalTimerInMs());
	...
}

class EvictionTask extends TimerTask {
	...
	public void run() {
		// 具体的剔除任务
		AbstractInstanceRegistry.this.evict(compensationTimeMs);
	}
}

// 剔除方法
public void evict(long additionalLeaseMs) {
	logger.debug("Running the evict task");
	// 是否启用心跳保护机制
	if (!this.isLeaseExpirationEnabled()) {
		logger.debug("DS: lease expiration is currently disabled.");
	} else {
		...
		int registrySize = (int)this.getLocalRegistrySize();
		// 根据阈值计算可以被剔除的服务最大数量
		int registrySizeThreshold = (int)((double)registrySize * 
		this.serverConfig.getRenewalPercentThreshold());
		// 剔除后剩余最小数量
		int evictionLimit = registrySize - registrySizeThreshold;
		// 计算两者的最小值,剔除较小数量的服务实例
		int toEvict = Math.min(expiredLeases.size(), evictionLimit);
		if (toEvict > 0) {
		   for(int i = 0; i < toEvict; ++i) {
		   ...
		   // 执行删除动作
		   this.internalCancel(appName, id, false);
		   ...
		}
	}
}

3、保护机制

eureka服务端在短时间内丢失大量客户端时,会启用自我保护模式。在保护模式下,eureka服务端不会剔除服务,直到故障恢复后,eureka服务端才会退出保护模式

eureka的自我保护机制和剔除服务相关,找到AbstractInstanceRegistry类postInit服务剔除方法

 // 剔除方法
public void evict(long additionalLeaseMs) {
	// 是否允许剔除服务
	if (!this.isLeaseExpirationEnabled()) {
	   ...
	}
}
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
    
    public boolean isLeaseExpirationEnabled() {
        // 是否启用自我保护机制
        if (!this.isSelfPreservationModeEnabled()) {
            return true;
        } else {
            // 已启用保护机制
            // 实际续约个数大于最小续约个数时,允许服务剔除
            return this.numberOfRenewsPerMinThreshold > 0 && this.getNumOfRenewsInLastMin() > (long)this.numberOfRenewsPerMinThreshold;
        }
    }

    public boolean isSelfPreservationModeEnabled() {
        return this.serverConfig.shouldEnableSelfPreservation();
    }
}

4、总结

eureka客户端通过定时任务向服务端发送心跳完成续约,eureka服务端会从服务列表中剔除超时未续约的服务;若出现网络故障丢失大量服务端,eureka服务端会进入自我保护模式,不再剔除服务,直到故障修复