EurekaClient源码分析

EurekaClient.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.netflix.discovery;

import com.google.inject.ImplementedBy;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.HealthCheckCallback;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.LookupService;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;

@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {
    Applications getApplicationsForARegion(@Nullable String var1);

    Applications getApplications(String var1);

    List<InstanceInfo> getInstancesByVipAddress(String var1, boolean var2);

    List<InstanceInfo> getInstancesByVipAddress(String var1, boolean var2, @Nullable String var3);

    List<InstanceInfo> getInstancesByVipAddressAndAppName(String var1, String var2, boolean var3);

    Set<String> getAllKnownRegions();

    InstanceStatus getInstanceRemoteStatus();

    /** @deprecated */
    @Deprecated
    List<String> getDiscoveryServiceUrls(String var1);

    /** @deprecated */
    @Deprecated
    List<String> getServiceUrlsFromConfig(String var1, boolean var2);

    /** @deprecated */
    @Deprecated
    List<String> getServiceUrlsFromDNS(String var1, boolean var2);

    /** @deprecated */
    @Deprecated
    void registerHealthCheckCallback(HealthCheckCallback var1);

    void registerHealthCheck(HealthCheckHandler var1);

    void registerEventListener(EurekaEventListener var1);

    boolean unregisterEventListener(EurekaEventListener var1);

    HealthCheckHandler getHealthCheckHandler();

    void shutdown();

    EurekaClientConfig getEurekaClientConfig();

    ApplicationInfoManager getApplicationInfoManager();
}

健康检测器和事件监听器

EurekaClient在LookupService上做了扩充

提供了更丰富的获取服务实例的方法我们看一下另外两个方法

public void registerHealthCheck(HealthCheckHandler healthCheckHandler)

向client注册健康检查处理器,client存在一个定时任务通过HealthCheckHandler检查当前client状态

当client状态发生变化时,将会触发新的注册事件,去更新eureka server的注册表中的服务实例信息

通过HealthCheckHandler 实现应用状态检测

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.netflix.appinfo;

import com.netflix.appinfo.InstanceInfo.InstanceStatus;

public interface HealthCheckHandler {
    InstanceStatus getStatus(InstanceStatus var1);
}

HealthCheckHandler的实现类org.springframework.cloud.netflix.eureka.EurekaHealthCheckHandler

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.netflix.eureka;

import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthContributor;
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthIndicator;
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthIndicator.Holder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;

public class EurekaHealthCheckHandler implements HealthCheckHandler, ApplicationContextAware, InitializingBean {
    private static final Map<Status, InstanceStatus> STATUS_MAPPING = new HashMap<Status, InstanceStatus>() {
        {
            this.put(Status.UNKNOWN, InstanceStatus.UNKNOWN);
            this.put(Status.OUT_OF_SERVICE, InstanceStatus.OUT_OF_SERVICE);
            this.put(Status.DOWN, InstanceStatus.DOWN);
            this.put(Status.UP, InstanceStatus.UP);
        }
    };
    private StatusAggregator statusAggregator;
    private ApplicationContext applicationContext;
    private Map<String, HealthIndicator> healthIndicators;
    /** @deprecated */
    @Deprecated
    private CompositeHealthIndicator healthIndicator;
    /** @deprecated */
    @Deprecated
    private HealthIndicatorRegistryFactory healthIndicatorRegistryFactory;
    /** @deprecated */
    @Deprecated
    private HealthAggregator healthAggregator;

    /** @deprecated */
    @Deprecated
    public EurekaHealthCheckHandler(HealthAggregator healthAggregator) {
        Assert.notNull(healthAggregator, "HealthAggregator must not be null");
        this.healthAggregator = healthAggregator;
        this.healthIndicatorRegistryFactory = new HealthIndicatorRegistryFactory();
        this.healthIndicator = new CompositeHealthIndicator(this.healthAggregator, new DefaultHealthIndicatorRegistry());
    }

    public EurekaHealthCheckHandler(StatusAggregator statusAggregator) {
        this.statusAggregator = statusAggregator;
        Assert.notNull(statusAggregator, "StatusAggregator must not be null");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void afterPropertiesSet() throws Exception {
        Map<String, HealthIndicator> healthIndicators = this.applicationContext.getBeansOfType(HealthIndicator.class);
        this.healthIndicators = new HashMap();
        if (this.statusAggregator != null) {
            this.populateHealthIndicators(healthIndicators);
        } else {
            this.createHealthIndicator(healthIndicators);
        }

    }

    /** @deprecated */
    @Deprecated
    void createHealthIndicator(Map<String, HealthIndicator> healthIndicators) {
        Iterator var2 = healthIndicators.entrySet().iterator();

        while(true) {
            while(var2.hasNext()) {
                Entry<String, HealthIndicator> entry = (Entry)var2.next();
                if (entry.getValue() instanceof DiscoveryCompositeHealthIndicator) {
                    DiscoveryCompositeHealthIndicator indicator = (DiscoveryCompositeHealthIndicator)entry.getValue();
                    Iterator var5 = indicator.getHealthIndicators().iterator();

                    while(var5.hasNext()) {
                        Holder holder = (Holder)var5.next();
                        if (!(holder.getDelegate() instanceof EurekaHealthIndicator)) {
                            this.healthIndicators.put(holder.getDelegate().getName(), holder);
                        }
                    }
                } else {
                    this.healthIndicators.put(entry.getKey(), entry.getValue());
                }
            }

            this.healthIndicator = new CompositeHealthIndicator(this.healthAggregator, this.healthIndicatorRegistryFactory.createHealthIndicatorRegistry(this.healthIndicators));
            return;
        }
    }

    void populateHealthIndicators(Map<String, HealthIndicator> healthIndicators) {
        Iterator var2 = healthIndicators.entrySet().iterator();

        while(var2.hasNext()) {
            Entry<String, HealthIndicator> entry = (Entry)var2.next();
            if (entry.getValue() instanceof DiscoveryCompositeHealthContributor) {
                DiscoveryCompositeHealthContributor indicator = (DiscoveryCompositeHealthContributor)entry.getValue();
                indicator.forEach((contributor) -> {
                    if (!(contributor.getContributor() instanceof EurekaHealthIndicator)) {
                        this.healthIndicators.put(contributor.getName(), (HealthIndicator)contributor.getContributor());
                    }

                });
            } else {
                this.healthIndicators.put(entry.getKey(), entry.getValue());
            }
        }

    }

    public InstanceStatus getStatus(InstanceStatus instanceStatus) {
        return this.getHealthStatus();
    }

    protected InstanceStatus getHealthStatus() {
        Status status;
        if (this.statusAggregator != null) {
            status = this.getStatus(this.statusAggregator);
        } else {
            status = this.getStatus(this.getHealthIndicator());
        }

        return this.mapToInstanceStatus(status);
    }

    /** @deprecated */
    @Deprecated
    private Status getStatus(CompositeHealthIndicator healthIndicator) {
        Status status = healthIndicator.health().getStatus();
        return status;
    }

    protected Status getStatus(StatusAggregator statusAggregator) {
        Set<Status> statusSet = (Set)this.healthIndicators.values().stream().map(HealthIndicator::health).map(Health::getStatus).collect(Collectors.toSet());
        Status status = statusAggregator.getAggregateStatus(statusSet);
        return status;
    }

    protected InstanceStatus mapToInstanceStatus(Status status) {
        return !STATUS_MAPPING.containsKey(status) ? InstanceStatus.UNKNOWN : (InstanceStatus)STATUS_MAPPING.get(status);
    }

    /** @deprecated */
    @Deprecated
    protected CompositeHealthIndicator getHealthIndicator() {
        return this.healthIndicator;
    }
}

看其构造函数

