dubbo icon indicating copy to clipboard operation
dubbo copied to clipboard

容器上使用DUBBO 为什么2.7.12版本 Bind端口强制使用了注册端口号,导致容器下无法正确访问

Open fl061157 opened this issue 4 years ago • 11 comments

2.7.8 版本代码如下。 ServiceConfig.java 方法 findConfigedPorts 代码 map.put(BIND_PORT_KEY, String.valueOf(portToBind));

2.7.12版本代码 ServiceConfig.java 方法 findConfigedPorts 代码 map.put(BIND_PORT_KEY, String.valueOf(portToRegistry));

有什么特殊的考虑吗?

fl061157 avatar Sep 08 '21 06:09 fl061157

这个是为了解决这个case: #7656

把DUBBO_DUBBO_PORT_TO_BIND和DUBBO_PORT_TO_REGISTRY两个配置统一了。 如果DUBBO_PORT_TO_REGISTRY指定了就用这个;没指定就用DUBBO_DUBBO_PORT_TO_BIND。

ps:我觉得这个代码的写法确实不是很合适,如果是我会这么写:

portToBind = portToRegistry == null ? portToRegistry : portToRegistry;
map.put(BIND_PORT_KEY, String.valueOf(portToBind));

这样可读性会高一点;

我后续调整一下。

changfubai avatar Sep 09 '21 08:09 changfubai

本质上逻辑和2.7.8是没区别的,只是只能用一个配置端口而已,毕竟也无法配置两个不同的端口

changfubai avatar Sep 09 '21 08:09 changfubai

@changfubai
你好,这个在 容器环境下 , 使用同一个 IP , 跑多个实例 规划端口会比较麻烦。

fl061157 avatar Sep 11 '21 00:09 fl061157

@changfubai 你好,这个在 容器环境下 , 使用同一个 IP , 跑多个实例 规划端口会比较麻烦。

你说的是多个实例分别在不同的容器里,ip和端口怎么设计是么? 为啥会存在同一个ip,多实例用不同的端口的情况呢? 假设是我说的那种case,就用环境变量注入ip和端口可以了,容器外能访问通的地址就行

changfubai avatar Sep 11 '21 02:09 changfubai

你好,我们使用的是 NodePort 模式。 注册到注册中心的是 Node 的 IP , 因此需要每一个 实例在Node 上都有唯一映射 Port(这个也是 注册到注册中心的Port) 这样才能够全部访问到。

fl061157 avatar Sep 13 '21 00:09 fl061157

你好,我们使用的是 NodePort 模式。 注册到注册中心的是 Node 的 IP , 因此需要每一个 实例在Node 上都有唯一映射 Port(这个也是 注册到注册中心的Port) 这样才能够全部访问到。

这种情况没什么好的办法,你要用nodePort注册就是会需要开很多端口。正常上线的服务,不需要对集群外部暴露服务的吧?那使用nodePort是基于什么考量呢

changfubai avatar Sep 18 '21 01:09 changfubai

这个问题我也发现了。服务绑定的端口是容器内的端口,而上报到注册中心的端口应该是宿主机的端口。比如内部IP和端口是IP1:Port1,上报到注册中心的IP和端口应该是宿主机的IP端口,比如IP2:Port2。 容器负载配置port2映射到port1。主机外的消费者看到的服务提供者的地址是IP2:Port2。因为消费者不能直接通过IP1:Port1访问服务。将请求发给IP2:Port2,然后宿主机将其转发到内部的容器实例IP1:Port1。

dubbo这么一改后,业务配置的绑定端口port1就无效了,直接监听端口DUBBO_PORT_TO_REGISTRY,即port2了。以前端口映射配置是port2->port1,现在就变成了port2->port2了。port1就没用了。

zhoutianli518 avatar Oct 08 '21 01:10 zhoutianli518

    // registry port, not used as bind port by default
    String key = DUBBO_PORT_TO_REGISTRY;
    if (protocolConfigNum > 1) {
        key = getProtocolConfigId(protocolConfig).toUpperCase() + "_" + key;
    }
    String portToRegistryStr = getValueFromConfig(protocolConfig, key);
    Integer portToRegistry = parsePort(portToRegistryStr);
    if (portToRegistry == null) {
        portToRegistry = portToBind;
    }

    // save bind port, used as url's key later
    map.put(BIND_PORT_KEY, String.valueOf(portToRegistry));

    return portToRegistry;
}

