Spring Cloud配置use-only-site-local-interfaces及ignored-interfaces不起作用的解决方法

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启动前就加载的.

你可能感兴趣的:(Spring Cloud配置use-only-site-local-interfaces及ignored-interfaces不起作用的解决方法)