  public EurekaHealthCheckHandler(HealthAggregator healthAggregator) {
    Assert.notNull(healthAggregator, "HealthAggregator must not be null");
    this.healthAggregator = healthAggregator;
    this.healthIndicatorRegistryFactory = new HealthIndicatorRegistryFactory();
    this.healthIndicator = new CompositeHealthIndicator(this.healthAggregator, new DefaultHealthIndicatorRegistry());
}
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
private final CompositeHealthIndicator healthIndicator

此类事属于org.springframework.boot.actuate.health包下,可以得出,是通过actuator来实现对应用的检测的

注册事件监听器,当实例信息有变时,触发对应的处理事件

找到com.netflix.discovery.DiscoveryClient

源码位置:https://www.cnblogs.com/YC-L/p/14474683.html#155152858

注册函数

public void registerEventListener(EurekaEventListener eventListener) {
    this.eventListeners.add(eventListener);
}

public boolean unregisterEventListener(EurekaEventListener eventListener) {
    return this.eventListeners.remove(eventListener);
}

找到EurekaDiscoveryClientConfigServiceBootstrapConfiguration.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.netflix.eureka.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
import org.springframework.cloud.netflix.eureka.reactive.EurekaReactiveDiscoveryClientConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@ConditionalOnClass({ConfigServicePropertySourceLocator.class})
@ConditionalOnProperty(
    value = {"spring.cloud.config.discovery.enabled"},
    matchIfMissing = false
)
@Configuration(
    proxyBeanMethods = false
)
@Import({EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class, EurekaReactiveDiscoveryClientConfiguration.class, ReactiveCommonsClientAutoConfiguration.class})
public class EurekaDiscoveryClientConfigServiceBootstrapConfiguration {
    public EurekaDiscoveryClientConfigServiceBootstrapConfiguration() {
    }
}
@Import({EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class, EurekaReactiveDiscoveryClientConfiguration.class, ReactiveCommonsClientAutoConfiguration.class})

注解中有个类: EurekaClientAutoConfiguration

 public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance, @Autowired(required = false) HealthCheckHandler healthCheckHandler) {
            ApplicationInfoManager appManager;
            if (AopUtils.isAopProxy(manager)) {
                appManager = (ApplicationInfoManager)ProxyUtils.getTargetObject(manager);
            } else {
                appManager = manager;
            }

            CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager, config, this.optionalArgs, this.context);
            cloudEurekaClient.registerHealthCheck(healthCheckHandler);
            return cloudEurekaClient;
        }

CloudEurekaClient.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.netflix.eureka;

import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.util.ReflectionUtils;

public class CloudEurekaClient extends DiscoveryClient {
    private static final Log log = LogFactory.getLog(CloudEurekaClient.class);
    private final AtomicLong cacheRefreshedCount;
    private ApplicationEventPublisher publisher;
    private Field eurekaTransportField;
    private ApplicationInfoManager applicationInfoManager;
    private AtomicReference<EurekaHttpClient> eurekaHttpClient;

    public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, ApplicationEventPublisher publisher) {
        this(applicationInfoManager, config, (AbstractDiscoveryClientOptionalArgs)null, publisher);
    }

    public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args, ApplicationEventPublisher publisher) {
        super(applicationInfoManager, config, args);
        this.cacheRefreshedCount = new AtomicLong(0L);
        this.eurekaHttpClient = new AtomicReference();
        this.applicationInfoManager = applicationInfoManager;
        this.publisher = publisher;
        this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, "eurekaTransport");
        ReflectionUtils.makeAccessible(this.eurekaTransportField);
    }

    public ApplicationInfoManager getApplicationInfoManager() {
        return this.applicationInfoManager;
    }

    public void cancelOverrideStatus(InstanceInfo info) {
        this.getEurekaHttpClient().deleteStatusOverride(info.getAppName(), info.getId(), info);
    }

    public InstanceInfo getInstanceInfo(String appname, String instanceId) {
        EurekaHttpResponse<InstanceInfo> response = this.getEurekaHttpClient().getInstance(appname, instanceId);
        HttpStatus httpStatus = HttpStatus.valueOf(response.getStatusCode());
        return httpStatus.is2xxSuccessful() && response.getEntity() != null ? (InstanceInfo)response.getEntity() : null;
    }

    EurekaHttpClient getEurekaHttpClient() {
        if (this.eurekaHttpClient.get() == null) {
            try {
                Object eurekaTransport = this.eurekaTransportField.get(this);
                Field registrationClientField = ReflectionUtils.findField(eurekaTransport.getClass(), "registrationClient");
                ReflectionUtils.makeAccessible(registrationClientField);
                this.eurekaHttpClient.compareAndSet((Object)null, (EurekaHttpClient)registrationClientField.get(eurekaTransport));
            } catch (IllegalAccessException var3) {
                log.error("error getting EurekaHttpClient", var3);
            }
        }

        return (EurekaHttpClient)this.eurekaHttpClient.get();
    }

    public void setStatus(InstanceStatus newStatus, InstanceInfo info) {
        this.getEurekaHttpClient().statusUpdate(info.getAppName(), info.getId(), newStatus, info);
    }

    protected void onCacheRefreshed() {
        super.onCacheRefreshed();
        if (this.cacheRefreshedCount != null) {
            long newCount = this.cacheRefreshedCount.incrementAndGet();
            log.trace("onCacheRefreshed called with count: " + newCount);
            this.publisher.publishEvent(new HeartbeatEvent(this, newCount));
        }

    }
}

com.netflix.discovery.DiscoveryClient构造函数-不注册不拉取

DiscoveryClient的构造函数

public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, EndpointRandomizer randomizer) {}

此方法中依次执行以下步骤,贯穿了Eureka Client启动阶段的各项工作

  • 从eureka server中拉取注册表
  • 服务注册
  • 初始化发送心跳
  • 缓存刷新(定时拉取注册表信息)
  • 按需注册定时任务等

