Eureka之IpAddress:eureka.instance.prefer-ip-address = true

阅读本文你将了解

  • 微服务注册到Eureka Server上的粗粒度过程
  • eureka.instance.prefer-ip-address = true 时,发生的一些事
  • 深度理解eureka.instance.ip-address 和eureka.instance.prefer-ip-address = true 。
  • 杂谈

注:本篇较长、烧脑,并且涉及的范围相对广,建议选择一个舒服的姿势阅读。

分析,eureka.instance.prefer-ip-address

  • 本节解释为什么配置eureka.instance.prefer-ip-address = true时,注册到Eureka Server上的是IP,以及是什么IP

老套路,定位问题从配置开始。

(1) 我们通过eureka.instance.prefer-ip-address 配置项,可以找到源码

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.preferIpAddress

(2) preferIpAddress被哪里调用,可以找到

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.getHostName(boolean)

代码如下:

@Override
public String getHostName(boolean refresh) {
    if (refresh && !this.hostInfo.override) {
        this.ipAddress = this.hostInfo.getIpAddress();
        this.hostname = this.hostInfo.getHostname();
    }
    return this.preferIpAddress ? this.ipAddress : this.hostname;
}

从这里我们可以知道,为什么配置eureka.instance.prefer-ip-address = true 就可以将IP注册到Eureka Server上,而如果不配置就是机器的主机名。

我们看到以上代码有个hostInfo,这是在哪里实例化的呢?

(3) hostInfo在

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

的构造方法中实例化!我们来阅读该类的构造方法:

public EurekaInstanceConfigBean(InetUtils inetUtils) {
    this.inetUtils = inetUtils;
    this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo();
    this.ipAddress = this.hostInfo.getIpAddress();
    this.hostname = this.hostInfo.getHostname();
}

从中 可以看到,hostInfo是调用了

this.inetUtils.findFirstNonLoopbackHostInfo();

从中可以看到,原来hostInfo是调用了

org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackHostInfo()

(4) 阅读

org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackHostInfo()

可以看到以下代码:

public HostInfo findFirstNonLoopbackHostInfo() {
    InetAddress address = findFirstNonLoopbackAddress();
    if (address != null) {
        return convertAddress(address);
    }
    HostInfo hostInfo = new HostInfo();
    hostInfo.setHostname(this.properties.getDefaultHostname());
    hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
    return hostInfo;
}

我们再看一下该类的

org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackAddress()

方法:

 

Eureka Client在com.netflix.appinfo包下的InstanceInfo类封装了本机信息,其中就包括了IP地址。

在 Spring Cloud 环境下,Eureka Client并没有自己实现探测本机IP的逻辑,而是交给Spring的InetUtils工具类的findFirstNonLoopbackAddress()方法完成的具体如下:

public InetAddress findFirstNonLoopbackAddress() {
   InetAddress result = null;
   try {
      // 记录网卡最小索引
      int lowest = Integer.MAX_VALUE;
      // 获取所有网卡
      for (Enumeration 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())) {
               for (Enumeration addrs = ifc
                     .getInetAddresses(); addrs.hasMoreElements();) {
                  InetAddress address = addrs.nextElement();
                  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的InetAddress.getLocalhost()
      return InetAddress.getLocalHost(); 
   }
   catch (UnknownHostException e) {
      log.warn("Unable to retrieve localhost");
   }

   return null;
}

通过源码可以看出,该工具类会获取所有网卡,依次进行遍历,取ip地址合理、索引值最小且不在忽略列表的网卡的ip地址作为结果。如果仍然没有找到合适的IP, 那么就将InetAddress.getLocalHost()做为最后的fallback方案。

至此,终于找到了获得IP的详细方法,原来只需要配置eureka.instance.prefer-ip-address = true,Spring就会自动为我们获取第一个非回环IP地址(这只是简单的说法,事实上这段代码有些容错的处理)。代码虽然长,但是很清晰。不做赘述。

eureka.instance.ip-address和eureka.instance.prefer-ip-address = true同时设置,会用自动获取的ip还是手动设置的?

上文是讨论设置eureka.instance.prefer-ip-address = true ,但没有指定eureka.instance.ip-address的情况。

那么如果两者都被指定了,Spring会怎么处理呢?

是使用eureka.instance.ip-address手动设置的IP,还是用上面自动获取的IP呢?

答案是听eureka.instance.ip-address的。

原因是:在

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.setIpAddress(String)

中:

public void setIpAddress(String ipAddress) {
    this.ipAddress = ipAddress;
    this.hostInfo.override = true;
}

这边设置了this.hostInfo.override,因此会导致getHostName方法不会进if语句,直接返回this.ipAddress。

@Override
public String getHostName(boolean refresh) {
    if (refresh && !this.hostInfo.override) {
        this.ipAddress = this.hostInfo.getIpAddress();
        this.hostname = this.hostInfo.getHostname();
    }
    return this.preferIpAddress ? this.ipAddress : this.hostname;
}

B.T.W

回到上文的

org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackAddress()

方法,上面有很多ignoreXXX的代码。那么,如何配置想要忽略的网卡或者IP地址呢?

答案非常简单,详见Spring Cloud官方文档:

http://cloud.spring.io/spring-cloud-static/Camden.SR3/#ignore-network-interfaces

当然了,这些配置的本意并不是用来注册到Eureka上的,而是用作其他用途,只不过如果没有设置eureka.instance.ip-address时,这个IP就是注册到Eureka Server上的IP。

我们可以在应用的/env端点看到Spring为我们挑选的IP:

springCloudClientHostInfo: {
  spring.cloud.client.hostname: "itmuch",
  spring.cloud.client.ipAddress: "192.168.0.59"
},

 

你可能感兴趣的:(Eureka之IpAddress:eureka.instance.prefer-ip-address = true)