本文已参与「新人创作礼」活动,一起开启掘金创作之路。
服务端实例构造
初始化 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
EurekaClientConfig
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();
-
读取EurekaClientConfig,包括TransportConfig
-
保存EurekaInstanceConfig和InstanceInfo
-
处理是否要注册以及抓取注册表,如果不要的话,释放一些资源
-
支持调度的线程池
-
支持心跳的线程池
-
支持缓存刷新的线程池
-
EurekaTransport,支持底层的eureka client跟eureka server进行网络通信的组件,对网络通信组件进行了一些初始化的操作
- ClosableResolver bootstrapResolver;
- TransportClientFactory transportClientFactory;
- EurekaHttpClient registrationClient;
- EurekaHttpClientFactory registrationClientFactory;
- EurekaHttpClient queryClient;
- EurekaHttpClientFactory queryClientFactory;
-
如果要抓取注册表的话,在这里就会去抓取注册表了,但是如果说你配置了不抓取,那么这里就不抓取了
-
初始化调度任务:
-
服务发现:注册定时任务,按照设定的抓取间隔(默认是30s),执行CacheRefreshThread任务。
-
服务注册:
- 注册定时任务,每隔一定时间发送心跳,执行一个HeartbeatThread任务;
- 创建了服务实例信息同步器(InstanceInfoReplicator),将自己作为一个定时任务进行调度;
- 创建了服务实例的状态变更监听器(statusChangeListener),如果配置了监听,那么就会注册监听器
-