构造函数中查找

 if (config.shouldFetchRegistry()) {
            this.registryStalenessMonitor = new ThresholdLevelsMetric(this, "eurekaClient.registry.lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

        if (config.shouldRegisterWithEureka()) {
            this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, "eurekaClient.registration.lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

点其实现类EurekaClientConfigBean

shouldRegistry返回一个boolean值,fetchRegistry,默认是true,表示client从server拉取注册表信息

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.netflix.eureka;

import com.netflix.appinfo.EurekaAccept;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.EurekaTransportConfig;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.core.Ordered;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.StringUtils;

@ConfigurationProperties("eureka.client")
public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
    public static final String PREFIX = "eureka.client";
    public static final String DEFAULT_URL = "http://localhost:8761/eureka/";
    public static final String DEFAULT_ZONE = "defaultZone";
    private static final int MINUTES = 60;
    @Autowired(
        required = false
    )
    PropertyResolver propertyResolver;
    private boolean enabled = true;
    @NestedConfigurationProperty
    private EurekaTransportConfig transport = new CloudEurekaTransportConfig();
    private int registryFetchIntervalSeconds = 30;
    private int instanceInfoReplicationIntervalSeconds = 30;
    private int initialInstanceInfoReplicationIntervalSeconds = 40;
    private int eurekaServiceUrlPollIntervalSeconds = 300;
    private String proxyPort;
    private String proxyHost;
    private String proxyUserName;
    private String proxyPassword;
    private int eurekaServerReadTimeoutSeconds = 8;
    private int eurekaServerConnectTimeoutSeconds = 5;
    private String backupRegistryImpl;
    private int eurekaServerTotalConnections = 200;
    private int eurekaServerTotalConnectionsPerHost = 50;
    private String eurekaServerURLContext;
    private String eurekaServerPort;
    private String eurekaServerDNSName;
    private String region = "us-east-1";
    private int eurekaConnectionIdleTimeoutSeconds = 30;
    private String registryRefreshSingleVipAddress;
    private int heartbeatExecutorThreadPoolSize = 2;
    private int heartbeatExecutorExponentialBackOffBound = 10;
    private int cacheRefreshExecutorThreadPoolSize = 2;
    private int cacheRefreshExecutorExponentialBackOffBound = 10;
    private Map<String, String> serviceUrl = new HashMap();
    private boolean gZipContent;
    private boolean useDnsForFetchingServiceUrls;
    private boolean registerWithEureka;
    private boolean preferSameZoneEureka;
    private boolean logDeltaDiff;
    private boolean disableDelta;
    private String fetchRemoteRegionsRegistry;
    private Map<String, String> availabilityZones;
    private boolean filterOnlyUpInstances;
    private boolean fetchRegistry;
    private String dollarReplacement;
    private String escapeCharReplacement;
    private boolean allowRedirects;
    private boolean onDemandUpdateStatusChange;
    private String encoderName;
    private String decoderName;
    private String clientDataAccept;
    private boolean shouldUnregisterOnShutdown;
    private boolean shouldEnforceRegistrationAtInit;
    private int order;

    public EurekaClientConfigBean() {
        this.serviceUrl.put("defaultZone", "http://localhost:8761/eureka/");
        this.gZipContent = true;
        this.useDnsForFetchingServiceUrls = false;
        this.registerWithEureka = true;
        this.preferSameZoneEureka = true;
        this.availabilityZones = new HashMap();
        this.filterOnlyUpInstances = true;
        this.fetchRegistry = true;
        this.dollarReplacement = "_-";
        this.escapeCharReplacement = "__";
        this.allowRedirects = false;
        this.onDemandUpdateStatusChange = true;
        this.clientDataAccept = EurekaAccept.full.name();
        this.shouldUnregisterOnShutdown = true;
        this.shouldEnforceRegistrationAtInit = false;
        this.order = 0;
    }

    public boolean shouldGZipContent() {
        return this.gZipContent;
    }

    public boolean shouldUseDnsForFetchingServiceUrls() {
        return this.useDnsForFetchingServiceUrls;
    }

    public boolean shouldRegisterWithEureka() {
        return this.registerWithEureka;
    }

    public boolean shouldPreferSameZoneEureka() {
        return this.preferSameZoneEureka;
    }

    public boolean shouldLogDeltaDiff() {
        return this.logDeltaDiff;
    }

    public boolean shouldDisableDelta() {
        return this.disableDelta;
    }

    public boolean shouldUnregisterOnShutdown() {
        return this.shouldUnregisterOnShutdown;
    }

    public boolean shouldEnforceRegistrationAtInit() {
        return this.shouldEnforceRegistrationAtInit;
    }

    public String fetchRegistryForRemoteRegions() {
        return this.fetchRemoteRegionsRegistry;
    }

    public String[] getAvailabilityZones(String region) {
        String value = (String)this.availabilityZones.get(region);
        if (value == null) {
            value = "defaultZone";
        }

        return value.split(",");
    }

    public List<String> getEurekaServerServiceUrls(String myZone) {
        String serviceUrls = (String)this.serviceUrl.get(myZone);
        if (serviceUrls == null || serviceUrls.isEmpty()) {
            serviceUrls = (String)this.serviceUrl.get("defaultZone");
        }

        if (!StringUtils.isEmpty(serviceUrls)) {
            String[] serviceUrlsSplit = StringUtils.commaDelimitedListToStringArray(serviceUrls);
            List<String> eurekaServiceUrls = new ArrayList(serviceUrlsSplit.length);
            String[] var5 = serviceUrlsSplit;
            int var6 = serviceUrlsSplit.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                String eurekaServiceUrl = var5[var7];
                if (!this.endsWithSlash(eurekaServiceUrl)) {
                    eurekaServiceUrl = eurekaServiceUrl + "/";
                }

                eurekaServiceUrls.add(eurekaServiceUrl.trim());
            }

            return eurekaServiceUrls;
        } else {
            return new ArrayList();
        }
    }

    private boolean endsWithSlash(String url) {
        return url.endsWith("/");
    }

    public boolean shouldFilterOnlyUpInstances() {
        return this.filterOnlyUpInstances;
    }

    public boolean shouldFetchRegistry() {
        return this.fetchRegistry;
    }

    public boolean allowRedirects() {
        return this.allowRedirects;
    }

    public boolean shouldOnDemandUpdateStatusChange() {
        return this.onDemandUpdateStatusChange;
    }

    public String getExperimental(String name) {
        return this.propertyResolver != null ? (String)this.propertyResolver.getProperty("eureka.client.experimental." + name, String.class, (Object)null) : null;
    }

    public EurekaTransportConfig getTransportConfig() {
        return this.getTransport();
    }

    public PropertyResolver getPropertyResolver() {
        return this.propertyResolver;
    }

    public void setPropertyResolver(PropertyResolver propertyResolver) {
        this.propertyResolver = propertyResolver;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public EurekaTransportConfig getTransport() {
        return this.transport;
    }

    public void setTransport(EurekaTransportConfig transport) {
        this.transport = transport;
    }

    public int getRegistryFetchIntervalSeconds() {
        return this.registryFetchIntervalSeconds;
    }

    public void setRegistryFetchIntervalSeconds(int registryFetchIntervalSeconds) {
        this.registryFetchIntervalSeconds = registryFetchIntervalSeconds;
    }

    public int getInstanceInfoReplicationIntervalSeconds() {
        return this.instanceInfoReplicationIntervalSeconds;
    }

    public void setInstanceInfoReplicationIntervalSeconds(int instanceInfoReplicationIntervalSeconds) {
        this.instanceInfoReplicationIntervalSeconds = instanceInfoReplicationIntervalSeconds;
    }

    public int getInitialInstanceInfoReplicationIntervalSeconds() {
        return this.initialInstanceInfoReplicationIntervalSeconds;
    }

    public void setInitialInstanceInfoReplicationIntervalSeconds(int initialInstanceInfoReplicationIntervalSeconds) {
        this.initialInstanceInfoReplicationIntervalSeconds = initialInstanceInfoReplicationIntervalSeconds;
    }

    public int getEurekaServiceUrlPollIntervalSeconds() {
        return this.eurekaServiceUrlPollIntervalSeconds;
    }

    public void setEurekaServiceUrlPollIntervalSeconds(int eurekaServiceUrlPollIntervalSeconds) {
        this.eurekaServiceUrlPollIntervalSeconds = eurekaServiceUrlPollIntervalSeconds;
    }

    public String getProxyPort() {
        return this.proxyPort;
    }

    public void setProxyPort(String proxyPort) {
        this.proxyPort = proxyPort;
    }

    public String getProxyHost() {
        return this.proxyHost;
    }

    public void setProxyHost(String proxyHost) {
        this.proxyHost = proxyHost;
    }

    public String getProxyUserName() {
        return this.proxyUserName;
    }

    public void setProxyUserName(String proxyUserName) {
        this.proxyUserName = proxyUserName;
    }

    public String getProxyPassword() {
        return this.proxyPassword;
    }

    public void setProxyPassword(String proxyPassword) {
        this.proxyPassword = proxyPassword;
    }

    public int getEurekaServerReadTimeoutSeconds() {
        return this.eurekaServerReadTimeoutSeconds;
    }

    public void setEurekaServerReadTimeoutSeconds(int eurekaServerReadTimeoutSeconds) {
        this.eurekaServerReadTimeoutSeconds = eurekaServerReadTimeoutSeconds;
    }

    public int getEurekaServerConnectTimeoutSeconds() {
        return this.eurekaServerConnectTimeoutSeconds;
    }

    public void setEurekaServerConnectTimeoutSeconds(int eurekaServerConnectTimeoutSeconds) {
        this.eurekaServerConnectTimeoutSeconds = eurekaServerConnectTimeoutSeconds;
    }

    public String getBackupRegistryImpl() {
        return this.backupRegistryImpl;
    }

    public void setBackupRegistryImpl(String backupRegistryImpl) {
        this.backupRegistryImpl = backupRegistryImpl;
    }

    public int getEurekaServerTotalConnections() {
        return this.eurekaServerTotalConnections;
    }

    public void setEurekaServerTotalConnections(int eurekaServerTotalConnections) {
        this.eurekaServerTotalConnections = eurekaServerTotalConnections;
    }

    public int getEurekaServerTotalConnectionsPerHost() {
        return this.eurekaServerTotalConnectionsPerHost;
    }

    public void setEurekaServerTotalConnectionsPerHost(int eurekaServerTotalConnectionsPerHost) {
        this.eurekaServerTotalConnectionsPerHost = eurekaServerTotalConnectionsPerHost;
    }

    public String getEurekaServerURLContext() {
        return this.eurekaServerURLContext;
    }

    public void setEurekaServerURLContext(String eurekaServerURLContext) {
        this.eurekaServerURLContext = eurekaServerURLContext;
    }

    public String getEurekaServerPort() {
        return this.eurekaServerPort;
    }

    public void setEurekaServerPort(String eurekaServerPort) {
        this.eurekaServerPort = eurekaServerPort;
    }

    public String getEurekaServerDNSName() {
        return this.eurekaServerDNSName;
    }

    public void setEurekaServerDNSName(String eurekaServerDNSName) {
        this.eurekaServerDNSName = eurekaServerDNSName;
    }

    public String getRegion() {
        return this.region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public int getEurekaConnectionIdleTimeoutSeconds() {
        return this.eurekaConnectionIdleTimeoutSeconds;
    }

    public void setEurekaConnectionIdleTimeoutSeconds(int eurekaConnectionIdleTimeoutSeconds) {
        this.eurekaConnectionIdleTimeoutSeconds = eurekaConnectionIdleTimeoutSeconds;
    }

    public String getRegistryRefreshSingleVipAddress() {
        return this.registryRefreshSingleVipAddress;
    }

    public void setRegistryRefreshSingleVipAddress(String registryRefreshSingleVipAddress) {
        this.registryRefreshSingleVipAddress = registryRefreshSingleVipAddress;
    }

    public int getHeartbeatExecutorThreadPoolSize() {
        return this.heartbeatExecutorThreadPoolSize;
    }

    public void setHeartbeatExecutorThreadPoolSize(int heartbeatExecutorThreadPoolSize) {
        this.heartbeatExecutorThreadPoolSize = heartbeatExecutorThreadPoolSize;
    }

    public int getHeartbeatExecutorExponentialBackOffBound() {
        return this.heartbeatExecutorExponentialBackOffBound;
    }

    public void setHeartbeatExecutorExponentialBackOffBound(int heartbeatExecutorExponentialBackOffBound) {
        this.heartbeatExecutorExponentialBackOffBound = heartbeatExecutorExponentialBackOffBound;
    }

    public int getCacheRefreshExecutorThreadPoolSize() {
        return this.cacheRefreshExecutorThreadPoolSize;
    }

    public void setCacheRefreshExecutorThreadPoolSize(int cacheRefreshExecutorThreadPoolSize) {
        this.cacheRefreshExecutorThreadPoolSize = cacheRefreshExecutorThreadPoolSize;
    }

    public int getCacheRefreshExecutorExponentialBackOffBound() {
        return this.cacheRefreshExecutorExponentialBackOffBound;
    }

    public void setCacheRefreshExecutorExponentialBackOffBound(int cacheRefreshExecutorExponentialBackOffBound) {
        this.cacheRefreshExecutorExponentialBackOffBound = cacheRefreshExecutorExponentialBackOffBound;
    }

    public Map<String, String> getServiceUrl() {
        return this.serviceUrl;
    }

    public void setServiceUrl(Map<String, String> serviceUrl) {
        this.serviceUrl = serviceUrl;
    }

    public boolean isgZipContent() {
        return this.gZipContent;
    }

    public void setgZipContent(boolean gZipContent) {
        this.gZipContent = gZipContent;
    }

    public boolean isUseDnsForFetchingServiceUrls() {
        return this.useDnsForFetchingServiceUrls;
    }

    public void setUseDnsForFetchingServiceUrls(boolean useDnsForFetchingServiceUrls) {
        this.useDnsForFetchingServiceUrls = useDnsForFetchingServiceUrls;
    }

    public boolean isRegisterWithEureka() {
        return this.registerWithEureka;
    }

    public void setRegisterWithEureka(boolean registerWithEureka) {
        this.registerWithEureka = registerWithEureka;
    }

    public boolean isPreferSameZoneEureka() {
        return this.preferSameZoneEureka;
    }

    public void setPreferSameZoneEureka(boolean preferSameZoneEureka) {
        this.preferSameZoneEureka = preferSameZoneEureka;
    }

    public boolean isLogDeltaDiff() {
        return this.logDeltaDiff;
    }

    public void setLogDeltaDiff(boolean logDeltaDiff) {
        this.logDeltaDiff = logDeltaDiff;
    }

    public boolean isDisableDelta() {
        return this.disableDelta;
    }

    public void setDisableDelta(boolean disableDelta) {
        this.disableDelta = disableDelta;
    }

    public String getFetchRemoteRegionsRegistry() {
        return this.fetchRemoteRegionsRegistry;
    }

    public void setFetchRemoteRegionsRegistry(String fetchRemoteRegionsRegistry) {
        this.fetchRemoteRegionsRegistry = fetchRemoteRegionsRegistry;
    }

    public Map<String, String> getAvailabilityZones() {
        return this.availabilityZones;
    }

    public void setAvailabilityZones(Map<String, String> availabilityZones) {
        this.availabilityZones = availabilityZones;
    }

    public boolean isFilterOnlyUpInstances() {
        return this.filterOnlyUpInstances;
    }

    public void setFilterOnlyUpInstances(boolean filterOnlyUpInstances) {
        this.filterOnlyUpInstances = filterOnlyUpInstances;
    }

    public boolean isFetchRegistry() {
        return this.fetchRegistry;
    }

    public void setFetchRegistry(boolean fetchRegistry) {
        this.fetchRegistry = fetchRegistry;
    }

    public String getDollarReplacement() {
        return this.dollarReplacement;
    }

    public void setDollarReplacement(String dollarReplacement) {
        this.dollarReplacement = dollarReplacement;
    }

    public String getEscapeCharReplacement() {
        return this.escapeCharReplacement;
    }

    public void setEscapeCharReplacement(String escapeCharReplacement) {
        this.escapeCharReplacement = escapeCharReplacement;
    }

    public boolean isAllowRedirects() {
        return this.allowRedirects;
    }

    public void setAllowRedirects(boolean allowRedirects) {
        this.allowRedirects = allowRedirects;
    }

    public boolean isOnDemandUpdateStatusChange() {
        return this.onDemandUpdateStatusChange;
    }

    public void setOnDemandUpdateStatusChange(boolean onDemandUpdateStatusChange) {
        this.onDemandUpdateStatusChange = onDemandUpdateStatusChange;
    }

    public String getEncoderName() {
        return this.encoderName;
    }

    public void setEncoderName(String encoderName) {
        this.encoderName = encoderName;
    }

    public String getDecoderName() {
        return this.decoderName;
    }

    public void setDecoderName(String decoderName) {
        this.decoderName = decoderName;
    }

    public String getClientDataAccept() {
        return this.clientDataAccept;
    }

    public void setClientDataAccept(String clientDataAccept) {
        this.clientDataAccept = clientDataAccept;
    }

    public boolean isShouldUnregisterOnShutdown() {
        return this.shouldUnregisterOnShutdown;
    }

    public void setShouldUnregisterOnShutdown(boolean shouldUnregisterOnShutdown) {
        this.shouldUnregisterOnShutdown = shouldUnregisterOnShutdown;
    }

    public boolean isShouldEnforceRegistrationAtInit() {
        return this.shouldEnforceRegistrationAtInit;
    }

    public void setShouldEnforceRegistrationAtInit(boolean shouldEnforceRegistrationAtInit) {
        this.shouldEnforceRegistrationAtInit = shouldEnforceRegistrationAtInit;
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else if (o != null && this.getClass() == o.getClass()) {
            EurekaClientConfigBean that = (EurekaClientConfigBean)o;
            return Objects.equals(this.propertyResolver, that.propertyResolver) && this.enabled == that.enabled && Objects.equals(this.transport, that.transport) && this.registryFetchIntervalSeconds == that.registryFetchIntervalSeconds && this.instanceInfoReplicationIntervalSeconds == that.instanceInfoReplicationIntervalSeconds && this.initialInstanceInfoReplicationIntervalSeconds == that.initialInstanceInfoReplicationIntervalSeconds && this.eurekaServiceUrlPollIntervalSeconds == that.eurekaServiceUrlPollIntervalSeconds && this.eurekaServerReadTimeoutSeconds == that.eurekaServerReadTimeoutSeconds && this.eurekaServerConnectTimeoutSeconds == that.eurekaServerConnectTimeoutSeconds && this.eurekaServerTotalConnections == that.eurekaServerTotalConnections && this.eurekaServerTotalConnectionsPerHost == that.eurekaServerTotalConnectionsPerHost && this.eurekaConnectionIdleTimeoutSeconds == that.eurekaConnectionIdleTimeoutSeconds && this.heartbeatExecutorThreadPoolSize == that.heartbeatExecutorThreadPoolSize && this.heartbeatExecutorExponentialBackOffBound == that.heartbeatExecutorExponentialBackOffBound && this.cacheRefreshExecutorThreadPoolSize == that.cacheRefreshExecutorThreadPoolSize && this.cacheRefreshExecutorExponentialBackOffBound == that.cacheRefreshExecutorExponentialBackOffBound && this.gZipContent == that.gZipContent && this.useDnsForFetchingServiceUrls == that.useDnsForFetchingServiceUrls && this.registerWithEureka == that.registerWithEureka && this.preferSameZoneEureka == that.preferSameZoneEureka && this.logDeltaDiff == that.logDeltaDiff && this.disableDelta == that.disableDelta && this.filterOnlyUpInstances == that.filterOnlyUpInstances && this.fetchRegistry == that.fetchRegistry && this.allowRedirects == that.allowRedirects && this.onDemandUpdateStatusChange == that.onDemandUpdateStatusChange && this.shouldUnregisterOnShutdown == that.shouldUnregisterOnShutdown && this.shouldEnforceRegistrationAtInit == that.shouldEnforceRegistrationAtInit && Objects.equals(this.proxyPort, that.proxyPort) && Objects.equals(this.proxyHost, that.proxyHost) && Objects.equals(this.proxyUserName, that.proxyUserName) && Objects.equals(this.proxyPassword, that.proxyPassword) && Objects.equals(this.backupRegistryImpl, that.backupRegistryImpl) && Objects.equals(this.eurekaServerURLContext, that.eurekaServerURLContext) && Objects.equals(this.eurekaServerPort, that.eurekaServerPort) && Objects.equals(this.eurekaServerDNSName, that.eurekaServerDNSName) && Objects.equals(this.region, that.region) && Objects.equals(this.registryRefreshSingleVipAddress, that.registryRefreshSingleVipAddress) && Objects.equals(this.serviceUrl, that.serviceUrl) && Objects.equals(this.fetchRemoteRegionsRegistry, that.fetchRemoteRegionsRegistry) && Objects.equals(this.availabilityZones, that.availabilityZones) && Objects.equals(this.dollarReplacement, that.dollarReplacement) && Objects.equals(this.escapeCharReplacement, that.escapeCharReplacement) && Objects.equals(this.encoderName, that.encoderName) && Objects.equals(this.decoderName, that.decoderName) && Objects.equals(this.clientDataAccept, that.clientDataAccept) && Objects.equals(this.order, that.order);
        } else {
            return false;
        }
    }

    public int hashCode() {
        return Objects.hash(new Object[]{this.propertyResolver, this.enabled, this.transport, this.registryFetchIntervalSeconds, this.instanceInfoReplicationIntervalSeconds, this.initialInstanceInfoReplicationIntervalSeconds, this.eurekaServiceUrlPollIntervalSeconds, this.proxyPort, this.proxyHost, this.proxyUserName, this.proxyPassword, this.eurekaServerReadTimeoutSeconds, this.eurekaServerConnectTimeoutSeconds, this.backupRegistryImpl, this.eurekaServerTotalConnections, this.eurekaServerTotalConnectionsPerHost, this.eurekaServerURLContext, this.eurekaServerPort, this.eurekaServerDNSName, this.region, this.eurekaConnectionIdleTimeoutSeconds, this.registryRefreshSingleVipAddress, this.heartbeatExecutorThreadPoolSize, this.heartbeatExecutorExponentialBackOffBound, this.cacheRefreshExecutorThreadPoolSize, this.cacheRefreshExecutorExponentialBackOffBound, this.serviceUrl, this.gZipContent, this.useDnsForFetchingServiceUrls, this.registerWithEureka, this.preferSameZoneEureka, this.logDeltaDiff, this.disableDelta, this.fetchRemoteRegionsRegistry, this.availabilityZones, this.filterOnlyUpInstances, this.fetchRegistry, this.dollarReplacement, this.escapeCharReplacement, this.allowRedirects, this.onDemandUpdateStatusChange, this.encoderName, this.decoderName, this.clientDataAccept, this.shouldUnregisterOnShutdown, this.shouldEnforceRegistrationAtInit, this.order});
    }

    public String toString() {
        return "EurekaClientConfigBean{" + "propertyResolver=" + this.propertyResolver + ", " + "enabled=" + this.enabled + ", " + "transport=" + this.transport + ", " + "registryFetchIntervalSeconds=" + this.registryFetchIntervalSeconds + ", " + "instanceInfoReplicationIntervalSeconds=" + this.instanceInfoReplicationIntervalSeconds + ", " + "initialInstanceInfoReplicationIntervalSeconds=" + this.initialInstanceInfoReplicationIntervalSeconds + ", " + "eurekaServiceUrlPollIntervalSeconds=" + this.eurekaServiceUrlPollIntervalSeconds + ", " + "proxyPort='" + this.proxyPort + "', " + "proxyHost='" + this.proxyHost + "', " + "proxyUserName='" + this.proxyUserName + "', " + "proxyPassword='" + this.proxyPassword + "', " + "eurekaServerReadTimeoutSeconds=" + this.eurekaServerReadTimeoutSeconds + ", " + "eurekaServerConnectTimeoutSeconds=" + this.eurekaServerConnectTimeoutSeconds + ", " + "backupRegistryImpl='" + this.backupRegistryImpl + "', " + "eurekaServerTotalConnections=" + this.eurekaServerTotalConnections + ", " + "eurekaServerTotalConnectionsPerHost=" + this.eurekaServerTotalConnectionsPerHost + ", " + "eurekaServerURLContext='" + this.eurekaServerURLContext + "', " + "eurekaServerPort='" + this.eurekaServerPort + "', " + "eurekaServerDNSName='" + this.eurekaServerDNSName + "', " + "region='" + this.region + "', " + "eurekaConnectionIdleTimeoutSeconds=" + this.eurekaConnectionIdleTimeoutSeconds + ", " + "registryRefreshSingleVipAddress='" + this.registryRefreshSingleVipAddress + "', " + "heartbeatExecutorThreadPoolSize=" + this.heartbeatExecutorThreadPoolSize + ", " + "heartbeatExecutorExponentialBackOffBound=" + this.heartbeatExecutorExponentialBackOffBound + ", " + "cacheRefreshExecutorThreadPoolSize=" + this.cacheRefreshExecutorThreadPoolSize + ", " + "cacheRefreshExecutorExponentialBackOffBound=" + this.cacheRefreshExecutorExponentialBackOffBound + ", " + "serviceUrl=" + this.serviceUrl + ", " + "gZipContent=" + this.gZipContent + ", " + "useDnsForFetchingServiceUrls=" + this.useDnsForFetchingServiceUrls + ", " + "registerWithEureka=" + this.registerWithEureka + ", " + "preferSameZoneEureka=" + this.preferSameZoneEureka + ", " + "logDeltaDiff=" + this.logDeltaDiff + ", " + "disableDelta=" + this.disableDelta + ", " + "fetchRemoteRegionsRegistry='" + this.fetchRemoteRegionsRegistry + "', " + "availabilityZones=" + this.availabilityZones + ", " + "filterOnlyUpInstances=" + this.filterOnlyUpInstances + ", " + "fetchRegistry=" + this.fetchRegistry + ", " + "dollarReplacement='" + this.dollarReplacement + "', " + "escapeCharReplacement='" + this.escapeCharReplacement + "', " + "allowRedirects=" + this.allowRedirects + ", " + "onDemandUpdateStatusChange=" + this.onDemandUpdateStatusChange + ", " + "encoderName='" + this.encoderName + "', " + "decoderName='" + this.decoderName + "', " + "clientDataAccept='" + this.clientDataAccept + "', " + "shouldUnregisterOnShutdown='" + this.shouldUnregisterOnShutdown + "', " + "shouldEnforceRegistrationAtInit='" + this.shouldEnforceRegistrationAtInit + "', " + "order='" + this.order + "'}";
    }
}

继续看DiscoveryClient中的源码

if (config.shouldRegisterWithEureka()) {
    this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, "eurekaClient.registration.lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
    this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}

点其实现类EurekaClientConfigBean

shouldRegisterWithEureka 返回一个boolean值 registerWithEureka,默认为true,表示client将注册到server

继续看DiscoveryClient源码

if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
    logger.info("Client configured to neither register nor query for data.");
    this.scheduler = null;
    this.heartbeatExecutor = null;
    this.cacheRefreshExecutor = null;
    this.eurekaTransport = null;
    this.instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), this.clientConfig.getRegion());
    DiscoveryManager.getInstance().setDiscoveryClient(this);
    DiscoveryManager.getInstance().setEurekaClientConfig(config);
    this.initTimestampMs = System.currentTimeMillis();
    logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}", this.initTimestampMs, this.getApplications().size());
} else {}

