SpringCloud學習筆記(四)-InstanceId的生成

Eureka Client啟動時撩穿,會根據application.yml屬性信息初始化配置。配置入口可查看EurekaClientAutoConfiguration類。如果指定了eureka.instance.instance-id,就使用指定的參數作為InstanceId饱普,如果沒有指定运挫,會調用IdUtils這個工具類生成缺省的InstanceId状共。另外如果配置了eureka.instance.prefer-ip-address,那么客戶端注冊到注冊中心時將決定是否采用ip來注冊, 如果為true將用eureka.instance.ip-address指定的IP地址注冊谁帕。

eureka.instance后跟參數的寫法實際是比較靈活的峡继,spring會容錯多種形式,例如:
0 = "preferIpAddress"
1 = "prefer_ip_address"
2 = "prefer-ip-address"
3 = "preferipaddress"
4 = "PREFERIPADDRESS"
5 = "PREFER_IP_ADDRESS"
6 = "PREFER-IP-ADDRESS"

EurekaClient的注冊過程可以看另一篇SpringCloud學習筆記(一)-EurekaClient注冊過程

入口EurekaClientAutoConfiguration類路徑
spring-cloud-netflix-eureka-client-xx.jar/org.springframework.cloud.netflix.eureka/EurekaClientAutoConfiguration

public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
                                                             ManagementMetadataProvider managementMetadataProvider) throws MalformedURLException {
        PropertyResolver eurekaPropertyResolver = new RelaxedPropertyResolver(this.env, "eureka.instance.");
        String hostname = eurekaPropertyResolver.getProperty("hostname");
        //對應eureka.instance.prefer-ip-address配置
        boolean preferIpAddress = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("preferIpAddress"));
        boolean isSecurePortEnabled = Boolean.parseBoolean(eurekaPropertyResolver.getProperty("securePortEnabled"));
        String serverContextPath = propertyResolver.getProperty("server.contextPath", "/");
        int serverPort = Integer.valueOf(propertyResolver.getProperty("server.port", propertyResolver.getProperty("port", "8080")));

        Integer managementPort = propertyResolver.getProperty("management.port", Integer.class);// nullable. should be wrapped into optional
        String managementContextPath = propertyResolver.getProperty("management.contextPath");// nullable. should be wrapped into optional
        Integer jmxPort = propertyResolver.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable
        EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);

        instance.setNonSecurePort(serverPort);
        //設置缺省的InstanceId
        instance.setInstanceId(getDefaultInstanceId(propertyResolver));
        instance.setPreferIpAddress(preferIpAddress);

        if(isSecurePortEnabled) {
            instance.setSecurePort(serverPort);
        }

        if (StringUtils.hasText(hostname)) {
            instance.setHostname(hostname);
        }
        String statusPageUrlPath = eurekaPropertyResolver.getProperty("statusPageUrlPath");
        String healthCheckUrlPath = eurekaPropertyResolver.getProperty("healthCheckUrlPath");

        if (StringUtils.hasText(statusPageUrlPath)) {
            instance.setStatusPageUrlPath(statusPageUrlPath);
        }
        if (StringUtils.hasText(healthCheckUrlPath)) {
            instance.setHealthCheckUrlPath(healthCheckUrlPath);
        }

        ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort,
                serverContextPath, managementContextPath, managementPort);

        if(metadata != null) {
            instance.setStatusPageUrl(metadata.getStatusPageUrl());
            instance.setHealthCheckUrl(metadata.getHealthCheckUrl());
            Map<String, String> metadataMap = instance.getMetadataMap();
            if (metadataMap.get("management.port") == null) {
                metadataMap.put("management.port", String.valueOf(metadata.getManagementPort()));
            }
        }

        setupJmxPort(instance, jmxPort);
        return instance;
}

可以看到缺省InsanceId的生成是調用IdUtils這個工具類
類路徑
spring-cloud-commons-1.3.0.RELAEASE.jar/org.springframework.cloud.commons.util.IdUtils

public static String getDefaultInstanceId(PropertyResolver resolver) {
        RelaxedPropertyResolver relaxed = new RelaxedPropertyResolver(resolver);
        String vcapInstanceId = relaxed.getProperty("vcap.application.instance_id");
        if (StringUtils.hasText(vcapInstanceId)) {
            return vcapInstanceId;
        }

        String hostname = relaxed.getProperty("spring.cloud.client.hostname");
        String appName = relaxed.getProperty("spring.application.name");

        String namePart = combineParts(hostname, SEPARATOR, appName);
        //如果沒有配置spring.application.instance_id,indexPart默認為server.port
        String indexPart = relaxed.getProperty("spring.application.instance_id",
                relaxed.getProperty("server.port"));

        return combineParts(namePart, SEPARATOR, indexPart);
}

從源碼可以看出匈挖,這個方法作用主要是根據hostname碾牌、appName、端口等信息拼裝一個缺省的instanceId儡循。如果只配置了eureka.instance.prefer-ip-address舶吗,而沒有配置spring.application.instance_id,那么instanceId依然顯示hostname择膝,但是通過ip可以訪問誓琼。

最后返回結果


由此可見,instanceId的默認值是
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
根據個人對源碼的理解肴捉,以及實際測試腹侣,如果配置了spring.application.instance_id,會取代server.port齿穗,并不是拼接的方式(也可能是本人對源碼理解還不夠全面傲隶,如果有錯誤之處,還請不吝指教)窃页。在eureka監(jiān)控頁面可以看到instanceId跺株。

