Eureka 學習筆記(七)EurekaServerContext初始化(4)- ApplicationInfoManager創建過程

零、前言

    有幾個步驟比較簡單,且和主流程關係不大,就一起看了。

一、註冊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創建需要兩個參數,分別是EurekaInstanceConfigInstanceInfo,一個是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構造函數本身是沒有什麼邏輯的,就是把EurekaInstanceConfigInstanceInfo還有其他信息保存了一下,那麼我們回過頭看一下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的實例信息,作爲後續連接使用。繼續畫個圖總結一下~ 在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章