如果shouldRegisterWithEureka和shouldFetchRegistry两个都为false,则直接返回,构造方法执行结束,既不服务注册,也不服务发现

com.netflix.discovery.DiscoveryClient构造函数-两个定时任务

顺着上段代码中的else往下看

try {
    this.scheduler = Executors.newScheduledThreadPool(2, (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-%d").setDaemon(true).build());
    this.heartbeatExecutor = new ThreadPoolExecutor(1, this.clientConfig.getHeartbeatExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-HeartbeatExecutor-%d").setDaemon(true).build());
    this.cacheRefreshExecutor = new ThreadPoolExecutor(1, this.clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d").setDaemon(true).build());
    this.eurekaTransport = new DiscoveryClient.EurekaTransport();
    this.scheduleServerEndpointTask(this.eurekaTransport, args);
    Object azToRegionMapper;
    if (this.clientConfig.shouldUseDnsForFetchingServiceUrls()) {
        azToRegionMapper = new DNSBasedAzToRegionMapper(this.clientConfig);
    } else {
        azToRegionMapper = new PropertyBasedAzToRegionMapper(this.clientConfig);
    }

    if (null != this.remoteRegionsToFetch.get()) {
        ((AzToRegionMapper)azToRegionMapper).setRegionsToFetch(((String)this.remoteRegionsToFetch.get()).split(","));
    }

    this.instanceRegionChecker = new InstanceRegionChecker((AzToRegionMapper)azToRegionMapper, this.clientConfig.getRegion());
} catch (Throwable var10) {
    throw new RuntimeException("Failed to initialize DiscoveryClient!", var10);
}
  • scheduler定义了一个基于线程池的定时器线程池,大小为2
  • heartbeatExecutor:用于发送心跳
  • cacheRefreshExecutor:用于刷新缓存
  • eurekaTransport,它是eureka Client和eureka server进行http交互jersey客户端

com.netflix.discovery.DiscoveryClient构造函数-client和server交互的Jersey客户端

com.netflix.discovery.DiscoveryClient构造函数-拉取注册信息

if (this.clientConfig.shouldFetchRegistry() && !this.fetchRegistry(false)) {
    this.fetchRegistryFromBackup();
}

符合上面这个条件,执行fetchRegistry,从eureka server拉取注册表信息,缓存到本地,减少server交互

com.netflix.discovery.DiscoveryClient构造函数-服务注册

if (this.clientConfig.shouldRegisterWithEureka() && this.clientConfig.shouldEnforceRegistrationAtInit()) {
    try {
        if (!this.register()) {
            throw new IllegalStateException("Registration error at startup. Invalid server response.");
        }
    } catch (Throwable var9) {
        logger.error("Registration error at startup: {}", var9.getMessage());
        throw new IllegalStateException(var9);
    }
}

注册失败会抛出异常

com.netflix.discovery.DiscoveryClient构造函数-启动定时任务

initScheduledTasks

private void initScheduledTasks() {
    int renewalIntervalInSecs;
    int expBackOffBound;
    if (this.clientConfig.shouldFetchRegistry()) {
        renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds();
        expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
        this.cacheRefreshTask = new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread());
        this.scheduler.schedule(this.cacheRefreshTask, (long)renewalIntervalInSecs, TimeUnit.SECONDS);
    }

    if (this.clientConfig.shouldRegisterWithEureka()) {
        renewalIntervalInSecs = this.instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
        expBackOffBound = this.clientConfig.getHeartbeatExecutorExponentialBackOffBound();
        logger.info("Starting heartbeat executor: renew interval is: {}", renewalIntervalInSecs);
        this.heartbeatTask = new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread());
        this.scheduler.schedule(this.heartbeatTask, (long)renewalIntervalInSecs, TimeUnit.SECONDS);
        this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);
        this.statusChangeListener = new StatusChangeListener() {
            public String getId() {
                return "statusChangeListener";
            }

            public void notify(StatusChangeEvent statusChangeEvent) {
                if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
                    DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
                } else {
                    DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
                }

                DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
            }
        };
        if (this.clientConfig.shouldOnDemandUpdateStatusChange()) {
            this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);
        }

        this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
    } else {
        logger.info("Not registering with Eureka server per configuration");
    }

}
  • cacheRefreshTask
  • heartbeatTask
  • instanceInfoReplicator

