4. 上接Jersey1TransportClientFactories#newTransportClientFactory,构造收集监控信息的工厂metricsFactory
public static TransportClientFactory createFactory(final TransportClientFactory delegateFactory) {
final Map<RequestType, EurekaHttpClientRequestMetrics> metricsByRequestType = initializeMetrics();
final ExceptionsMetric exceptionMetrics = new ExceptionsMetric(EurekaClientNames.METRIC_TRANSPORT_PREFIX + "exceptions");
return new TransportClientFactory() {
@Override
public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
return new MetricsCollectingEurekaHttpClient(
delegateFactory.newClient(endpoint),
metricsByRequestType,
exceptionMetrics,
false
);
}
@Override
public void shutdown() {
shutdownMetrics(metricsByRequestType);
exceptionMetrics.shutdown();
}
};
}
初始化各种请求类型指标,统计次数以及时间等数据维度
private static Map<RequestType, EurekaHttpClientRequestMetrics> initializeMetrics() {
Map<RequestType, EurekaHttpClientRequestMetrics> result = new EnumMap<>(RequestType.class);
try {
for (RequestType requestType : RequestType.values()) {
result.put(requestType, new EurekaHttpClientRequestMetrics(requestType.name()));
}
} catch (Exception e) {
logger.warn("Metrics initialization failure", e);
}
return result;
}
EurekaHttpClientRequestMetrics(String resourceName) {
this.countersByStatus = createStatusCounters(resourceName);
latencyTimer = new BasicTimer(
MonitorConfig.builder(EurekaClientNames.METRIC_TRANSPORT_PREFIX + "latency")
.withTag("id", resourceName)
.withTag("class", MetricsCollectingEurekaHttpClient.class.getSimpleName())
.build(),
TimeUnit.MILLISECONDS
);
ServoUtil.register(latencyTimer);
this.connectionErrors = new BasicCounter(
MonitorConfig.builder(EurekaClientNames.METRIC_TRANSPORT_PREFIX + "connectionErrors")
.withTag("id", resourceName)
.withTag("class", MetricsCollectingEurekaHttpClient.class.getSimpleName())
.build()
);
ServoUtil.register(connectionErrors);
}
private static Map<Status, Counter> createStatusCounters(String resourceName) {
Map<Status, Counter> result = new EnumMap<>(Status.class);
for (Status status : Status.values()) {
BasicCounter counter = new BasicCounter(
MonitorConfig.builder(EurekaClientNames.METRIC_TRANSPORT_PREFIX + "request")
.withTag("id", resourceName)
.withTag("class", MetricsCollectingEurekaHttpClient.class.getSimpleName())
.withTag("status", status.name())
.build()
);
ServoUtil.register(counter);
result.put(status, counter);
}
return result;
}
统计异常信息指标,最后包装代理生成MetricsCollectingEurekaHttpClient统一包装管理,关于网络交互以及指标收集的工厂eurekaTransport.transportClientFactory构造完成。 构建获取应用服务信息的对象
ApplicationsResolver.ApplicationsSource applicationsSource = new ApplicationsResolver.ApplicationsSource() {
@Override
public Applications getApplications(int stalenessThreshold, TimeUnit timeUnit) {
long thresholdInMs = TimeUnit.MILLISECONDS.convert(stalenessThreshold, timeUnit);
long delay = getLastSuccessfulRegistryFetchTimePeriod();
if (delay > thresholdInMs) {
logger.info("Local registry is too stale for local lookup. Threshold:{}, actual:{}",
thresholdInMs, delay);
return null;
} else {
return localRegionApps.get();
}
}
};
5.构建eurekaTransport.bootstrapResolver,EurekaHttpClients#newBootstrapResolver,根据是否是混合型策略构建不同解析器
public static ClosableResolver<AwsEndpoint> newBootstrapResolver(
final EurekaClientConfig clientConfig,
final EurekaTransportConfig transportConfig,
final TransportClientFactory transportClientFactory,
final InstanceInfo myInstanceInfo,
final ApplicationsResolver.ApplicationsSource applicationsSource)
{
if (COMPOSITE_BOOTSTRAP_STRATEGY.equals(transportConfig.getBootstrapResolverStrategy())) {
if (clientConfig.shouldFetchRegistry()) {
return compositeBootstrapResolver(
clientConfig,
transportConfig,
transportClientFactory,
myInstanceInfo,
applicationsSource
);
} else {
logger.warn("Cannot create a composite bootstrap resolver if registry fetch is disabled." +
" Falling back to using a default bootstrap resolver.");
}
}
// if all else fails, return the default
return defaultBootstrapResolver(clientConfig, myInstanceInfo);
}
这是初始化默认的解析器。获取可用区域,筛选出本服务所在区域myZone
static ClosableResolver<AwsEndpoint> defaultBootstrapResolver(final EurekaClientConfig clientConfig,
final InstanceInfo myInstanceInfo) {
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
String myZone = InstanceInfo.getZone(availZones, myInstanceInfo);
ClusterResolver<AwsEndpoint> delegateResolver = new ZoneAffinityClusterResolver(
new ConfigClusterResolver(clientConfig, myInstanceInfo),
myZone,
true
);
List<AwsEndpoint> initialValue = delegateResolver.getClusterEndpoints();
if (initialValue.isEmpty()) {
String msg = "Initial resolution of Eureka server endpoints failed. Check ConfigClusterResolver logs for more info";
logger.error(msg);
failFastOnInitCheck(clientConfig, msg);
}
return new AsyncResolver<>(
EurekaClientNames.BOOTSTRAP,
delegateResolver,
initialValue,
1,
clientConfig.getEurekaServiceUrlPollIntervalSeconds() * 1000
);
}
构建区域集群解析器ZoneAffinityClusterResolver并从其中获取AwsEndpoint
public List<AwsEndpoint> getClusterEndpoints() {
List<AwsEndpoint>[] parts = ResolverUtils.splitByZone(delegate.getClusterEndpoints(), myZone);
List<AwsEndpoint> myZoneEndpoints = parts[0];
List<AwsEndpoint> remainingEndpoints = parts[1];
List<AwsEndpoint> randomizedList = randomizeAndMerge(myZoneEndpoints, remainingEndpoints);
if (!zoneAffinity) {
Collections.reverse(randomizedList);
}
logger.debug("Local zone={}; resolved to: {}", myZone, randomizedList);
return randomizedList;
}
根据配置clientConfig.shouldUseDnsForFetchingServiceUrls()从配置中获取
private List<AwsEndpoint> getClusterEndpointsFromConfig() {
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
String myZone = InstanceInfo.getZone(availZones, myInstanceInfo);
Map<String, List<String>> serviceUrls = EndpointUtils
.getServiceUrlsMapFromConfig(clientConfig, myZone, clientConfig.shouldPreferSameZoneEureka());
List<AwsEndpoint> endpoints = new ArrayList<>();
for (String zone : serviceUrls.keySet()) {
for (String url : serviceUrls.get(zone)) {
try {
endpoints.add(new AwsEndpoint(url, getRegion(), zone));
} catch (Exception ignore) {
logger.warn("Invalid eureka server URI: {}; removing from the server pool", url);
}
}
}
logger.debug("Config resolved to {}", endpoints);
if (endpoints.isEmpty()) {
logger.error("Cannot resolve to any endpoints from provided configuration: {}", serviceUrls);
}
return endpoints;
}
根据区域从配置中获取服务连接,最后包装成AwsEndpoint返回,最后根据是否本区域把AwsEndpoint分成两批,最后打乱顺序后返回。
public static Map<String, List<String>> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
Map<String, List<String>> orderedUrls = new LinkedHashMap<>();
String region = getRegion(clientConfig);
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
if (availZones == null || availZones.length == 0) {
availZones = new String[1];
availZones[0] = DEFAULT_ZONE;
}
logger.debug("The availability zone for the given region {} are {}", region, availZones);
int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
String zone = availZones[myZoneOffset];
List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
if (serviceUrls != null) {
orderedUrls.put(zone, serviceUrls);
}
int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
while (currentOffset != myZoneOffset) {
zone = availZones[currentOffset];
serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
if (serviceUrls != null) {
orderedUrls.put(zone, serviceUrls);
}
if (currentOffset == (availZones.length - 1)) {
currentOffset = 0;
} else {
currentOffset++;
}
}
if (orderedUrls.size() < 1) {
throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
}
return orderedUrls;
}
组装AsyncResolver异步解析器,组件监督任务TimedSupervisorTask,定时上报监控数据
AsyncResolver(String name,
ClusterResolver<T> delegate,
List<T> initialValue,
int executorThreadPoolSize,
int refreshIntervalMs,
int warmUpTimeoutMs) {
this.name = name;
this.delegate = delegate;
this.refreshIntervalMs = refreshIntervalMs;
this.warmUpTimeoutMs = warmUpTimeoutMs;
this.executorService = Executors.newScheduledThreadPool(1,
new ThreadFactoryBuilder()
.setNameFormat("AsyncResolver-" + name + "-%d")
.setDaemon(true)
.build());
this.threadPoolExecutor = new ThreadPoolExecutor(
1, executorThreadPoolSize, 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), // use direct handoff
new ThreadFactoryBuilder()
.setNameFormat("AsyncResolver-" + name + "-executor-%d")
.setDaemon(true)
.build()
);
this.backgroundTask = new TimedSupervisorTask(
this.getClass().getSimpleName(),
executorService,
threadPoolExecutor,
refreshIntervalMs,
TimeUnit.MILLISECONDS,
5,
updateTask
);
this.resultsRef = new AtomicReference<>(initialValue);
Monitors.registerObject(name, this);
}