在上一篇文章中提到 SpringCloud Brixton和Camden.SR7 的Inets的一點小bug
2018-05-27 開發(fā)環(huán)境下加快項目啟動速度-點評CAT使用經(jīng)驗
問題
問題并不是很嚴(yán)重,注冊中心當(dāng)然知道客戶端的IP地址 了,但是注冊的時候绪囱,一般是以IP顯示的切诀。
eureka:
instance:
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
appname: ${spring.application.name}
prefer-ip-address: true
就是這個spring.cloud.client.ipAddress 變量,在SpringCloud Brixton 中房蝉,SpringCloud可能會把不是無線網(wǎng)卡瓤球,也不是本機以太網(wǎng)的網(wǎng)卡地址注冊上去。就是上一篇文章中說到的VMware的兩張網(wǎng)卡會干擾SpringCloud的判斷咱圆。這個版本的顯示的和注冊的地址可能都不是正確的地址。這容易導(dǎo)致功氨,局域網(wǎng)內(nèi)其他人調(diào)用的時候序苏,不小心調(diào)用到你的機器上了,但是這個IP不通捷凄。容易出現(xiàn)說不清楚的網(wǎng)絡(luò)超時問題忱详。注冊中心頁面顯示的IP地址也容易引起誤導(dǎo)。
解決方案
禁用虛擬機網(wǎng)卡
更有意思的方案
喜歡研究技術(shù)的同學(xué)跺涤,可以看看:
虛擬機網(wǎng)卡繼續(xù)開著匈睁,升級到SpringCloud Camden.SR7 以上
加上配置:
spring:
cloud:
inetutils:
ignoredInterfaces: ['VMware.*']
preferredNetworks: ['172.16']
use-only-site-local-interfaces: true
這還不夠:
用下面的方式打印主機地址,觀察是否正確
@Component
@Slf4j
public class SelfCheckProgram implements CommandLineRunner {
@Value("${spring.cloud.client.ipAddress}")
private String ip;
@Value("${spring.cloud.client.hostname}")
private String hostName;
@Override
public void run(String... strings) throws Exception {
log.warn("spring.cloud.client.ipAddress = {}" ,ip);
log.warn("spring.cloud.client.hostname = {}", hostName);
}
}
其實會發(fā)現(xiàn)打印的是正確的桶错。但是實際上還是不太對航唆。
再寫一個隨著系統(tǒng)啟動,觀察IP地址是否正確院刁。
這里觀察inetUtils的配置是否正確:
@Component
@Slf4j
public class InspectEurekaClientIp implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("inetUtilsProperties")) {
InetUtilsProperties inetUtilsProperties = (InetUtilsProperties) bean;
log.warn("ignored interfaces {}", inetUtilsProperties.getIgnoredInterfaces());
log.warn("prefered network {}", inetUtilsProperties.getPreferredNetworks());
return inetUtilsProperties;
}
if (beanName.equals("eurekaInstanceConfigBean")) {
EurekaInstanceConfigBean eurekaInstanceConfigBean = (EurekaInstanceConfigBean) bean;
log.warn("eurekaInstanceConfig Ip: {}", eurekaInstanceConfigBean.getIpAddress());
log.warn("eurekaInstanceConfig IstanceId: {}", eurekaInstanceConfigBean.getInstanceId());
return bean;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
inetsUtilsProperties會打印多次:
其實第一次的配置還是不正確糯钙,沒有注入忽略的網(wǎng)卡配置,但是后面打印得卻是正確的。任岸。==
閱讀源碼其實可以發(fā)現(xiàn):
public class HostInfoEnvironmentPostProcessor
implements EnvironmentPostProcessor, Ordered {
// Before ConfigFileApplicationListener
private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;
@Override
public int getOrder() {
return this.order;
}
.......省略部分方法
private HostInfo getFirstNonLoopbackHostInfo(ConfigurableEnvironment environment) {
InetUtilsProperties target = new InetUtilsProperties();
RelaxedDataBinder binder = new RelaxedDataBinder(target,
InetUtilsProperties.PREFIX);
binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources()));
try (InetUtils utils = new InetUtils(target)) {
return utils.findFirstNonLoopbackHostInfo();
}
}
}
SpringCloud新增了binder.bind再榄,邏輯,然而不幸的是享潜,這個PropertySourcesPropertyValues 拿不到配置文件的配置困鸥。只能通過系統(tǒng)環(huán)境變量。猜測可能是啟動得比較早米碰,還沒來得及讀取application.yml生成PropertySource窝革。
最終解決方案
把上面的類升級成EnvironmnetPostProcessor
META-INF下面新增spring.factories
寫入
org.springframework.boot.env.EnvironmentPostProcessor=\
com.github.slankka.config.InspectEurekaClientIp
InspectEurekaClientIp 多實現(xiàn)一個接口:
@Component
@Slf4j
public class InspectEurekaClientIp implements BeanPostProcessor, EnvironmentPostProcessor, Ordered {
@Override
public int getOrder() {
//這里是依據(jù)HostInfoEnvironmentPostProcessor 的優(yōu)先級,這里讓本類優(yōu)先處理吕座。
return ConfigFileApplicationListener.DEFAULT_ORDER - 2;
}
/**
* 鑒于 HostInfoEnvironmentPostProcessor 處理inetUtilsProperties 時虐译,
* 只從系統(tǒng)環(huán)境變量中取得空的 ignoredInterfaces.
* 這里強制寫入,防止被傳入了錯誤的VMware的網(wǎng)卡的IP地址
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> ignoredInterfaces = Maps.newHashMap();
ignoredInterfaces.put("spring.cloud.inetutils.ignoredInterfaces", Lists.newArrayList("VMware.*"));
SystemEnvironmentPropertySource systemEnvironmentPropertySource = new SystemEnvironmentPropertySource("systemEnvironment", ignoredInterfaces);
environment.getPropertySources().addLast(systemEnvironmentPropertySource);
}
}
//其余內(nèi)容和上面的相同吴趴,此處省略
其實還有更簡單的方案
相比上面的方案而言漆诽,每一個人都需要自己配置,那就是在環(huán)境變量里面添加锣枝,想想就覺得很丑厢拭,這么長,容易忘加撇叁。但確保至少是Spring-cloud Camden.SR7版本或以上供鸠。
總結(jié)
Spring框架之所以這么受歡迎,就是因為他的容器管理陨闹,IOC楞捂,等等技術(shù),使得應(yīng)用可以方便的做任何自定義修改趋厉,而且Spring是盡量避免硬編碼的啟動某個東西寨闹,追求注解自動化。
十分優(yōu)秀君账。