方法中有statusChangeListener,按需注册是一个事件StatusChangeEvent,状态改变,向server注册

com.netflix.discovery.DiscoveryClient构造函数-总结

  • 初始化配置
  • 从注册表拉取信息
  • 向server注册自己
  • 初始化定时任务

拉取注册表信息详解

查看上面的fetchRegistry(false)

private boolean fetchRegistry(boolean forceFullRegistryFetch) {
    Stopwatch tracer = this.FETCH_REGISTRY_TIMER.start();

    label122: {
        boolean var4;
        try {
            Applications applications = this.getApplications();
            if (!this.clientConfig.shouldDisableDelta() && Strings.isNullOrEmpty(this.clientConfig.getRegistryRefreshSingleVipAddress()) && !forceFullRegistryFetch && applications != null && applications.getRegisteredApplications().size() != 0 && applications.getVersion() != -1L) {
                this.getAndUpdateDelta(applications);
            } else {
                logger.info("Disable delta property : {}", this.clientConfig.shouldDisableDelta());
                logger.info("Single vip registry refresh property : {}", this.clientConfig.getRegistryRefreshSingleVipAddress());
                logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
                logger.info("Application is null : {}", applications == null);
                logger.info("Registered Applications size is zero : {}", applications.getRegisteredApplications().size() == 0);
                logger.info("Application version is -1: {}", applications.getVersion() == -1L);
                this.getAndStoreFullRegistry();
            }

            applications.setAppsHashCode(applications.getReconcileHashCode());
            this.logTotalInstances();
            break label122;
        } catch (Throwable var8) {
            logger.error("DiscoveryClient_{} - was unable to refresh its cache! status = {}", new Object[]{this.appPathIdentifier, var8.getMessage(), var8});
            var4 = false;
        } finally {
            if (tracer != null) {
                tracer.stop();
            }

        }

        return var4;
    }

    this.onCacheRefreshed();
    this.updateInstanceRemoteStatus();
    return true;
}

