Netflix Eureka - 服务端实例构造(EurekaClient)

298 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

服务端实例构造

初始化 ApplicationInfoManager

Application

EurekaInstanceConfig

MyDataCenterInstanceConfig

PropertiesInstanceConfig

CommonConstants

Archaius1Utils

InstanceInfo

初始化 eureka-client

初始化 eureka-server 内部的一个 eureka-client(用来与其他 eureka-server 节点进行 注册和通信)
// 获取 eureka客户端配置 实例
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
// 获取 (EurekaClient)DiscoveryClient eureka客户端 实例
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);

EurekaClientConfig -> DefaultEurekaClientConfig

EurekaClientConfig接口,加载eureka-client.properties里的配置,对外暴露配置项。

EurekaIntanceConfig:偏重于eureka-client 服务实例的配置项信息

EurekaClientConfig:偏重于eureka-client 客户端的配置项信息

EurekaInstanceConfig

image.png

EurekaClientConfig

image.png

DynamicPropertyFactory

EurekaTransportConfig -> DefaultEurekaTransportConfig

package com.netflix.discovery;

@Singleton
@ProvidedBy(DefaultEurekaClientConfigProvider.class)
public class DefaultEurekaClientConfig implements EurekaClientConfig {

    @Deprecated
    public static final String DEFAULT_NAMESPACE = CommonConstants.DEFAULT_CONFIG_NAMESPACE + ".";
    public static final String DEFAULT_ZONE = "defaultZone";

    private final String namespace;
    private final DynamicPropertyFactory configInstance;
    private final EurekaTransportConfig transportConfig;

    public DefaultEurekaClientConfig() {
        // eureka
        this(CommonConstants.DEFAULT_CONFIG_NAMESPACE);
    }

    public DefaultEurekaClientConfig(String namespace) {
        // eureka.
        this.namespace = namespace.endsWith(".") ? namespace : namespace + ".";

        // eureka-client 配置实例 实例
        this.configInstance = Archaius1Utils.initConfig(CommonConstants.CONFIG_FILE_NAME);
        // 网络配置实例
        this.transportConfig = new DefaultEurekaTransportConfig(namespace, configInstance);
    }
}

EurekaClient -> DiscoveryClient

创建EurekaClient

以EurekaClient的子类DiscoveryClient创建EurekaClient。

构造参数有:

  • ApplicationInfoManager:服务实例的信息、配置,作为服务实例管理的一个组件
  • eurekaClientConfig:eureka client相关的配置
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);

构建appPathIdentifier

  • appName:服务名称。通过eureka-client.properties文件获取eureka.name=eureka
  • id:instanceId 如果为空,则返回hostname。一个服务可以存在多个服务实例,构建成集群。id(instanceId)表示一个服务实例
appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();

fetchRegistryGeneration & remoteRegionsToFetch & remoteRegionsRef

AtomicLong和AtomicReference,原子性操作的一些类,用于统计Eureka服务注册及租约信息

拉取注册表

fetchRegistry:默认是true,允许拉取注册中心的注册表。

单机模式需要设置为false,不允许拉取注册中心的注册表,因为就一个注册中心,还是自己。

集群模式需要设置为true,允许拉取注册中心的注册表,因为有多个注册中心,需要同步各个节点间的注册表信息

// 期望抓取注册表
if (config.shouldFetchRegistry()) {
    // 抓取注册表监控器
    this.registryStalenessMonitor = new ThresholdLevelsMetric(this,
            METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L,
            240L, 480L});
} else {
    this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}

注册服务到注册中心

// 允许将自己注册到注册中心
if (config.shouldRegisterWithEureka()) {
    // 心跳监控器
    this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this,
            METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L,
            120L, 240L, 480L});
} else {
    this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}

既不允许注册服务到注册中心,也不允许抓取注册表(此处清理资源并结束方法)

// 如果既不允许注册服务到注册中心,也不允许抓取注册表, 清理资源并结束方法
if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
    logger.info("Client configured to neither register nor query for data.");
    scheduler = null;
    heartbeatExecutor = null;
    cacheRefreshExecutor = null;
    eurekaTransport = null;
    instanceRegionChecker =
            new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config),
                    clientConfig.getRegion());

    // This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()
    // to work with DI'd DiscoveryClient
    DiscoveryManager.getInstance().setDiscoveryClient(this);
    DiscoveryManager.getInstance().setEurekaClientConfig(config);

    initTimestampMs = System.currentTimeMillis();
    logger.info("Discovery Client initialized at timestamp {} with initial instances " +
            "count: {}", initTimestampMs, this.getApplications().size());

    return;  // no need to setup up an network tasks and we are done
}

创建调度的线程池

// 支持调度的线程池
// default size of 2 - 1 each for heartbeat and cacheRefresh
scheduler = Executors.newScheduledThreadPool(2,
        new ThreadFactoryBuilder().setNameFormat("DiscoveryClient-%d").setDaemon(true).build());

创建心跳的线程池

// 支持心跳的线程池
heartbeatExecutor = new ThreadPoolExecutor(1,
        clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder().setNameFormat(
                "DiscoveryClient-HeartbeatExecutor-%d").setDaemon(true).build());  //

创建缓存刷新的线程池

// 支持缓存刷新的线程池
cacheRefreshExecutor = new ThreadPoolExecutor(1,
        clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder().setNameFormat(
                "DiscoveryClient-CacheRefreshExecutor-%d").setDaemon(true).build());  // use

创建网络通信组件并初始化

// 网络通信组件
eurekaTransport = new EurekaTransport();
// 初始化网络通信组件
scheduleServerEndpointTask(eurekaTransport, args);

拉取(全量、增量)注册表成功

// 允许拉取注册表,并且拉取(全量、增量)注册表成功
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
    // 抓取失败,从备份里抓取
    fetchRegistryFromBackup();
}

初始化调度任务

// 初始化调度任务
initScheduledTasks();
  1. 读取EurekaClientConfig,包括TransportConfig

  2. 保存EurekaInstanceConfig和InstanceInfo

  3. 处理是否要注册以及抓取注册表,如果不要的话,释放一些资源

  4. 支持调度的线程池

  5. 支持心跳的线程池

  6. 支持缓存刷新的线程池

  7. EurekaTransport,支持底层的eureka client跟eureka server进行网络通信的组件,对网络通信组件进行了一些初始化的操作

    • ClosableResolver bootstrapResolver;
    • TransportClientFactory transportClientFactory;

    • EurekaHttpClient registrationClient;
    • EurekaHttpClientFactory registrationClientFactory;

    • EurekaHttpClient queryClient;
    • EurekaHttpClientFactory queryClientFactory;
  8. 如果要抓取注册表的话,在这里就会去抓取注册表了,但是如果说你配置了不抓取,那么这里就不抓取了

  9. 初始化调度任务:

    • 服务发现:注册定时任务,按照设定的抓取间隔(默认是30s),执行CacheRefreshThread任务。

    • 服务注册:

      • 注册定时任务,每隔一定时间发送心跳,执行一个HeartbeatThread任务;
      • 创建了服务实例信息同步器(InstanceInfoReplicator),将自己作为一个定时任务进行调度;
      • 创建了服务实例的状态变更监听器(statusChangeListener),如果配置了监听,那么就会注册监听器