感觉上面的代码,将 map.put(BIND_PORT_KEY, String.valueOf(portToRegistry)); 改成 map.put(BIND_PORT_KEY, String.valueOf(portToBind)); 更合适一点。 绑定端口和注册端口是可以不一样的。这样就和以前一样,能解决@ fl061157 提出的问题了。

zhoutianli518 avatar Oct 08 '21 01:10 zhoutianli518

fl061157

天立哥,我直接换版本了。2.7.11 。

fl061157 avatar Oct 08 '21 01:10 fl061157

dubbo3.0.4也有这个问题,只设置了DUBBO_PORT_TO_REGISTRY=20881,但是监听端口也变成了20881,只想让上报到注册中心的端口变

monkeyWie avatar Nov 11 '21 07:11 monkeyWie

使用了2.7.x新版,这里提供个没有办法的办法:

思路是: ServiceConfig里面有一个扩展点可以用,ConfiguratorFactory这个扩展点执行在port之后,并且可以对URL进行重置,我们实现扩展点,来对port进行重置

ServiceConfig源码如下

String host = findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = findConfigedPorts(protocolConfig, name, map, protocolConfigNum);
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

    // You can customize Configurator to append extra parameters
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

实现扩展点

public class BindPortFixConfiguratorFactory implements ConfiguratorFactory {

@Override
public Configurator getConfigurator(URL url) {
	return new Configurator() {
		@Override
		public URL getUrl() {
			return url;
		}

		@Override
		public URL configure(URL url) {
			URL finalURL = checkAndFixRegistryPort(url);
			finalURL = checkAndFixBindPort(finalURL);
			return finalURL;
		}
	};
}

private URL checkAndFixRegistryPort(URL url) {
	Integer portToRegistryFromEnv = getEnvPortToRegistry();
	if(portToRegistryFromEnv != null) {
		int portToRegistryFromUrl = url.getPort();
		if(portToRegistryFromEnv != portToRegistryFromUrl) {
			return url.setPort(portToRegistryFromEnv);
		}
	}
	return url;
}

private URL checkAndFixBindPort(URL url) {
	Integer portToBindFromEnv = getEnvPortToBind();
	if(portToBindFromEnv != null) {
		String bindPort = url.getParameter("bind.port");
		if(!portToBindFromEnv.toString().equals(bindPort)) {
			return url.addParameter("bind.port", String.valueOf(portToBindFromEnv));
		}
	}
	return url;
}

private Integer getEnvPortToBind() {
	String portStr = getValueFromConfig(DUBBO_PORT_TO_BIND);
	return parsePort(portStr);
}

private Integer getEnvPortToRegistry() {
	String portStr = getValueFromConfig(DUBBO_PORT_TO_REGISTRY);
	return parsePort(portStr);
}

private String getValueFromConfig(String key) {
	String value = ConfigUtils.getSystemProperty(key);
	if (StringUtils.isEmpty(value)) {
		value = ConfigUtils.getSystemProperty(key);
	}
	return value;
}

private Integer parsePort(String configPort) {
	Integer port = null;
	if (configPort != null && configPort.length() > 0) {
		try {
			Integer intPort = Integer.parseInt(configPort);
			if (isInvalidPort(intPort)) {
				throw new IllegalArgumentException("Specified invalid port from env value:" + configPort);
			}
			port = intPort;
		} catch (Exception e) {
			throw new IllegalArgumentException("Specified invalid port from env value:" + configPort);
		}
	}
	return port;
}

}`

创建文件spi文件 resources/META-INF/dubbo/xxx.dubbo.spi.BindPortFixConfiguratorFactory

文件内容如下 dubbo=xxx.dubbo.spi.BindPortFixConfiguratorFactory

jojocookx avatar Jan 03 '24 09:01 jojocookx