如果增量式拉取被禁止或第一次拉取注册表

则进行全量拉取:getAndStoreFullRegistry()

否则进行增量拉取注册表信息getAndUpdateDelta(applications)

一般情况,在Eureka client第一次启动,会进行全量拉取。之后的拉取都尽量尝试只进行增量拉取

拉取服务注册表:

  • 全量拉取:getAndStoreFullRegistry()
  • 增量拉取:getAndUpdateDelta(applications)

全量拉取

getAndStoreFullRegistry

private void getAndStoreFullRegistry() throws Throwable {
    long currentUpdateGeneration = this.fetchRegistryGeneration.get();
    logger.info("Getting all instance registry info from the eureka server");
    Applications apps = null;
    EurekaHttpResponse<Applications> httpResponse = this.clientConfig.getRegistryRefreshSingleVipAddress() == null ? this.eurekaTransport.queryClient.getApplications((String[])this.remoteRegionsRef.get()) : this.eurekaTransport.queryClient.getVip(this.clientConfig.getRegistryRefreshSingleVipAddress(), (String[])this.remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        apps = (Applications)httpResponse.getEntity();
    }

    logger.info("The response status is {}", httpResponse.getStatusCode());
    if (apps == null) {
        logger.error("The application is null for some reason. Not storing this information");
    } else if (this.fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1L)) {
        this.localRegionApps.set(this.filterAndShuffle(apps));
        logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
    } else {
        logger.warn("Not updating applications as another thread is updating it already");
    }

}

有个方法

this.eurekaTransport.queryClient.getApplications((String[])this.remoteRegionsRef.get())