那么spring.cloud.client.hostname又是如何獲取的呢复濒?HostInfoEnvironmentPostProcessor類中可以看到對hostName和ipAddress的設置。

public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        InetUtils.HostInfo hostInfo = getFirstNonLoopbackHostInfo(environment);
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        map.put("spring.cloud.client.hostname", hostInfo.getHostname());
        map.put("spring.cloud.client.ipAddress", hostInfo.getIpAddress());
        MapPropertySource propertySource = new MapPropertySource(
                "springCloudClientHostInfo", map);
        environment.getPropertySources().addLast(propertySource);
}

以上代碼中根據InetUtils工具類提供的方法查找第一個非回送主機信息
類路徑:
spring-cloud-commons-1.3.0.RELAEASE.jar/org.springframework.cloud.commons.util.InetUtils

public HostInfo findFirstNonLoopbackHostInfo() {
        //查找合適的網絡地址信息
        InetAddress address = findFirstNonLoopbackAddress();
        if (address != null) {
            //組裝成HostInfo對象乒省,主要是主機名和IP地址
            return convertAddress(address);
        }
        //獲取的網絡信息為空就用默認的
        HostInfo hostInfo = new HostInfo();
        hostInfo.setHostname(this.properties.getDefaultHostname());
        hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
        return hostInfo;
}

接著看findFirstNonLoopbackAddress是如何查找合適的網絡接口地址的芝薇,包含了對多網卡情況下的網絡地址的選擇。

//根據InetUtils工具類提供的方法查找第一個非回送地址
public InetAddress findFirstNonLoopbackAddress() {
        InetAddress result = null;
        try {
            int lowest = Integer.MAX_VALUE;
            //遍歷所有網絡接口
            for (Enumeration<NetworkInterface> nics = NetworkInterface
                    .getNetworkInterfaces(); nics.hasMoreElements();) {
                NetworkInterface ifc = nics.nextElement();
                //判斷網絡接口是否已啟動并正在運行
                if (ifc.isUp()) {
                    log.trace("Testing interface: " + ifc.getDisplayName());
                    //獲取該網絡接口的索引
                    if (ifc.getIndex() < lowest || result == null) {
                        lowest = ifc.getIndex();
                    }
                    else if (result != null) {
                        continue;
                    }

                    // @formatter:off
                    //判斷該網絡接口是否是被忽略的
                    if (!ignoreInterface(ifc.getDisplayName())) {
                        //獲取綁定到此網絡接口的InetAddress集合
                        for (Enumeration<InetAddress> addrs = ifc
                                .getInetAddresses(); addrs.hasMoreElements();) {
                            InetAddress address = addrs.nextElement();
                            //判斷是否是IPV4作儿,并且不是回送地址洛二,并且不是被忽略的地址
                            if (address instanceof Inet4Address
                                    && !address.isLoopbackAddress()
                                    && !ignoreAddress(address)) {
                                log.trace("Found non-loopback interface: "
                                        + ifc.getDisplayName());
                                result = address;
                            }
                        }
                    }
                    // @formatter:on
                }
            }
        }
        catch (IOException ex) {
            log.error("Cannot get first non-loopback address", ex);
        }

        if (result != null) {
            return result;
        }

        try {
            //如果沒有找到合適的網絡接口,使用JDK自帶的getLocalHost返回本機地址攻锰,
            //也就是本機配置的hostname及/etc/hosts配置的映射地址
            return InetAddress.getLocalHost();
        }
        catch (UnknownHostException e) {
            log.warn("Unable to retrieve localhost");
        }

        return null;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末晾嘶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子娶吞,更是在濱河造成了極大的恐慌垒迂,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妒蛇,死亡現(xiàn)場離奇詭異机断,居然都是意外死亡,警方通過查閱死者的電腦和手機绣夺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門吏奸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陶耍,你說我怎么就攤上這事奋蔚。” “怎么了烈钞?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵泊碑,是天一觀的道長。 經常有香客問我毯欣,道長馒过,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任酗钞,我火速辦了婚禮腹忽,結果婚禮上,老公的妹妹穿的比我還像新娘算吩。我一直安慰自己留凭,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布偎巢。 她就那樣靜靜地躺著蔼夜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪压昼。 梳的紋絲不亂的頭發(fā)上求冷,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天瘤运,我揣著相機與錄音,去河邊找鬼匠题。 笑死拯坟,一個胖子當著我的面吹牛,可吹牛的內容都是我干的韭山。 我是一名探鬼主播郁季,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钱磅!你這毒婦竟也來了梦裂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盖淡,失蹤者是張志新(化名)和其女友劉穎年柠,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體褪迟,經...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡冗恨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了味赃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掀抹。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洁桌,靈堂內的尸體忽然破棺而出渴丸,到底是詐尸還是另有隱情,我是刑警寧澤另凌,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站戒幔,受9級特大地震影響吠谢,放射性物質發(fā)生泄漏。R本人自食惡果不足惜诗茎,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一工坊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敢订,春花似錦王污、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矾柜,卻和暖如春阱驾,著一層夾襖步出監(jiān)牢的瞬間就谜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工里覆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留丧荐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓喧枷,卻偏偏與公主長得像虹统,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子隧甚,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355