Eureka Client 源码解析
来一波大招先
ExampleEurekaClient.java 解析
public class ExampleEurekaClient {
/**
* 应用信息管理器
*/
private static ApplicationInfoManager applicationInfoManager;
/**
* eureka 客户端
*/
private static EurekaClient eurekaClient;
/**
* 初始化应用信息管理器
*
* @param instanceConfig eureka 应用配置
* @return 应用信息管理器
*/
private static synchronized ApplicationInfoManager initializeApplicationInfoManager(EurekaInstanceConfig instanceConfig) {
// 判断是否为空 应用信息管理器
if (applicationInfoManager == null) {
// 初始化实例信息
InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
// 根据实例信息和实例配置参数 构建 应用信息管理器
applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);
}
// 返回 应用信息管理器
return applicationInfoManager;
}
/**
* 初始化eureka客户端
*
* @param applicationInfoManager 应用信息管理器
* @param clientConfig eureka client 客户端配置参数
* @return eureka client
*/
private static synchronized EurekaClient initializeEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig clientConfig) {
// 判断 eureka client 是否为空
if (eurekaClient == null) {
// 根据 eureka client配置参数和应用信息管理器 初始化eureka client
eurekaClient = new DiscoveryClient(applicationInfoManager, clientConfig);
}
// 返回 eureka client
return eurekaClient;
}
/**
* 模拟发送请求和使用eureka
*
* @param eurekaClient eureka client
*/
public void sendRequestToServiceUsingEureka(EurekaClient eurekaClient) {
// initialize the client
// this is the vip address for the example service to talk to as defined in conf/sample-eureka-service.properties
String vipAddress = "sampleservice.mydomain.net";
InstanceInfo nextServerInfo = null;
try {
nextServerInfo = eurekaClient.getNextServerFromEureka(vipAddress, false);
} catch (Exception e) {
System.err.println("Cannot get an instance of example service to talk to from eureka");
System.exit(-1);
}
System.out.println("Found an instance of example service to talk to from eureka: "
+ nextServerInfo.getVIPAddress() + ":" + nextServerInfo.getPort());
System.out.println("healthCheckUrl: " + nextServerInfo.getHealthCheckUrl());
System.out.println("override: " + nextServerInfo.getOverriddenStatus());
Socket s = new Socket();
int serverPort = nextServerInfo.getPort();
try {
s.connect(new InetSocketAddress(nextServerInfo.getHostName(), serverPort));
} catch (IOException e) {
System.err.println("Could not connect to the server :"
+ nextServerInfo.getHostName() + " at port " + serverPort);
} catch (Exception e) {
System.err.println("Could not connect to the server :"
+ nextServerInfo.getHostName() + " at port " + serverPort + "due to Exception " + e);
}
try {
String request = "FOO " + new Date();
System.out.println("Connected to server. Sending a sample request: " + request);
PrintStream out = new PrintStream(s.getOutputStream());
out.println(request);
System.out.println("Waiting for server response..");
BufferedReader rd = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = rd.readLine();
if (str != null) {
System.out.println("Received response from server: " + str);
System.out.println("Exiting the client. Demo over..");
}
rd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 1、读取eureka-client.properties配置信息,形成一个服务实例的配置,基于接口对外提供服务实例的配置信息
* 2、基于服务实例配置构造一个服务实例信息管理器
* 3、
*/
public static void main(String[] args) throws UnknownHostException {
// 自定义注入 eureka client 配置参数
injectEurekaConfiguration();
ExampleEurekaClient sampleClient = new ExampleEurekaClient();
// create the client
// 初始化应用信息管理器
ApplicationInfoManager applicationInfoManager = initializeApplicationInfoManager(new MyDataCenterInstanceConfig());
// 初始化eureka客户端
// new DefaultEurekaClientConfig() 从配置文件 eureka-client.properties 获取 eureka 前缀开头配置信息
EurekaClient client = initializeEurekaClient(applicationInfoManager, new DefaultEurekaClientConfig());
// use the client
// 模拟发送请求和使用eureka
sampleClient.sendRequestToServiceUsingEureka(client);
// shutdown the client
// 注销 eureka client
eurekaClient.shutdown();
}
/**
* 自定义注入 eureka client 配置参数
*
* @throws UnknownHostException
*/
private static void injectEurekaConfiguration() throws UnknownHostException {
String myHostName = InetAddress.getLocalHost().getHostName();
String myServiceUrl = "http://" + myHostName + ":8080/v2/";
System.setProperty("eureka.region", "default");
System.setProperty("eureka.name", "eureka");
System.setProperty("eureka.vipAddress", "eureka.mydomain.net");
System.setProperty("eureka.port", "8080");
System.setProperty("eureka.preferSameZone", "false");
System.setProperty("eureka.shouldUseDns", "false");
System.setProperty("eureka.shouldFetchRegistry", "true");
System.setProperty("eureka.serviceUrl.defaultZone", myServiceUrl);
System.setProperty("eureka.serviceUrl.default.defaultZone", myServiceUrl);
System.setProperty("eureka.awsAccessId", "fake_aws_access_id");
System.setProperty("eureka.awsSecretKey", "fake_aws_secret_key");
System.setProperty("eureka.numberRegistrySyncRetries", "0");
}
}
DiscoveryClient.java 类解析
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
// 判断是否第一次启动eureka,清理参数配置
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;
}
// 将ApplicationInfoManager转存至本地
this.applicationInfoManager = applicationInfoManager;
// 从ApplicationInfoManager中获取实例信息
InstanceInfo myInfo = applicationInfoManager.getInfo();
// eureka client 配置信息
clientConfig = config;
// 将eureka client config 转静态
staticClientConfig = clientConfig;
// euraka传输协议配置:EurekaTransportConfig
transportConfig = config.getTransportConfig();
// 将实例信息转存至本地
instanceInfo = myInfo;
// 判断实例信息是否存在
if (myInfo != null) {
// 应用程序路径标识符 = 服务AppName + 服务ID
appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
} else {
logger.warn("Setting instanceInfo to a passed in null value");
}
// 备份注册表
this.backupRegistryProvider = backupRegistryProvider;
this.endpointRandomizer = endpointRandomizer;
this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo);
localRegionApps.set(new Applications());
// 获取注册表生成
fetchRegistryGeneration = new AtomicLong(0);
// 远程相关信息 AtomicReference 面试突击第二季
// 获取远程区域的注册表
remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions());
remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(","));
// 是否获取注册表 对应配置属性 shouldFetchRegistry
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;
}
// 是否获取eureka注册信息 对应配置属性 registration.enabled
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.info("Initializing Eureka in region {}", clientConfig.getRegion());
// shouldRegisterWithEureka=true,即要注册到Eureka Server 启动 heartbeat 心跳定时线程
// shouldFetchRegistry设置为true代表这是一个服务发现客户端
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();
initRegistrySize = this.getApplications().size();
registrySize = initRegistrySize;
logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}",
initTimestampMs, initRegistrySize);
return; // no need to setup up an network tasks and we are done
}
try {
// 初始化调度的线程池
// 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()
); // use direct handoff
// 初始化缓存刷新调度线程池
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
// 支持底层的eureka client跟eureka server进行网络通信的组件
eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);
// 是否启用DNS
AzToRegionMapper azToRegionMapper;
if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig);
} else {
azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig);
}
if (null != remoteRegionsToFetch.get()) {
azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(","));
}
instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, clientConfig.getRegion());
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
}
// 抓取注册表
if (clientConfig.shouldFetchRegistry()) {
try {
// 是否全量抓取注册表
boolean primaryFetchRegistryResult = fetchRegistry(false);
if (!primaryFetchRegistryResult) {
logger.info("Initial registry fetch from primary servers failed");
}
// 设置是否备份注册表
boolean backupFetchRegistryResult = true;
if (!primaryFetchRegistryResult && !fetchRegistryFromBackup()) {
backupFetchRegistryResult = false;
logger.info("Initial registry fetch from backup servers failed");
}
// shouldEnforceFetchRegistryAtInit 初始化时,强制eureka server注册eureka client
if (!primaryFetchRegistryResult && !backupFetchRegistryResult && clientConfig.shouldEnforceFetchRegistryAtInit()) {
throw new IllegalStateException("Fetch registry error at startup. Initial fetch failed.");
}
} catch (Throwable th) {
logger.error("Fetch registry error at startup: {}", th.getMessage());
throw new IllegalStateException(th);
}
}
// 全部并在所有后台任务(inc注册)开始之前执行预注册处理程序
// call and execute the pre registration handler before all background tasks (inc registration) is started
if (this.preRegistrationHandler != null) {
this.preRegistrationHandler.beforeRegistration();
}
// 注册到Eureka Server 启动 heartbeat 心跳定时线程 && 全部并在所有后台任务(inc注册)开始之前执行预注册处理程序
if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
try {
if (!register() ) {
throw new IllegalStateException("Registration error at startup. Invalid server response.");
}
} catch (Throwable th) {
logger.error("Registration error at startup: {}", th.getMessage());
throw new IllegalStateException(th);
}
}
// 初始化定时任务
// finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
initScheduledTasks();
// 注册监听器
try {
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register timers", e);
}
// 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();
initRegistrySize = this.getApplications().size();
registrySize = initRegistrySize;
logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}",
initTimestampMs, initRegistrySize);
}