Eureka在有虚拟网卡的情况下获取正确的IP
发现问题
最近项目在Eureka注册时,发现一个问题:注册的IP地址不是 192.168.0.XXX 的网络IP,而是另外一个网段的地址,如图
通过 ipconfig 命令查看本机的IP地址发现,该IP是本机虚拟网卡VMnet8的地址。
X:\Users\Keats>ipconfig
Windows IP 配置
以太网适配器 以太网:
连接特定的 DNS 后缀 . . . . . . . :
IPv4 地址 . . . . . . . . . . . . : 192.168.0.234
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.0.1
以太网适配器 VMware Network Adapter VMnet1:
连接特定的 DNS 后缀 . . . . . . . :
IPv4 地址 . . . . . . . . . . . . : 192.168.87.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
IPv4 地址 . . . . . . . . . . . . : 192.168.29.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
问题现象
Eureka管理页面注册列表展示的IP地址非局域网IP地址,是虚拟机的虚拟IP地址
可能引起的问题
多人开发时,同事通过Feign调用接口,无法正确匹配IP地址,从而导致接口调用失败。
尝试解决
通过百度查找,提供了该解决方案:在 yml 文件中添加一下的配置,以达到忽略指定网卡的目的
spring:
cloud:
inetutils:
ignored-interfaces: ## 忽略网卡
- VMware.*
可是当我添加该配置后,发现仍不起作用!页面上显示的instance-id 还是 192.168.29.1:8083 。这就很难受了
刨根问底
于是我想能不能跑一跑Spring的启动代码,看看他到底是怎么取IP的。首先从Eureka自动配置类EurekaClientAutoConfiguration入手
@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
ManagementMetadataProvider managementMetadataProvider) {
String hostname = getProperty("eureka.instance.hostname");
// 是否使用IP地址注册。这里就是从配置文件寻值,没找到就用默认值 false
boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
// 获取配置的IP地址
String ipAddress = getProperty("eureka.instance.ip-address");
instance.setPreferIpAddress(preferIpAddress);
if (StringUtils.hasText(ipAddress)) {
instance.setIpAddress(ipAddress);
}
return instance;
}
这里可以看到,Eureka并没有自己直接去系统获取IP地址,而是通过Spring的InetUtils类的findFirstNonLoopbackHostInfo来设置IP地址
public EurekaInstanceConfigBean(InetUtils inetUtils) {
this.inetUtils = inetUtils;
this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo();
this.ipAddress = this.hostInfo.getIpAddress();
this.hostname = this.hostInfo.getHostname();
}
接着看看findFirstNonLoopbackHostInfo()方法的代码。我在本地Debug跑的时候,项目启动该类会被调用两次,一次没有读取配置文件,项目启动Banner也没有打印,第二次配置文件已经读取。启动日志也打印了一部分。这里原因留个坑
public InetAddress findFirstNonLoopbackAddress() {
InetAddress result = null;
try {
int lowest = Integer.MAX_VALUE;
for (Enumeration<NetworkInterface> nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements();) {
NetworkInterface ifc = nics.nextElement();
// 该网络接口是否启用并正在运行。调用的是native方法
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())) {
// 遍历IP地址
for (Enumeration<InetAddress> addrs = ifc
.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress address = addrs.nextElement();
// 找到 IPV4 且不是回环地址(127.0.0.1) 且是优先选择的地址
if (address instanceof Inet4Address
&& !address.isLoopbackAddress()
&& isPreferredAddress(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 {
return InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
log.warn("Unable to retrieve localhost");
}
return null;
}
从上面的代码及注释可以看到,SpringCloud选择IP的原则是:选择已启动网卡的第一个不在忽略范围且不是回环地址(127.0.0.1)且是优先选择地址的IPV4地址
那么我们想要重定义其所选的IP地址,就需要从忽略范围 和 是否是优先选择地址来做了。
判断是否在忽略范围的代码
由该段代码知,要忽略的网口集合需要从 IgnoredInterfaces 这个属性中获得,那这个属性的值是什么?怎么配置呢?
/** for testing */ boolean ignoreInterface(String interfaceName) {
// 遍历IgnoredInterfaces属性集合,该集合内是忽略的网口名字的正则表达式形式
for (String regex : this.properties.getIgnoredInterfaces()) {
if (interfaceName.matches(regex)) {
log.trace("Ignoring interface: " + interfaceName);
return true;
}
}
return false;
}
判断是否是首选地址的代码
/** for testing */ boolean isPreferredAddress(InetAddress address) {
// 如果配置了仅使用本地接口,则当该InetAddress是本地站点地址时返回
if (this.properties.isUseOnlySiteLocalInterfaces()) {
final boolean siteLocalAddress = address.isSiteLocalAddress();
if (!siteLocalAddress) {
log.trace("Ignoring address: " + address.getHostAddress());
}
return siteLocalAddress;
}
// 如果preferredNetworks列表没有配置,则所有地址返回True
final List<String> preferredNetworks = this.properties.getPreferredNetworks();
if (preferredNetworks.isEmpty()) {
return true;
}
// 如果配置了,则返回符合正则的地址
for (String regex : preferredNetworks) {
final String hostAddress = address.getHostAddress();
if (hostAddress.matches(regex) || hostAddress.startsWith(regex)) {
return true;
}
}
log.trace("Ignoring address: " + address.getHostAddress());
return false;
}
结论
- eureka 显示的 instance-id 有两种值,通过 prefer-ip-address 的值来选择
- ip:端口 true
- hostname 主机名 false(默认)
- 当 prefer-ip-address 的值为 true 时,eureka 取这个值:eureka.client.instance-id
- 配置文件中的: eureka.client.instance-id 。可以配置成自己手写的值,也可以自动获取,通过 ${spring.cloud.client.ip-address}
Eureka在有虚拟网卡的情况下获取正确的IP的更多相关文章
- python未知网卡名情况下获取本机IP
import socket def get_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even ...
- Springboot在有锁的情况下如何正确使用事务
1. 概述 老话说的好:想要赚钱,就去看看有钱人有什么需求,因为有钱人钱多,所以赚的多. 言归正传,在Java项目的研发中,"锁"这个词并不陌生,最经典的使用场景是商品的超卖问题. ...
- Windows下获取本机IP地址方法介绍
Windows下获取本机IP地址方法介绍 if((hostinfo = gethostbyname(name)) != NULL) { #if 1 ; printf("IP COUNT: % ...
- Linux下获取本机IP地址的代码
Linux下获取本机IP地址的代码,返回值即为互联网标准点分格式的字符串. #define ETH_NAME "eth0" //获得本机IP地址 char* GetLocalAdd ...
- 把cookie以json形式返回,用js来set cookie.(解决手机浏览器未知情况下获取不到cookie)
.继上一篇随笔,链接点我,解决手机端cookie的问题. .上次用cookie+redis实现了session,并且手机浏览器可能回传cookies有问题,所以最后用js取出cookie跟在请求的ur ...
- Codeigniter 数据库操作事务情况下获取不到last_insert_id()
开发中,数据库Insert使用了事务,如果 $this->db->insert_id() 放在 $this->db->trans_complete(); 这句语句之后,$thi ...
- rust下获取本机IP
又拾起了rust语言, 想写一点东西玩一玩, 但是发现连一个获取本机IP地址的库都没有, 还得挽起袖子自己撸. https://crates.io/crates/local_ipaddress 没有用 ...
- QT5下获取本机IP地址、计算机名、网络连接名、MAC地址、子网掩码、广播地址
获取主机名称 /* * 名称:get_localmachine_name * 功能:获取本机机器名称 * 参数:no * 返回:QString */ QString CafesClient::get_ ...
- Linux下获取和设置IP
在Linux下获取关于IP和网关的操作:重点是对struct ifreq 的操作. 那么进入目录/usr/include/net/if.h下看查找struct ifreq结构体. /* Interfa ...
随机推荐
- POJ3276 Face The Right Way 开关问题
①每个K从最左边进行考虑 ②f[i]=[i,i+k-1]是否进行反转:1代表是,0代表否 ③∑ (i)(i=i+1-K+1) f[j]=∑ (i-1)(i=i-K+1) f[j]+f[i]-f[i-K ...
- centos7 部署vue项目(前后端分离、nginx)
一.环境准备 1.centos7系统 2.mysql数据库 3.在centos7虚拟机上安装好nginx 二.部署内容准备 1.后端war包 或者可执行jar 因为我这里是spring boot项目. ...
- java基础之异常 · fossi
在开发中,异常处理是一个不可绕开的话题,我们对于异常的处理已经非常熟练了,对于异常本身的概念.用法等不再赘述了,直接结合面试问题来加深对异常的理解吧. Throwable 可以用来表示任何可以作为异常 ...
- [LC] 300. Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence. Example: Inp ...
- 批量修改ACCESS表列名
问题来源:从ODBC导入数据到ACCESS 再从ACCESS导入到SQL数据库,ACCESS会多带个DBO. 所以需要批量修改ACCESS的表名. 首先需要引用ADOX引用方法:打开ACCESS的VB ...
- 搭建harbor企业级私有registry
主机环境要求 硬件Hardware Resource Capacity Description CPU minimal 2 CPU 4 CPU is prefered Mem minimal 4GB ...
- Java IO: RandomAccessFile
原文链接 作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) RandomAccessFile允许你来回读写文件,也可以替换文件中的某些部分.FileIn ...
- js 实现排序算法 -- 选择排序(Selection Sort)
原文: 十大经典排序算法(动图演示) 选择排序(Selection Sort) 选择排序(Selection-sort)是一种简单直观的排序算法.它的工作原理:首先在未排序序列中找到最小(大)元素,存 ...
- js Array 中的 map, filter 和 reduce
原文中部分源码来源于:JS Array.reduce 实现 Array.map 和 Array.filter Array 中的高阶函数 ---- map, filter, reduce map() - ...
- Proto3:C++基本使用
本教程提供protocol buffer在C++程序中的基础用法.通过创建一个简单的示例程序,向你展示如何: 在.proto中定义消息格式 使用protocol buffer编译器 使用C++ pro ...
- python未知网卡名情况下获取本机IP