本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Eureka Client 服务注册
InstanceInfoReplicator
InstanceInfoReplicator(服务实例信息复制组件):负责服务的注册
instanceInfoReplicator.start(
// 40秒
clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
初始化
InstanceInfoReplicator.start()方法将自己作为一个线程放到一个调度线程池中,将isDirty设置为了ture。默认延迟40s执行。
/**
* 1. 设置dirty标志
* 2. 加入调度任务
* @param initialDelayMs
*/
public void start(int initialDelayMs) {
if (started.compareAndSet(false, true)) {
instanceInfo.setIsDirty(); // for initial register
Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}
执行注册逻辑
InstanceInfoReplicator.run() 方法重载Runnable接口方法。由调度线程执行服务注册逻辑。
/**
* 服务实例信息复制器线程逻辑
*/
@Override
public void run() {
try {
// 刷新instanceInfo(数据中心、租约、服务实例状态)
discoveryClient.refreshInstanceInfo();
// 向注册中心注册服务
Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info replicator", t);
} finally {
Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}
刷新服务实例信息
EurekaClient.refreshInstanceInfo()调用ApplicationInfoManager的方法刷新服务实例信息。
/**
* Refresh the current local instanceInfo. Note that after a valid refresh where changes are
* observed, the
* isDirty flag on the instanceInfo is set to true
*/
void refreshInstanceInfo() {
// hostname、ipAddress如果发生变化的话就刷新一下
applicationInfoManager.refreshDataCenterInfoIfRequired();
// 租约信息如果发生变化的话就刷新一下
applicationInfoManager.refreshLeaseInfoIfRequired();
InstanceStatus status;
try {
// 通过健康检查处理器(HealthCheckHandler)获取InstanceStatus并设置到ApplicationInfoManager中
status = getHealthCheckHandler().getStatus(instanceInfo.getStatus());
} catch (Exception e) {
logger.warn("Exception from healthcheckHandler.getStatus, setting status to DOWN", e);
status = InstanceStatus.DOWN;
}
if (null != status) {
applicationInfoManager.setInstanceStatus(status);
}
}
数据中心信息刷新
hostname、ipAddress如果发生变化的话就刷新一下;
租约信息刷新
租约信息如果发生变化的话就刷新一下;
实例状态信息设置
通过健康检查处理器(HealthCheckHandler)获取InstanceStatus并设置到ApplicationInfoManager中;
向注册中心注册服务
EurekaClient.reigster()
通过EurekaClient的reigster()方法注册服务。
底层调用的是 eurekaTransport.registrationClient.register(instanceInfo)方法,将InstanceInfo服务实例的信息通过HTTP请求,发送到eureka server进行注册。
/**
* Register with the eureka service by making the appropriate REST call.
*/
boolean register() throws Throwable {
logger.info(PREFIX + appPathIdentifier + ": registering service...");
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage()
, e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info("{} - registration status: {}", PREFIX + appPathIdentifier,
httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}
eurekaTransport.registrationClient.register() 最终调用的是JerseyApplicationClient.register();
真正执行注册请求的是AbstractJerseyEurekaHttpClient的register方法,请求http://localhost:8080/v2/apps/ServiceA 将服务实例的信息发送过去
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = "apps/" + info.getAppName();
ClientResponse response = null;
try {
Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
addExtraHeaders(resourceBuilder);
response = resourceBuilder
.header("Accept-Encoding", "gzip")
.type(MediaType.APPLICATION_JSON_TYPE)
.accept(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, info);
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(),
response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
}
}
}
重置dirty标志
EurekaTransport 网络组件
-
TransportClientFactory transportClientFactory:网络组件客户端工厂实例
-
EurekaHttpClientFactory registrationClientFactory:通过transportClientFactory获取的用于获取注册客户端的工厂实例,
-
EurekaHttpClient registrationClient:通过registrationClientFactory工厂获得的用于注册服务的Http客户端
初始化网络组件
scheduleServerEndpointTask(eurekaTransport, args = null)
TransportClientFactories transportClientFactories = new Jersey1TransportClientFactories()
TransportClientFactory jerseyFactory:获取 jerseyFactory,生产JerseyApplicationClient。
TransportClientFactory metricsFactory:获取 metricsFactory,装饰 jerseyFactory。
RedirectingEurekaHttpClient:获取 RedirectingEurekaHttpClient ,装饰 metricsFactory
RetryableEurekaHttpClient:获取 RetryableEurekaHttpClient ,装饰 RedirectingEurekaHttpClient
SessionedEurekaHttpClient:获取 SessionedEurekaHttpClient ,装饰 RetryableEurekaHttpClient
eurekaTransport.transportClientFactory = transportClientFactories.newTransportClientFactory()
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<ClientFilter> additionalFilters,
final InstanceInfo myInstanceInfo) {
// 获取 jerseyFactory,生产JerseyApplicationClient。
// JerseyApplicationClient:负责发送http请求到eureka server的网络客户端
final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
clientConfig,
additionalFilters,
myInstanceInfo,
new EurekaClientIdentity(myInstanceInfo.getIPAddr())
);
// 获取 metricsFactory,装饰 jerseyFactory。
// MetricsCollectingEurekaHttpClient:为 JerseyApplicationClient 增加面板信息统计的能力
final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);
return new TransportClientFactory() {
@Override
public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
return metricsFactory.newClient(serviceUrl);
}
@Override
public void shutdown() {
metricsFactory.shutdown();
jerseyFactory.shutdown();
}
};
}
public static EurekaHttpClientFactory registrationClientFactory(ClusterResolver bootstrapResolver,
// metricsFactory
TransportClientFactory transportClientFactory,
EurekaTransportConfig transportConfig) {
return canonicalClientFactory(
EurekaClientNames.REGISTRATION,
transportConfig,
bootstrapResolver,
// metricsFactory
transportClientFactory);
}
static EurekaHttpClientFactory canonicalClientFactory(final String name,
final EurekaTransportConfig transportConfig,
final ClusterResolver<EurekaEndpoint> clusterResolver,
// metricsFactory
final TransportClientFactory transportClientFactory) {
return new EurekaHttpClientFactory() {
@Override
public EurekaHttpClient newClient() {
// 获取 SessionedEurekaHttpClient ,装饰 RetryableEurekaHttpClient
return new SessionedEurekaHttpClient(
name,
// 获取 RetryableEurekaHttpClient ,装饰 RedirectingEurekaHttpClient
RetryableEurekaHttpClient.createFactory(
name,
transportConfig,
clusterResolver,
// 获取 RedirectingEurekaHttpClient ,装饰 metricsFactory
RedirectingEurekaHttpClient.createFactory(
// metricsFactory
transportClientFactory),
ServerStatusEvaluators.legacyEvaluator()),
transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000
);
}
@Override
public void shutdown() {
wrapClosable(clusterResolver).shutdown();
}
};
}
eurekaTransport.registrationClientFactory =
EurekaHttpClients.registrationClientFactory(
eurekaTransport.bootstrapResolver,
// metricsFactory
eurekaTransport.transportClientFactory,
transportConfig);
eurekaTransport.registrationClient = eurekaTransport.registrationClientFactory.newClient();
EurekaHttpClient
EurekaHttpClient 使用装饰者模式装饰各个子类:
SessionedEurekaHttpClient:负责RetryEurekaHttpClient的超时重建
RetryEurekaHttpClient:负责请求失败重试
RedirectingEurekaHttpClient:负责死循环重定向或者超次数重定向断开处理
MetricsCollectingEurekaHttpClient:负责进行面板信息统计处理
JerseyApplicationClient:负责发送http请求到eureka server
eureka client核心机制:
- eureka client的服务注册,是在InstanceInfoReplicator中的
- 实际发送服务注册请求的是 AbstractJerseyEurekaHttpClient,调用了一个restful接口