Spring Cloud Application往Eureka注册时, 当服务器安装了多块网卡我们希望控制注册的ip是内网ip address而非公网ip或者loopback address. 查阅Service Discovery源码:
Service Registry入口:
EurekaServiceRegistry.java
public void register(EurekaRegistration reg) {
maybeInitializeClient(reg);
if (log.isInfoEnabled()) {
log.info("Registering application " + reg.getInstanceConfig().getAppname()
+ " with eureka with status "
+ reg.getInstanceConfig().getInitialStatus());
}
reg.getApplicationInfoManager()
.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
if (reg.getHealthCheckHandler() != null) {
reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler());
}
}
以上代码可以发现,服务注册信息全都通过EurekaRegistration获取,继续:
EurekaRegistration.java追踪进去会发现这是一个构造器, 调用者为EurekaClientAutoConfiguration. 代码就不贴了. 查看
EurekaClientAutoConfiguration.java
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
}
在这里注册的Spring Bean, 依赖了CloudEurekaInstanceConfig, 找到他:
CloudEurekaInstanceConfig.java
@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
ManagementMetadataProvider managementMetadataProvider) throws MalformedURLException {
// 省略无关代码
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(serverPort);
instance.setInstanceId(getDefaultInstanceId(propertyResolver));
instance.setPreferIpAddress(preferIpAddress);
return instance;
}
到这里,我们就发现了Host相关的核心类:InetUtils, 接下来就简单了, 很容易发现里面的ip address过滤逻辑:
InetUtils.java
boolean ignoreInterface(String interfaceName)
boolean ignoreAddress(InetAddress address)
判断是基于InetUtils.properties: InetUtilsProperties
跟进去,找到了本关大魔王:
InetUtilsProperties.java
public static final String PREFIX = "spring.cloud.inetutils";
于是添加配置:
application.yml
spring:
cloud:
inetutils:
use-only-site-local-interfaces: true
ignored-interfaces:teredo
关于use-only-site-local-interfaces的具体说明:https://stackoverflow.com/questions/5619345/what-does-inetaddress-issitelocaladdress-actually-mean
然而加上配置后发现并没有生效, 配置项没有被读取, 继续看代码, 原来在InetUtils里是这样初始化的:
private static final InetUtils instance = new InetUtils(new InetUtilsProperties());
难怪配置没用. 这下就好解决了, 自己重新注册一遍把InetUtilsProperties注册进去就ok
@Bean
@Inject
public InetUtils inetUtils(InetUtilsProperties inetUtilsProperties){
return new InetUtils(inetUtilsProperties);
}
收功!
补充:
后来发现会出现注册上的instance id和实际provider的ip:port不一致, 原因是InetUtils获取ip的时候会被调用多次, 时机还不一样.
最先的调用是在HostInfoEnvironmentPostProcessor, 而这个类执行的时候还没加载application.yml, 因此配置并不生效. 因此我们把配置拿出来放到bootstrap.yml里就可以了.它是在SpringBoot启动前就加载的.