其实现类是AbstractJerseyEurekaHttpClient

  • debug出webResource地址为:http://root:root@eureka-7900:7900/eureka/apps/
  • 此端点用于获取server中所有的注册表信息
  • getAndStoreFullRegistry()可能被多个线程同时调用,导致新拉取的注册表被旧的覆盖(如果新拉取的动作设置apps阻塞的情况下)
  • 此时用了AutomicLong来进行版本管理,如果更新时版本不一致,不保存apps

通过这个判断

fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)

如果版本一致,并设置新版本(+1), 接着执行localRegionApps.set(this.filterAndShuffle(apps));

过滤并洗牌apps

点开this.filterAndShuffle(apps)实现

  • 继续点apps.shuffleAndIndexInstances
  • 继续点shuffleInstances
  • 继续点application.shuffleAndStoreInstances
  • 继续点_shuffleAndStoreInstances
  • 发现if (filterUpInstances && InstanceStatus.UP != instanceInfo.getStatus())

只保留状态为UP的服务

增量拉取

回到刚才的fetchRegistry方法中,getAndUpdateDelta,增量拉取

  • 通过getDelta方法,看到实际拉取的地址是:apps/delta,如果获取到的delta为空,则全量拉取
  • 通常来讲是3分钟之内注册表的信息变化(在server端判断),获取到delta后,会更新本地注册表
  • 增量式拉取是为了维护client和server端 注册表的一致性,防止本地数据过久,而失效,采用增量式拉取的方式,减少了client和server的通信量
  • client有一个注册表缓存刷新定时器,专门负责维护两者之间的信息同步,但是当增量出现意外时,定时器将执行,全量拉取以更新本地缓存信息

更新本地注册表方法updateDelta,有一个细节

if (ActionType.ADDED.equals(instance.getActionType()))

if (ActionType.ADDED.equals(instance.getActionType())) {
    existingApp = applications.getRegisteredApplications(instance.getAppName());
    if (existingApp == null) {
        applications.addApplication(app);
    }

    logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion);
    applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.MODIFIED.equals(instance.getActionType())) {
    existingApp = applications.getRegisteredApplications(instance.getAppName());
    if (existingApp == null) {
        applications.addApplication(app);
    }

    logger.debug("Modified instance {} to the existing apps ", instance.getId());
    applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.DELETED.equals(instance.getActionType())) {
    existingApp = applications.getRegisteredApplications(instance.getAppName());
    if (existingApp != null) {
        logger.debug("Deleted instance {} to the existing apps ", instance.getId());
        existingApp.removeInstance(instance);
        if (existingApp.getInstancesAsIsFromEureka().isEmpty()) {
            applications.removeApplication(existingApp);
        }
    }
}

ActionType

public static enum ActionType {
    ADDED,
    MODIFIED,
    DELETED;

    private ActionType() {
    }
}

在InstanceInfo instance中有一个instance.getActionType()

  • ADDED和MODIFIED状态的将更新本地注册表applications.addApplication
  • DELETED将从本地剔除掉existingApp.removeInstance(instance)

服务注册

好了拉取完eureka server中的注册表了,接着进行服务注册

回到DiscoveryClient构造函数。

拉取fetchRegistry完后进行register注册

boolean register() throws Throwable {
    logger.info("DiscoveryClient_{}: registering service...", this.appPathIdentifier);

    EurekaHttpResponse httpResponse;
    try {
        httpResponse = this.eurekaTransport.registrationClient.register(this.instanceInfo);
    } catch (Exception var3) {
        logger.warn("DiscoveryClient_{} - registration failed {}", new Object[]{this.appPathIdentifier, var3.getMessage(), var3});
        throw var3;
    }

    if (logger.isInfoEnabled()) {
        logger.info("DiscoveryClient_{} - registration status: {}", this.appPathIdentifier, httpResponse.getStatusCode());
    }

    return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}
  • 由于构造函数开始时已经将服务实例元数据封装好了instanceInfo,所以此处之间向server发送instanceInfo
  • 通过方法httpResponse = eurekaTransport.registrationClient.register(instanceInfo)
  • 看到String urlPath = "apps/" + info.getAppName()
  • httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode()
  • 204状态码,则注册成功

初始化3个定时任务

看注释初始化3个定时任务

  • client会定时向server发送心跳,维持自己服务租约的有效性,用心跳定时任务实现
  • server中会有不同的服务实例注册进来,一进一出,就需要数据的同步,所以client需要定时从server拉取注册表信息,用缓存定时任务实现
  • client如果有变化,也会及时更新server中自己的信息,用按需注册定时任务实现

就是这三个定时任务

initScheduledTasks()方法中,

  • clientConfig.shouldFetchRegistry(),从server拉取注册表信息
  • int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds() 拉取的时间间隔,eureka.client.registryFetchIntervalSeconds进行设置
  • int renewalIntervalInSecs = nstanceInfo.getLeaseInfo().getRenewalIntervalInSecs() 心跳定时器,默认30秒

new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread()

看到HeartbeatThread线程

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

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

    }
}

里面是renew()方法

scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() )

看到CacheRefreshThread

class CacheRefreshThread implements Runnable {
    CacheRefreshThread() {
    }

    public void run() {
        DiscoveryClient.this.refreshRegistry();
    }
}

是用的refreshRegistry,进去发现fetchRegistry

回到原来讲过的地方

 boolean renew() {
    try {
        EurekaHttpResponse<InstanceInfo> httpResponse = this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, (InstanceStatus)null);
        logger.debug("DiscoveryClient_{} - Heartbeat status: {}", this.appPathIdentifier, httpResponse.getStatusCode());
        if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
            this.REREGISTER_COUNTER.increment();
            logger.info("DiscoveryClient_{} - Re-registering apps/{}", this.appPathIdentifier, this.instanceInfo.getAppName());
            long timestamp = this.instanceInfo.setIsDirtyWithTime();
            boolean success = this.register();
            if (success) {
                this.instanceInfo.unsetIsDirty(timestamp);
            }

            return success;
        } else {
            return httpResponse.getStatusCode() == Status.OK.getStatusCode();
        }
    } catch (Throwable var5) {
        logger.error("DiscoveryClient_{} - was unable to send heartbeat!", this.appPathIdentifier, var5);
        return false;
    }
}

看到如果遇到404,server没有此实例,则重新发起注册

如果续约成功返回 200. 点sendHeartBeat

找到接口EurekaHttpClient的实现类AbstractJerseyEurekaHttpClient

 public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {
    String urlPath = "apps/" + appName + '/' + id;
    ClientResponse response = null;

    EurekaHttpResponse var10;
    try {
        WebResource webResource = this.jerseyClient.resource(this.serviceUrl).path(urlPath).queryParam("status", info.getStatus().toString()).queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
        if (overriddenStatus != null) {
            webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name());
        }

        Builder requestBuilder = webResource.getRequestBuilder();
        this.addExtraHeaders(requestBuilder);
        response = (ClientResponse)requestBuilder.put(ClientResponse.class);
        EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = EurekaHttpResponse.anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response));
        if (response.hasEntity() && !"html".equals(response.getType().getSubtype())) {
            eurekaResponseBuilder.entity(response.getEntity(InstanceInfo.class));
        }

        var10 = eurekaResponseBuilder.build();
    } finally {
        if (logger.isDebugEnabled()) {
            logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", new Object[]{this.serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()});
        }

        if (response != null) {
            response.close();
        }

    }

    return var10;
}

进去String urlPath = "apps/" + appName + '/' + id;

还有一个定时任务,按需注册

当instanceinfo和status发生变化时,需要向server同步,去更新自己在server中的实例信息

保证server注册表中服务实例信息的有效和可用

this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);
this.statusChangeListener = new StatusChangeListener() {
    public String getId() {
        return "statusChangeListener";
    }

    public void notify(StatusChangeEvent statusChangeEvent) {
        if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
            DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
        } else {
            DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
        }

        DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
    }
};
if (this.clientConfig.shouldOnDemandUpdateStatusChange()) {
    this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);
}
this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());

此定时任务有2个部分        

1:定时刷新服务实例信息和检查应用状态的变化,在服务实例信息发生改变的情况下向server重新发起注册

