天天看点

spring cloud eureka server 源码分析

还是从 spring.factories 文件分析开始
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
           
EurekaServerAutoConfiguration 源码
@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
    
    
	@Bean
	@ConditionalOnMissingBean
	public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
			PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
		return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
				registry, peerEurekaNodes, this.applicationInfoManager);
	}
    
    @Bean
	public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
			ServerCodecs serverCodecs) {
		this.eurekaClient.getApplications(); // force initialization
		return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
				serverCodecs, this.eurekaClient,
				this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
				this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
	}
    
    @Bean
	public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
			EurekaServerContext serverContext) {
		return new EurekaServerBootstrap(this.applicationInfoManager,
				this.eurekaClientConfig, this.eurekaServerConfig, registry,
				serverContext);
	}
    
           

eureka server 驱逐策略

主要看EurekaServerInitializerConfiguration 类

@Configuration(proxyBeanMethods = false)
public class EurekaServerInitializerConfiguration
		implements ServletContextAware, SmartLifecycle, Ordered {
           

SmartLifecycle 为spring 的扩展接口这部分我们暂时不讨论最后会调到

protected void initEurekaServerContext() throws Exception {
		// For backward compatibility
		JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);
		XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);

		if (isAws(this.applicationInfoManager.getInfo())) {
			this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
					this.eurekaClientConfig, this.registry, this.applicationInfoManager);
			this.awsBinder.start();
		}

		EurekaServerContextHolder.initialize(this.serverContext);

		log.info("Initialized server context");

		// Copy registry from neighboring eureka node
		int registryCount = this.registry.syncUp();
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// Register all monitoring statistics.
		EurekaMonitors.registerAllStats();
	}
           

this.registry.openForTraffic(this.applicationInfoManager, registryCount); 分析重点再这行代码最后会调到

AbstractInstanceRegistry#postInit()

protected void postInit() {
        renewsLastMin.start();
        if (evictionTaskRef.get() != null) {
            evictionTaskRef.get().cancel();
        }
        evictionTaskRef.set(new EvictionTask());
        evictionTimer.schedule(evictionTaskRef.get(),
                serverConfig.getEvictionIntervalTimerInMs(),
                serverConfig.getEvictionIntervalTimerInMs());
    }
           

evictionTimer eureka server 驱逐策略,默认60s执行一次,我们可以修改默认配置

eureka:

server:

eviction-interval-timer-in-ms: 30

从eureka server 注册列表删除90s未续约的服务,主要代码如下,

public void evict(long additionalLeaseMs) {
        logger.debug("Running the evict task");

        if (!isLeaseExpirationEnabled()) {
            logger.debug("DS: lease expiration is currently disabled.");
            return;
        }

        // We collect first all expired items, to evict them in random order. For large eviction sets,
        // if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,
        // the impact should be evenly distributed across all applications.
        List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>();
        for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {
            Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();
            if (leaseMap != null) {
                for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {
                    Lease<InstanceInfo> lease = leaseEntry.getValue();
                    if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {
                        expiredLeases.add(lease);
                    }
                }
            }
        }

           

eureka server 自我保护

继续来看一下 eureka 的自我保护当一分钟收到客户端心跳的数量低于期望收到心跳数量的 0.85 的时候会开启自我保护 ,出现自我保护的条件是

numOfRenewsInLastMin 小于numOfRenewsPerMinThreshold 会激活eureka自我保护

MeasuredRate 这个类会每隔1分钟重置一下numOfRenewsInLastMin

public synchronized void start() {
        if (!isActive) {
            timer.schedule(new TimerTask() {

                @Override
                public void run() {
                    try {
                        // Zero out the current bucket.
                        lastBucket.set(currentBucket.getAndSet(0));
                    } catch (Throwable e) {
                        logger.error("Cannot reset the Measured Rate", e);
                    }
                }
            }, sampleInterval, sampleInterval);

            isActive = true;
        }
    }
           

numOfRenewsPerMinThreshold这个值的更新会出现在下面

  1. 服务注册的时候
  2. 服务下线的时候
  3. 服务续约的时候
  4. eureka集群节点同步服务实例的时候

当eureka server 执行驱逐动作的时候会判断当开启了自我保护不会删除90s未续签的服务

if (!isLeaseExpirationEnabled()) {

logger.debug(“DS: lease expiration is currently disabled.”);

return;

}

当Renews (last min) 小于Renews threshold 页面会出现

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

这个就是自我保护,当然我们也可以关闭自我保护通过配置参数

eureka:

server:

enable-self-preservation: false

设计技巧

eureka server端为了效率设计了几个缓存

1、private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>(); 每30s更新一次数据来源 readWriteCacheMap

2、private final LoadingCache<Key, Value> readWriteCacheMap;

每60s失效一次再一次查询从 registry更新

eureka 服务上下线最长最长感知时间

服务上线后最长感知时间(90s)=ribbon更新服务列表(30s)+eureka客户端30s拉取一次服务列表+readWriteCacheMap更新到readOnlyCacheMap (30s一次)

服务下线后最长感知时间趋近(240s)= ribbon更新服务列表(30s)+eureka客户端30s拉取一次服务列表+readWriteCacheMap更新到readOnlyCacheMap (30s一次)+eureka server 最长驱逐间隔