背景

背景如上一篇文章《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();
	}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