查看InstanceInfoReplicator

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.netflix.discovery;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.util.RateLimiter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class InstanceInfoReplicator implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(InstanceInfoReplicator.class);
    private final DiscoveryClient discoveryClient;
    private final InstanceInfo instanceInfo;
    private final int replicationIntervalSeconds;
    private final ScheduledExecutorService scheduler;
    private final AtomicReference<Future> scheduledPeriodicRef;
    private final AtomicBoolean started;
    private final RateLimiter rateLimiter;
    private final int burstSize;
    private final int allowedRatePerMinute;

    InstanceInfoReplicator(DiscoveryClient discoveryClient, InstanceInfo instanceInfo, int replicationIntervalSeconds, int burstSize) {
        this.discoveryClient = discoveryClient;
        this.instanceInfo = instanceInfo;
        this.scheduler = Executors.newScheduledThreadPool(1, (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-InstanceInfoReplicator-%d").setDaemon(true).build());
        this.scheduledPeriodicRef = new AtomicReference();
        this.started = new AtomicBoolean(false);
        this.rateLimiter = new RateLimiter(TimeUnit.MINUTES);
        this.replicationIntervalSeconds = replicationIntervalSeconds;
        this.burstSize = burstSize;
        this.allowedRatePerMinute = 60 * this.burstSize / this.replicationIntervalSeconds;
        logger.info("InstanceInfoReplicator onDemand update allowed rate per min is {}", this.allowedRatePerMinute);
    }

    public void start(int initialDelayMs) {
        if (this.started.compareAndSet(false, true)) {
            this.instanceInfo.setIsDirty();
            Future next = this.scheduler.schedule(this, (long)initialDelayMs, TimeUnit.SECONDS);
            this.scheduledPeriodicRef.set(next);
        }

    }

    public void stop() {
        this.shutdownAndAwaitTermination(this.scheduler);
        this.started.set(false);
    }

    private void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown();

        try {
            if (!pool.awaitTermination(3L, TimeUnit.SECONDS)) {
                pool.shutdownNow();
            }
        } catch (InterruptedException var3) {
            logger.warn("InstanceInfoReplicator stop interrupted");
        }

    }

    public boolean onDemandUpdate() {
        if (this.rateLimiter.acquire(this.burstSize, (long)this.allowedRatePerMinute)) {
            if (!this.scheduler.isShutdown()) {
                this.scheduler.submit(new Runnable() {
                    public void run() {
                        InstanceInfoReplicator.logger.debug("Executing on-demand update of local InstanceInfo");
                        Future latestPeriodic = (Future)InstanceInfoReplicator.this.scheduledPeriodicRef.get();
                        if (latestPeriodic != null && !latestPeriodic.isDone()) {
                            InstanceInfoReplicator.logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update");
                            latestPeriodic.cancel(false);
                        }

                        InstanceInfoReplicator.this.run();
                    }
                });
                return true;
            } else {
                logger.warn("Ignoring onDemand update due to stopped scheduler");
                return false;
            }
        } else {
            logger.warn("Ignoring onDemand update due to rate limiter");
            return false;
        }
    }

    public void run() {
        boolean var6 = false;

        ScheduledFuture next;
        label53: {
            try {
                var6 = true;
                this.discoveryClient.refreshInstanceInfo();
                Long dirtyTimestamp = this.instanceInfo.isDirtyWithTime();
                if (dirtyTimestamp != null) {
                    this.discoveryClient.register();
                    this.instanceInfo.unsetIsDirty(dirtyTimestamp);
                    var6 = false;
                } else {
                    var6 = false;
                }
                break label53;
            } catch (Throwable var7) {
                logger.warn("There was a problem with the instance info replicator", var7);
                var6 = false;
            } finally {
                if (var6) {
                    ScheduledFuture next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
                    this.scheduledPeriodicRef.set(next);
                }
            }

            next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
            this.scheduledPeriodicRef.set(next);
            return;
        }

        next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
        this.scheduledPeriodicRef.set(next);
    }
}

看到一个方法refreshInstanceInfo点进去,如果有变化,在下次心跳时,同步向server

 void refreshInstanceInfo() {
    this.applicationInfoManager.refreshDataCenterInfoIfRequired();
    this.applicationInfoManager.refreshLeaseInfoIfRequired();

    InstanceStatus status;
    try {
        status = this.getHealthCheckHandler().getStatus(this.instanceInfo.getStatus());
    } catch (Exception var3) {
        logger.warn("Exception from healthcheckHandler.getStatus, setting status to DOWN", var3);
        status = InstanceStatus.DOWN;
    }

    if (null != status) {
        this.applicationInfoManager.setInstanceStatus(status);
    }

}

注册状态改变监听器,在应用状态发生变化时,刷新服务实例信息,在服务实例信息发生改变时向server注册

看这段            

this.statusChangeListener = new StatusChangeListener() {
    public String getId() {
        return "statusChangeListener";
    }

    public void notify(StatusChangeEvent statusChangeEvent) {
        if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
            DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
        } else {
            DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
        }

        DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
    }
};

如果状态发生改变,调用onDemandUpdate(),点onDemandUpdate进去,看到InstanceInfoReplicator.this.run();    

一部分自己去检查,一部分等待状态监听事件

初始化定时任务完成,最后一步启动步骤完成

接下来就是正常服务于业务。然后消亡       

服务下线

服务下线:在应用关闭时,client会向server注销自己,在Discoveryclient销毁前,会执行下面清理方法。

@PreDestroy
public synchronized void shutdown() {
    if (this.isShutdown.compareAndSet(false, true)) {
        logger.info("Shutting down DiscoveryClient ...");
        if (this.statusChangeListener != null && this.applicationInfoManager != null) {
            this.applicationInfoManager.unregisterStatusChangeListener(this.statusChangeListener.getId());
        }

        this.cancelScheduledTasks();
        if (this.applicationInfoManager != null && this.clientConfig.shouldRegisterWithEureka() && this.clientConfig.shouldUnregisterOnShutdown()) {
            this.applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
            this.unregister();
        }

        if (this.eurekaTransport != null) {
            this.eurekaTransport.shutdown();
        }

        this.heartbeatStalenessMonitor.shutdown();
        this.registryStalenessMonitor.shutdown();
        Monitors.unregisterObject(this);
        logger.info("Completed shut down of DiscoveryClient");
    }

}

@PreDestroy 表示:在销毁前执行此方法

  • unregisterStatusChangeListener注销监听器
  • cancelScheduledTasks取消定时任务
  • unregister服务下线
  • eurekaTransport.shutdown关闭jersy客户端

unregister

void unregister() {
    if (this.eurekaTransport != null && this.eurekaTransport.registrationClient != null) {
        try {
            logger.info("Unregistering ...");
            EurekaHttpResponse<Void> httpResponse = this.eurekaTransport.registrationClient.cancel(this.instanceInfo.getAppName(), this.instanceInfo.getId());
            logger.info("DiscoveryClient_{} - deregister  status: {}", this.appPathIdentifier, httpResponse.getStatusCode());
        } catch (Exception var2) {
            logger.error("DiscoveryClient_{} - de-registration failed{}", new Object[]{this.appPathIdentifier, var2.getMessage(), var2});
        }
    }

}

AbstractJerseyEurekaHttpClient的cancel方法

public EurekaHttpResponse<Void> cancel(String appName, String id) {
    String urlPath = "apps/" + appName + '/' + id;
    ClientResponse response = null;

    EurekaHttpResponse var6;
    try {
        Builder resourceBuilder = this.jerseyClient.resource(this.serviceUrl).path(urlPath).getRequestBuilder();
        this.addExtraHeaders(resourceBuilder);
        response = (ClientResponse)resourceBuilder.delete(ClientResponse.class);
        var6 = EurekaHttpResponse.anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
    } finally {
        if (logger.isDebugEnabled()) {
            logger.debug("Jersey HTTP DELETE {}/{}; statusCode={}", new Object[]{this.serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()});
        }

        if (response != null) {
            response.close();
        }

    }

    return var6;
}

String urlPath = "apps/" + appName + '/' + id

看到url和http请求delete方法

Server的配合

github地址:https://github.com/Netflix/eureka/wiki/Eureka-REST-operations

论读书
睁开眼,书在面前
闭上眼,书在心里
原文地址:https://www.cnblogs.com/YC-L/p/14479577.html