零、前言
有几个步骤比较简单,且和主流程关系不大,就一起看了。
一、注册JSON、XML数据流转换器
这里的代码很简单,就是创建了两个转换器,然后保存到了一个底层的PrioritizedList
数据结构中。
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
public void registerConverter(Converter converter, int priority) {
if (converterRegistry != null) {
converterRegistry.registerConverter(converter, priority);
}
}
private final PrioritizedList converters = new PrioritizedList();
public void registerConverter(Converter converter, int priority) {
typeToConverterMap.clear();
converters.add(converter, priority);
}
二、创建ServerCodecs
这里其实就是创建了一个编码解码器。简单看看就好~
ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);
@Inject
public DefaultServerCodecs(EurekaServerConfig serverConfig) {
this (
getFullJson(serverConfig),
CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class),
getFullXml(serverConfig),
CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class)
);
}
protected DefaultServerCodecs(CodecWrapper fullJsonCodec,
CodecWrapper compactJsonCodec,
CodecWrapper fullXmlCodec,
CodecWrapper compactXmlCodec) {
this.fullJsonCodec = fullJsonCodec;
this.compactJsonCodec = compactJsonCodec;
this.fullXmlCodec = fullXmlCodec;
this.compactXmlCodec = compactXmlCodec;
}
三、创建ApplicationInfoManager
ApplicationInfoManager就是当前Eureka实例信息的管理器,我们来看看它的逻辑~
ApplicationInfoManager applicationInfoManager = null;
if (eurekaClient == null) {
EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
? new CloudInstanceConfig()
: new MyDataCenterInstanceConfig();
applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
} else {
applicationInfoManager = eurekaClient.getApplicationInfoManager();
}
首先它判断了一下eurekaClient
是否存在,如果存在就从eurekaClient
中获取,不存在就创建一个,并且创建一个eurekaClient
,再把applicationInfoManager
设置到eurekaClient
里面。这里一开始默认是没有的,所以要先创建。
第一步:它根据Eureka是否部署在云上创建不同的EurekaInstanceConfig
实例。这里要吐槽一下,Eureka貌似很多地方都判断了它自己是否部署在亚马逊的AWS云上- -! 但这两个实现类都继承自PropertiesInstanceConfig
,所以这里就不纠结细节了。而且根据笔者一层一层找上去,他最终就是读取了Eureka-Client.properties文件里面的配置,自己封装了一下,这里有兴趣得到小伙伴自己看看~ 需要注意的是这里读取的是client的配置信息,那么可以得知applicationInfoManager
组装的是Client端的信息,进而可以证明EurekaServer端也是一个EurekaClient端,需要把自己的节点信息同步给其他EurekaServer节点。
第二步:这里创建了一个ApplicationInfoManager
,我们可以看到ApplicationInfoManager
创建需要两个参数,分别是EurekaInstanceConfig
和InstanceInfo
,一个是Client端配置信息,一个是Client节点本身的信息。
public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo) {
this(config, instanceInfo, null);
}
public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo, OptionalArgs optionalArgs) {
this.config = config;
this.instanceInfo = instanceInfo;
this.listeners = new ConcurrentHashMap<String, StatusChangeListener>();
if (optionalArgs != null) {
this.instanceStatusMapper = optionalArgs.getInstanceStatusMapper();
} else {
this.instanceStatusMapper = NO_OP_MAPPER;
}
// Hack to allow for getInstance() to use the DI'd ApplicationInfoManager
instance = this;
}
这里我们可以看到,ApplicationInfoManager
构造函数本身是没有什么逻辑的,就是把EurekaInstanceConfig
和InstanceInfo
还有其他信息保存了一下,那么我们回过头看一下InstanceInfo
是如何被创建的new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get()
。
// 这里没什么东西,我们看一下get方法
public EurekaConfigBasedInstanceInfoProvider(EurekaInstanceConfig config) {
this.config = config;
}
public synchronized InstanceInfo get() {
if (instanceInfo == null) {
// Build the lease information to be passed to the server based on config
LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
if (vipAddressResolver == null) {
vipAddressResolver = new Archaius1VipAddressResolver();
}
// Builder the instance information to be registered with eureka server
InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(vipAddressResolver);
// set the appropriate id for the InstanceInfo, falling back to datacenter Id if applicable, else hostname
String instanceId = config.getInstanceId();
if (instanceId == null || instanceId.isEmpty()) {
DataCenterInfo dataCenterInfo = config.getDataCenterInfo();
if (dataCenterInfo instanceof UniqueIdentifier) {
instanceId = ((UniqueIdentifier) dataCenterInfo).getId();
} else {
instanceId = config.getHostName(false);
}
}
String defaultAddress;
if (config instanceof RefreshableInstanceConfig) {
// Refresh AWS data center info, and return up to date address
defaultAddress = ((RefreshableInstanceConfig) config).resolveDefaultAddress(false);
} else {
defaultAddress = config.getHostName(false);
}
// fail safe
if (defaultAddress == null || defaultAddress.isEmpty()) {
defaultAddress = config.getIpAddress();
}
builder.setNamespace(config.getNamespace())
.setInstanceId(instanceId)
.setAppName(config.getAppname())
.setAppGroupName(config.getAppGroupName())
.setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress())
.setHostName(defaultAddress)
.setPort(config.getNonSecurePort())
.enablePort(PortType.UNSECURE, config.isNonSecurePortEnabled())
.setSecurePort(config.getSecurePort())
.enablePort(PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName())
.setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
.setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl())
.setASGName(config.getASGName())
.setHealthCheckUrls(config.getHealthCheckUrlPath(),
config.getHealthCheckUrl(), config.getSecureHealthCheckUrl());
// Start off with the STARTING state to avoid traffic
if (!config.isInstanceEnabledOnit()) {
InstanceStatus initialStatus = InstanceStatus.STARTING;
LOG.info("Setting initial instance status as: {}", initialStatus);
builder.setStatus(initialStatus);
} else {
LOG.info("Setting initial instance status as: {}. This may be too early for the instance to advertise "
+ "itself as available. You would instead want to control this via a healthcheck handler.",
InstanceStatus.UP);
}
// Add any user-specific metadata information
for (Map.Entry<String, String> mapEntry : config.getMetadataMap().entrySet()) {
String key = mapEntry.getKey();
String value = mapEntry.getValue();
// only add the metadata if the value is present
if (value != null && !value.isEmpty()) {
builder.add(key, value);
}
}
instanceInfo = builder.build();
instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
}
return instanceInfo;
}
我们可以看到在get方法中,Eureka使用了构造器模式创建了一个InstanceInfo
实例,它在中间设置了InstanceId、AppName、IPAddr等一系列信息,为连接EurekaServer做准备。这里大家可以学习一下,当一个类需要配置大量参数的时候,可以使用构造器模式,减少大量的setXXX代码。
四、总结
那么到这里ApplicationInfoManager
就创建完毕了,它本质上封装了EurekaClient的配置信息和Client连接Server的实例信息,作为后续连接使用。继续画个图总结一下~