背景
背景如上一篇文章《request.getRequestDispatcher().forward()的妙用以及DispatcherType 对Filter配置的影响》,项目要将服务从虚拟机从迁入docker中,方便自动化部署以及弹性扩容等。
闭坑一-应用看到的ip及port是docker的内部ip、端口,外部无法访问
应用看到的是docker内部ip及端口,这个是宿主机虚拟出来的,对外部不可见;如果不做任何处理,应用会拿着这个ip及docker去注册服务,导致外部调用方调用失败。
处理方法
这个是所有厂商面临的问题,因此厂商一般会在docker中提供一种方法去获取宿主机的IP,以及宿主机的映射端口。
然后通过实现ConsulRegistrationCustomizer对consul的注册信息进行定制。
# getHostPort()、getHostIP()大家根据各自云服提供厂商的解决方案适配
public class ConsulRegistrationCustomizerForDocker implements ConsulRegistrationCustomizer
@Override
public void customize(ConsulRegistration registration) {
if (isRunningInDcoker()) {
registration.getService().setPort(getHostPort());
registration.getService().setAddress(getHostIP());
}
}
}
具体原理参考spring-cloud-consul-discovery自动配置的源码
org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration#consulRegistration
@Bean
@ConditionalOnMissingBean
public ConsulAutoRegistration consulRegistration(
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties, ApplicationContext applicationContext,
ObjectProvider<List<ConsulRegistrationCustomizer>> registrationCustomizers,
ObjectProvider<List<ConsulManagementRegistrationCustomizer>> managementRegistrationCustomizers,
HeartbeatProperties heartbeatProperties) {
return ConsulAutoRegistration.registration(autoServiceRegistrationProperties,
properties, applicationContext, registrationCustomizers.getIfAvailable(),
managementRegistrationCustomizers.getIfAvailable(), heartbeatProperties);
}
org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration#registration
public static ConsulAutoRegistration registration(
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties, ApplicationContext context,
List<ConsulRegistrationCustomizer> registrationCustomizers,
List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers,
HeartbeatProperties heartbeatProperties) {
NewService service = new NewService();
String appName = getAppName(properties, context.getEnvironment());
service.setId(getInstanceId(properties, context));
# 重点关注,下面一节会用到这里
if (!properties.isPreferAgentAddress()) {
service.setAddress(properties.getHostname());
}
service.setName(normalizeForDns(appName));
service.setTags(createTags(properties));
service.setEnableTagOverride(properties.getEnableTagOverride());
service.setMeta(getMetadata(properties));
if (properties.getPort() != null) {
service.setPort(properties.getPort());
// we know the port and can set the check
setCheck(service, autoServiceRegistrationProperties, properties, context,
heartbeatProperties);
}
ConsulAutoRegistration registration = new ConsulAutoRegistration(service,
autoServiceRegistrationProperties, properties, context,
heartbeatProperties, managementRegistrationCustomizers);
# here,就是使用ConsulRegistrationCustomizer对注册信息进行定制
customize(registrationCustomizers, registration);
return registration;
}
闭坑二-有时候大家会在虚拟机中配置/etc/hosts,来隔离环境的差异或者说ip的变化,如果hosts中给本机ip也配置了域名(别名,属于私有的,域名系统无法识别),因consul-discovery注册服务时,优先使用域名而不是ip;一般docker中无法去配置hosts(不会让人随便登录进去配置,而且每次重新部署docker都会重建、重新配置,不太可行),导致该域名无法识别,服务间调用不通
解决方案
配置spring.cloud.consul.discovery.preferAgentAddress=true
从上面代码中可以看到address的设置逻辑
if (!properties.isPreferAgentAddress()) {
service.setAddress(properties.getHostname());
}
ConsulDiscoveryProperties源码
public String getHostname() {
return this.preferIpAddress ? this.ipAddress : this.hostname;
}
public ConsulDiscoveryProperties(InetUtils inetUtils) {
this();
this.hostInfo = inetUtils.findFirstNonLoopbackHostInfo();
this.ipAddress = this.hostInfo.getIpAddress();
this.hostname = this.hostInfo.getHostname();
}