零、前言
有幾個步驟比較簡單,且和主流程關係不大,就一起看了。
一、註冊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的實例信息,作爲後續連接使用。繼續畫個圖總結一下~