(二)EurekaClient-拉取注册表

0. 前言

  • springboot版本:2.1.9.RELEASE
  • springcloud版本:Greenwich.SR4

1. 方法入口

public class EurekaClientAutoConfiguration {
  
    @Configuration
    @ConditionalOnRefreshScope
    protected static class RefreshableEurekaClientConfiguration {
        // ......
        @Bean(destroyMethod = "shutdown")
        @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
	@org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public EurekaClient eurekaClient(ApplicationInfoManager manager,
		EurekaClientConfig config, EurekaInstanceConfig instance,
	        @Autowired(required = false) HealthCheckHandler healthCheckHandler) {
            // ......
            // Step1:初始化 CloudEurekaClient
	    CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
                    config, this.optionalArgs, this.context);
	    cloudEurekaClient.registerHealthCheck(healthCheckHandler);
	    return cloudEurekaClient;
        }
    }
    // ......
}


// CloudEurekaClient.class
public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, 
        EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args,
        ApplicationEventPublisher publisher) {
    // Step2:调用父类构造方法
    super(applicationInfoManager, config, args);
    // ......
}


// DiscoveryClient.class
public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
    this(...);
}

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

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
    // ......
    // 2 从服务端拉取注册表信息 fetchRegistry()
    if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
        // 如果从服务端拉取注册表失败,则获取备用的
        // 备用注册表需要我们自己实现,并通过配置文件配置后才能使用
        // 备用注册表实现方式:
        //     1. 从远程 region 的注册表获取
        //     2. 本地返回默认注册表
        fetchRegistryFromBackup();
    }
    // ......
    // 3 初始化定时任务,其中包含拉取注册表信息的定时任务
    initScheduledTasks();
    // ......
}

2. fetchRegistry()

// DiscoveryClient.class
private boolean fetchRegistry(boolean forceFullRegistryFetch) {
    Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

    try {
      // If the delta is disabled or if it is the first time, get all
      // applications
      Applications applications = getApplications();

        if (clientConfig.shouldDisableDelta()
                || (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
                || forceFullRegistryFetch
                || (applications == null)
                || (applications.getRegisteredApplications().size() == 0)
                || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
        {
            // ......
            // 2.1 全量拉取注册表信息
            getAndStoreFullRegistry();
        } else {
            // 2.2 增量拉取注册表信息
            getAndUpdateDelta(applications);
        }
        // ......
    } catch (Throwable e) {
        // ......
    } finally {
        // ......
    }
    // ......
}
2.1 全量拉取注册表信息
// DiscoveryClient.class
private void getAndStoreFullRegistry() throws Throwable {
    long currentUpdateGeneration = fetchRegistryGeneration.get();

    logger.info("Getting all instance registry info from the eureka server");

    Applications apps = null;
    // Jersey 请求获取服务端注册表信息
    // 配置文件中未配置 registry-refresh-single-vip-address ,默认为 null ,所以调用 getApplications() 方法
    EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
            ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
            : eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        apps = 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 (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
        // 服务端返回注册表信息过滤乱序后放入本地 localRegionApps 
        // 保留到本地注册表的实例条件:
        //     1. 实例状态为 UP (默认)
        //     2. 和当前客户端实例一样的 region (分区)
        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");
    }
}
2.2 增量拉取注册表信息

《EurekaClient-启动时初始化的定时任务》中讲到过

3. initScheduledTasks()

// DiscoveryClient.class
private void initScheduledTasks() {
    if (clientConfig.shouldFetchRegistry()) {
        // registry cache refresh timer
        int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
        int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
        // 开启定时刷新注册表任务
        scheduler.schedule(
                new TimedSupervisorTask(
                        "cacheRefresh",
                        scheduler,
                        cacheRefreshExecutor,
                        registryFetchIntervalSeconds,
                        TimeUnit.SECONDS,
                        expBackOffBound,
                        // 3.1 任务线程
                        new CacheRefreshThread()
                ),
                registryFetchIntervalSeconds, TimeUnit.SECONDS);
    }
    // ......
}
3.1 CacheRefreshThread()
// DiscoveryClient.class
class CacheRefreshThread implements Runnable {
    public void run() {
        // 刷新注册表
        refreshRegistry();
    }
}

@VisibleForTesting
void refreshRegistry() {
    try {
        // ......
        boolean remoteRegionsModified = false;
        // ......
        // 2 从服务端拉取注册表信息
        boolean success = fetchRegistry(remoteRegionsModified);
        // ......
    } catch (Throwable e) {
        logger.error("Cannot fetch registry from server", e);
    }
}

4. 总结

  • 拉起注册表的入口:客户端启动时、定时刷新注册表任务
  • 拉取注册表的方式:全量、增量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值