一、客户端工作流程
1.1、初始化阶段
- 1、读取与server交互的信息,封装成EurekaClientConfig
- 2、读取自身服务信息,封装成EurekaInstanceConfig
- 3、拉取Server注册信息缓存到本地
- 4、服务注册
- 5、发送心跳,刷新缓存,注册定时任务
1.2、启动阶段
- 1、发送心跳维持租约
- 2、定时获取注册表信息更新本地缓存
- 3、监控自身信息,如果有变化重新注册服务
1.3、注销阶段
- 从server端注销服务
二、配置信息类
2.1、EurekaDiscoveryClientConfiguration
- 作用:帮助Client维持必要bean的属性读取和配置
2.1.1 读取属性和配置类
- EurekaClientConfig:封装了与server交互的信息
- ApplicationInfoManager:应用信息管理器,管理InstanceInfo,EurekaInstanceConfig
- InstanceInfo:发送到server进行注册的元数据
- EurekaInstanceConfig:自身服务实例的配置信息,用于构建InstanceInfo
- DiscoverClient:用于服务发现的客户端接口
2.2、DiscoverClient
- 说明:是服务端发现的核心接口
- String description():获取实现类的描述
- List getInstances(String serviceId):通过服务id获取服务信息
- List getServices():获取服务id列表
package org.springframework.cloud.client.discovery;
public interface DiscoveryClient {
String description();
List<ServiceInstance> getInstances(String serviceId);
List<String> getServices();
}
2.3、EurekaDiscoveryClient
- 继承了DiscoveryClient,组合EurekaClient实现接口功能
package org.springframework.cloud.netflix.eureka;
public class EurekaDiscoveryClient implements DiscoveryClient {}
三、DiscoverClient类结构
3.1、DiscoveryClient
- 包名:com.netflix.discovery.DiscoveryClient
- 说明:Client端与Server端交互关键逻辑
3.1.1、功能
- 1、注册服务到Server
- 2、发送心跳更新租约
- 3、服务关闭时从Server中取消租约下线服务
- 4、查询在Server中注册的服务实例列表
package com.netflix.discovery;
@Singleton
public class DiscoveryClient implements EurekaClient {}
3.1.2、类体系
LookupService—> EurekaClient ----> DiscoveryClient
3.2、LookupService
- Application getApplication(String appName):通过服务名称获取实例信息
- Applications getApplications():获取实例列表
- List getInstancesById(String id):通过服务id获取服务信息
package com.netflix.discovery.shared;
public interface LookupService<T> {
Application getApplication(String appName);
Applications getApplications();
List<InstanceInfo> getInstancesById(String id);
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}
3.3、Application
- Application中对InstanceInfo的操作都是同步
- Applications里面的操作基本也是同步操作
3.3.1、Application所有属性
package com.netflix.discovery.shared;
@Serializer("com.netflix.discovery.converters.EntityBodyConverter")
@XStreamAlias("application")
@JsonRootName("application")
public class Application {
private static Random shuffleRandom = new Random();
private String name;
private volatile boolean isDirty = false;
private final Set<InstanceInfo> instances;
private final AtomicReference<List<InstanceInfo>> shuffledInstances;
private final Map<String, InstanceInfo> instancesMap;
}
3.3.2、Application操作同步
private void removeInstance(InstanceInfo i, boolean markAsDirty) {
instancesMap.remove(i.getId());
synchronized (instances) {
instances.remove(i);
if (markAsDirty) {
isDirty = true;
}
}
}
3.4、EurekaClient
- 特点:继承了LookupService,为DiscoveryClient提供了上层接口,属于比较稳定的接口(扩展层)
3.4.1、作用
- 提供了多种获取InstanceInfo接口
- 提供了本地客户端数据
- 提供了为客户端注册和获取健康检查处理器的能力
3.4.2、核心方法
- 为Eureka Client注册健康检查处理器
- 监听Client服务实例信息的更新
//为Eureka Client注册健康检查处理器
public void registerHealthCheck(HealthCheckHandler healthCheckHandler);
//监听Client服务实例信息的更新
public void registerEventListener(EurekaEventListener eventListener);
3.5、HealthCheckHandler
- 用于检查当前Client的状态,如果Client的姿态发生改变,将会触发新的注册事件
- 该事件属于观察者模式,事件监听器将监听Client的服务实例信息变化,触发对应的处理事件
public interface HealthCheckHandler {
InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus);
}
四、、DiscoverClient 源码解读
4.1、DiscoverClient构造方法
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider)
4.1.1、构造方法参数
参数说明
- ApplicationInfoManager:应用信息管理器
- EurekaClientConfig:Client与Server交互配置信息
- AbstractDiscoveryClientOptionalArgs:注入可选参数
- Provider:用于获取注册表信息
4.1.2、构造方法主要操作
- 从Server中拉取注册表信息,服务信息,初始化发送心跳,刷新缓存,按需注册定时任务
- 1、初始化阶段:基础信息初始化,配置信息初始化,线程池初始化
- 2、构建阶段:构建EurekaTransport
- 3、预处理阶段:拉取Server注册表信息,注册前预处理
- 4、注册阶段:向Server中注册,初始化心跳定时任务(线程池2个线程)
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider) {
//1、参数说明
//ApplicationInfoManager:应用信息管理器
//EurekaClientConfig:Client与Server交互信息
//AbstractDiscoveryClientOptionalArgs:可选参数
//Provider<BackupRegistry>:注册中心备份
//2、初始化信息
if (args != null) {
this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
this.eventListeners.addAll(args.getEventListeners());
this.preRegistrationHandler = args.preRegistrationHandler;
} else {
this.healthCheckCallbackProvider = null;
this.healthCheckHandlerProvider = null;
this.preRegistrationHandler = null;
}
this.applicationInfoManager = applicationInfoManager;
InstanceInfo myInfo = applicationInfoManager.getInfo();
clientConfig = config;
staticClientConfig = clientConfig;
transportConfig = config.getTransportConfig();
instanceInfo = myInfo;
if (myInfo != null) {
appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
} else {
logger.warn("Setting instanceInfo to a passed in null value");
}
this.backupRegistryProvider = backupRegistryProvider;
this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo);
localRegionApps.set(new Applications());
fetchRegistryGeneration = new AtomicLong(0);
remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions());
remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(","));
//3、配置信息读取初始化
//shouldFetchRegistry对应配置:eureka.client.fetch-register=true/false(是否从Server中拉取注册表信息)
//shouldRegisterWithEureka对应配置:eureka.client.register-with-eureka=true/false(是否将自身信息注册到Server)
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;
}
logger.inf