SpringCloud部分源码:Eureka Client注册

1:使用SpringCloud框架的程序,除了Eureka服务之外,其余的服务都是Eureka Client。默认的情况,会向Eureka注册自己的服务实例,并从Eureka服务器周期性的获取注册信息。
在应用程序 引入 spring-cloud-starter-eureka 依赖包,即可引入 Eureka Clinet:spring-cloud-netflix-eureka-client。(可能不同的版本,依赖的包名称不一样)。如下:

 org.springframework.cloud
 spring-cloud-starter-eureka

2:在spring-cloud-netflix-eureka-client包的 spring.factories里,代码如下。在这里里面,会执行自动配置加载类:EurekaClientAutoConfiguration。这里主要就是注册Eureka客户端需要的对象。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

3:默认情况,配置 eureka.client.enabled 的值是true。在yml文件以 eureka.client 开头的配置,解析的对象是EurekaClientConfigBean。
其默认配置如下:
registryFetchIntervalSeconds:从erureka服务端获取服务实例的频率。默认情况下。每个30秒一次
eurekaServerReadTimeoutSeconds:从eureka服务端读取获取信息的超时时间,默认:8秒
eurekaServerConnectTimeoutSeconds:连接eureka服务器的超时时间,默认:5秒
serviceUrl:eureka服务注册的URL:默认地址是:http://localhost:8761/eureka。这里是个MAP,可以配置多个地址
eurekaServerTotalConnections:连接到eureka的总连接数,默认:200个
eurekaServerTotalConnectionsPerHost:连接单个eureka的连接数,默认:50个
registerWithEureka:是否注册到eureka,默认值:true
fetchRegistry:是否从eureka服务端获取注册信息,默认值:true.

@ConfigurationProperties(EurekaClientConfigBean.PREFIX)
public class EurekaClientConfigBean implements EurekaClientConfig, EurekaConstants {
   public static final String PREFIX = "eureka.client";
 @Autowired(required = false)
   PropertyResolver propertyResolver;
 public static final String DEFAULT_URL = "http://localhost:8761" + DEFAULT_PREFIX
 + "/";
 public static final String DEFAULT_ZONE = "defaultZone";
 
 @Bean
@ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
public EurekaClientConfigBean eurekaClientConfigBean() {
   EurekaClientConfigBean client = new EurekaClientConfigBean();
 if ("bootstrap".equals(propertyResolver.getProperty("spring.config.name"))) {
      // We don't register during bootstrap by default, but there will be another
 // chance later. client.setRegisterWithEureka(false);
 }
   return client;
}

4:注册DiscoveryClient实例,可以通过该实例对象,从缓存中获取在eureka上注册的服务信息。~~~~

@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
   return new EurekaDiscoveryClient(config, client);
}

5:注册EurekaClient实例,类型是CloudEurekaClient。其父类是 DiscoveryClient。在该类的构造函数里,会建立注册到eureka和从eureka获取服务服务的定时任务。

@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
   return new CloudEurekaClient(manager, config, this.optionalArgs,
 this.context);
}

6:在 DiscoveryClient 的构造函数里,除了各种属性赋值外,还调用了一个非常重要的方法initScheduledTasks()来初始化定时任务,当然,如果该应用的registerWithEureka和fetchRegistry都是false,则不不会执行该方法。

private void initScheduledTasks() {
    if (clientConfig.shouldFetchRegistry()) { //fetchRegistry配置的是true,则执行
        // registry cache refresh timer
 int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); //默认30S,从eureka获取注册信息间隔时间
 int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); //默认值是10,
 scheduler.schedule( //建立任务,从eureka获取注册信息,初始延迟时间,30S。任务类:TimedSupervisorTask
                new TimedSupervisorTask(
                        "cacheRefresh",
 scheduler,
 cacheRefreshExecutor,
 registryFetchIntervalSeconds,
 TimeUnit.SECONDS,
 expBackOffBound,
 new CacheRefreshThread()
                ),
 registryFetchIntervalSeconds, TimeUnit.SECONDS);
 }
    if (clientConfig.shouldRegisterWithEureka()) { //registerWithEureka配置的值是true则执行。
        int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
 int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); //默认30秒
 logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs);
 // Heartbeat timer
 scheduler.schedule( //注册心跳定时任务,定时向eureka发送信息,初始延迟时间30S
                new TimedSupervisorTask(
                        "heartbeat",
 scheduler,
 heartbeatExecutor,
 renewalIntervalInSecs,
 TimeUnit.SECONDS,
 expBackOffBound,
 new HeartbeatThread()
                ),
 renewalIntervalInSecs, TimeUnit.SECONDS);
 // InstanceInfo replicator
 instanceInfoReplicator = new InstanceInfoReplicator(
                this,
 instanceInfo,
 clientConfig.getInstanceInfoReplicationIntervalSeconds(),
 2); // burstSize
 statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
            @Override
 public String getId() {
                return "statusChangeListener";
 }
            @Override
 public void notify(StatusChangeEvent statusChangeEvent) {
                if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
                        InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
                    // log at warn level if DOWN was involved
 logger.warn("Saw local status change event {}", statusChangeEvent);
 } else {
                    logger.info("Saw local status change event {}", statusChangeEvent);
 }
                instanceInfoReplicator.onDemandUpdate();
 }
        };
 if (clientConfig.shouldOnDemandUpdateStatusChange()) { //注册事件监听器,按照上面的代码,如果发现状态是DOWN,会立刻注册到eureka
            applicationInfoManager.registerStatusChangeListener(statusChangeListener);
 }
        instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); //建立定时任务,初始延迟时间是40S
 } else {
        logger.info("Not registering with Eureka server per configuration");
 }
}

7:实际上上面代码中,TimedSupervisorTask类实际执行的是类: CacheRefreshThread 和 HeartbeatThread 的run 方法。 这里就不展开。 代码也不难。

8:在自动化配置中,同时也会实例化 EurekaAutoServiceRegistration 对象。该对象监听了Spring的事件:EmbeddedServletContainerInitializedEvent。当WEB容器初始化完成后,会发送此事件。 在该事件处理方法中,会把当前节点注册到 eureka服务器中。

@EventListener(EmbeddedServletContainerInitializedEvent.class)
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
   // TODO: take SSL into account when Spring Boot 1.2 is available
 int localPort = event.getEmbeddedServletContainer().getPort();
 if (this.port.get() == 0) {
      log.info("Updating port to " + localPort);
 this.port.compareAndSet(0, localPort);
 start();
 }
}

public void start() {
   // only set the port if the nonSecurePort is 0 and this.port != 0
 if (this.port.get() != 0 && this.registration.getNonSecurePort() == 0) {
      this.registration.setNonSecurePort(this.port.get());
 }
   // only initialize if nonSecurePort is greater than 0 and it isn't already running
 // because of containerPortInitializer below if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
      this.serviceRegistry.register(this.registration);
 this.context.publishEvent(
            new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
 this.running.set(true);
 }
}

你可能感兴趣的:(java,springboot)