Spring Cloud组件之深入分析Eureka服务注册与发现核心源码


前言

对于一个微服务架构来说,通常都会将一个完整的项目拆分成许多个小的服务,而服务之间如何通讯以及服务如何治理是最需要解决的问题。而Eureka就是一种解决微服务架构中最基本的服务治理的一种技术。SpringCloud架构中Eureka作为服务治理的组件,管理各种服务功能包括服务的注册与发现。对于学习SpringCloud框架来说,还是需要对Eureka有一些了解的。下文将深入分析Eureka的一些核心源码,帮忙读者更多的了解Eureka这个组件。

源码分析

核心入口

Spring Cloud组件的加载也是按照SpringBoot的自动配置规范进行引入的。对于Eureka来说,它会在对应的jar中的META-INF下的spring.factories文件中,指定一系列的自动配置类,完成相应组件的自动加载
在这里插入图片描述
对于Eureka Client端,在客户端启动时,最为关键的是加载EurekaClientAutoConfiguration这个自动配置类
在这里插入图片描述

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({
    NoopDiscoveryClientAutoConfiguration.class,
		CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {
   
		"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
		"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
		"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })
public class EurekaClientAutoConfiguration {
   
     
    @Bean
	@ConditionalOnMissingBean(value = EurekaClientConfig.class,
			search = SearchStrategy.CURRENT)
	public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
   
	    //eureka客户端的配置类,可以在该类内部看到客户端的配置信息
		EurekaClientConfigBean client = new EurekaClientConfigBean();
		if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) {
   
			client.setRegisterWithEureka(false);
		}
		return client;
	}    
	....
}

该自动配置类内部又包含了EurekaClientConfiguration内部类,这个内部类通过@Bean的方式注入了一个CloudEurekaClient实例,查看CloudEurekaClient的类图可以发现它继承了DiscoveryClient类,而DiscoveryClient是eureka客户端最关键的一个类

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingRefreshScope
protected static class EurekaClientConfiguration {
   
    @Autowired
    private ApplicationContext context;
    
    @Autowired
    private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;

    //创建EurekaClient实例
    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(value = {
   EurekaClient.class}, search = SearchStrategy.CURRENT)
    public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
   
      //核心,创建CloudEurekaClient
      return (EurekaClient)new CloudEurekaClient(manager, config, this.optionalArgs, (ApplicationEventPublisher)this.context);
    }
   ....
}
public class CloudEurekaClient extends DiscoveryClient {
   
   ...
}

当CloudEurekaClient类被实例化时就会触发父类的DiscoveryClient实例化,然后就会执行到下面的这个构造函数。而这个构造函数中会做很多事情,包括:
(1)将客户端配置文件进行解析,然后将客户端配置信息封装到EurekaClientConfigBean对象中
(2)创建scheduler、heartbeatExecutor、cacheRefreshExecutor线程池
(3)创建定时任务,提交到线程池,进行服务注册、续约和服务拉取等功能

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
   
   ...
   if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
   
      ...
      return;
    } 
    try {
   
      AzToRegionMapper azToRegionMapper;
      //定时任务线程池,负责服务注册、续约和服务拉取
      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());
      ....
    } catch (Throwable e) {
   
      throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
    } 
    ...
    //核心:服务拉取、服务注册和续约的实现
    initScheduledTasks();
    try {
   
      Monitors.registerObject(this);
    } catch (Throwable e) {
   
      logger.warn("Cannot register timers", e);
    } 
    ...
  }

服务拉取

先判断客户端是否配置了服务拉取eureka.client.fetchRegistry配置项,如果配置为false就不进行服务拉取,缺省值为true。服务拉取过程中会向定义的scheduler线程池提交一个定时任务

 private void initScheduledTasks() {
   
    //确认是否配置eureka.client.fetch-registry=true 缺省为 true
    if (this.clientConfig.shouldFetchRegistry()) {
   
      int registryFetchIntervalSeconds = this.clientConfig.getRegistryFetchIntervalSeconds();
      int expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
      //向scheduler线程池提交一个拉取任务
      this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread()), registryFetchIntervalSeconds, TimeUnit.SECONDS);
    } 
    ....
  }
class CacheRefreshThread implements Runnable {
   
    public void run() {
   
      DiscoveryClient.this.refreshRegistry();
    }
  }

之后,任务执行时就会调用到refreshRegistry方法,该方法内部又会调用fetchRegistry方法进行真正的服务拉取工作。而服务拉取过程中会先判断客户端配置的eureka.client.disable-delta配置项的值,确认是全量拉取还是增量拉取
注意,增量拉取可以极大地减少流量,因为eureka服务器的更改速率通常远低于提取速率

void refreshRegistry() {
   
    try {
   
      ...
      //服务拉取实现
      boolean success = fetchRegistry(remoteRegionsModified);
      if (success) {
   
        this.registrySize = ((Applications)this.localRegionApps.get()).size();
        this.lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
      } 
      ....
      } 
    } catch (Throwable e) {
   
      logger.error("Cannot fetc
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值