Ipv6 Support (#10603)
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 321d8e2..bd4e256 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -392,6 +392,8 @@
String PROTOCOL_SERVER = "server";
+ String IPV6_KEY = "ipv6";
+
/**
* The parameter key for the class path of the ServiceNameMapping {@link Properties} file
*
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
index 2695f2a..0362af7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -86,7 +86,7 @@
}
public URLAddress setHost(String host) {
- return new URLAddress(host, port, rawAddress);
+ return new URLAddress(host, port, null);
}
public int getPort() {
@@ -94,7 +94,7 @@
}
public URLAddress setPort(int port) {
- return new URLAddress(host, port, rawAddress);
+ return new URLAddress(host, port, null);
}
public String getAddress() {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
index 473b836..43656f0 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
@@ -96,6 +96,7 @@
private static final Map<String, String> HOST_NAME_CACHE = new LRUCache<>(1000);
private static volatile InetAddress LOCAL_ADDRESS = null;
+ private static volatile Inet6Address LOCAL_ADDRESS_V6 = null;
private static final String SPLIT_IPV4_CHARACTER = "\\.";
private static final String SPLIT_IPV6_CHARACTER = ":";
@@ -250,6 +251,8 @@
private static volatile String HOST_ADDRESS;
+ private static volatile String HOST_ADDRESS_V6;
+
public static String getLocalHost() {
if (HOST_ADDRESS != null) {
return HOST_ADDRESS;
@@ -257,6 +260,15 @@
InetAddress address = getLocalAddress();
if (address != null) {
+ if (address instanceof Inet6Address) {
+ String ipv6AddressString = address.getHostAddress();
+ if (ipv6AddressString.contains("%")) {
+ ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
+ }
+ HOST_ADDRESS = ipv6AddressString;
+ return HOST_ADDRESS;
+ }
+
HOST_ADDRESS = address.getHostAddress();
return HOST_ADDRESS;
}
@@ -264,6 +276,29 @@
return LOCALHOST_VALUE;
}
+ public static String getLocalHostV6() {
+ if (StringUtils.isNotEmpty(HOST_ADDRESS_V6)) {
+ return HOST_ADDRESS_V6;
+ }
+ //avoid to search network interface card many times
+ if("".equals(HOST_ADDRESS_V6)){
+ return null;
+ }
+
+ Inet6Address address = getLocalAddressV6();
+ if (address != null) {
+ String ipv6AddressString = address.getHostAddress();
+ if (ipv6AddressString.contains("%")) {
+ ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
+ }
+
+ HOST_ADDRESS_V6 = ipv6AddressString;
+ return HOST_ADDRESS_V6;
+ }
+ HOST_ADDRESS_V6 = "";
+ return null;
+ }
+
public static String filterLocalHost(String host) {
if (host == null || host.length() == 0) {
return host;
@@ -309,6 +344,15 @@
return localAddress;
}
+ public static Inet6Address getLocalAddressV6() {
+ if (LOCAL_ADDRESS_V6 != null) {
+ return LOCAL_ADDRESS_V6;
+ }
+ Inet6Address localAddress = getLocalAddress0V6();
+ LOCAL_ADDRESS_V6 = localAddress;
+ return localAddress;
+ }
+
private static Optional<InetAddress> toValidAddress(InetAddress address) {
if (address instanceof Inet6Address) {
Inet6Address v6Address = (Inet6Address) address;
@@ -355,10 +399,34 @@
logger.warn(e);
}
+ localAddress = getLocalAddressV6();
return localAddress;
}
+ private static Inet6Address getLocalAddress0V6() {
+ // @since 2.7.6, choose the {@link NetworkInterface} first
+ try {
+ NetworkInterface networkInterface = findNetworkInterface();
+ Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ InetAddress address = addresses.nextElement();
+ if (address instanceof Inet6Address) {
+ if (!address.isLoopbackAddress() //filter 127.x.x.x
+ && !address.isAnyLocalAddress() // filter 0.0.0.0
+ && !address.isLinkLocalAddress() //filter 169.254.0.0/16
+ && address.getHostAddress().contains(":")) {//filter IPv6
+ return (Inet6Address) address;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e);
+ }
+
+ return null;
+ }
+
/**
* Returns {@code true} if the specified {@link NetworkInterface} should be ignored with the given conditions.
*
@@ -380,7 +448,7 @@
for (String ignoredInterface : ignoredInterfaces.split(",")) {
String trimIgnoredInterface = ignoredInterface.trim();
boolean matched = false;
- try {
+ try {
matched = networkInterfaceDisplayName.matches(trimIgnoredInterface);
} catch (PatternSyntaxException e) {
// if trimIgnoredInterface is an invalid regular expression, a PatternSyntaxException will be thrown out
@@ -744,4 +812,28 @@
return Integer.parseInt(ipSegment, 16);
}
+
+ public static boolean isIPV6URLStdFormat(String ip) {
+ if ((ip.charAt(0) == '[' && ip.indexOf(']') > 2)) {
+ return true;
+ } else if (ip.indexOf(":") != ip.lastIndexOf(":")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static String getLegalIP(String ip) {
+ //ipv6 [::FFFF:129.144.52.38]:80
+ int ind;
+ if ((ip.charAt(0) == '[' && (ind = ip.indexOf(']')) > 2)) {
+ String nhost = ip;
+ ip = nhost.substring(0, ind + 1);
+ ip = ip.substring(1, ind);
+ return ip;
+ } else {
+ return ip;
+ }
+ }
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 09bc1a4..9996c91 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -52,7 +52,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -364,7 +363,7 @@
*/
protected static Map<String, String> convert(Map<String, String> parameters, String prefix) {
if (parameters == null || parameters.isEmpty()) {
- return Collections.emptyMap();
+ return new HashMap<>();
}
Map<String, String> result = new HashMap<>();
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
index f295de9..fdd3533 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
@@ -43,6 +43,7 @@
testCases.add("dubbo://192.168.1.1/" + RandomString.make(10240));
testCases.add("file:/path/to/file.txt");
testCases.add("dubbo://fe80:0:0:0:894:aeec:f37d:23e1%en0/path?abc=abc");
+ testCases.add("dubbo://[fe80:0:0:0:894:aeec:f37d:23e1]:20880/path?abc=abc");
errorDecodedCases.add("dubbo:192.168.1.1");
errorDecodedCases.add("://192.168.1.1");
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
index e78ab5d..033901b 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
@@ -18,7 +18,6 @@
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.CollectionUtils;
-
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -1082,4 +1081,13 @@
URL urlWithoutUserInformation = URL.valueOf("10.20.130.230:20880/context/path?version=1.0.0&application=app1");
assertEquals("10.20.130.230:20880", urlWithoutUserInformation.getAuthority());
}
+
+
+ @Test
+ public void testIPV6() {
+ URL url = URL.valueOf("dubbo://[2408:4004:194:8896:3e8a:82ae:814a:398]:20881?name=apache");
+ assertEquals("[2408:4004:194:8896:3e8a:82ae:814a:398]", url.getHost());
+ assertEquals(20881, url.getPort());
+ assertEquals("apache", url.getParameter("name"));
+ }
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 35a8cd4..833184f 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -28,6 +28,7 @@
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker;
@@ -554,6 +555,15 @@
// export service
String host = findConfiguredHosts(protocolConfig, provider, params);
+ if (NetUtils.isIPV6URLStdFormat(host)) {
+ if (!host.contains("[")) {
+ host = "[" + host + "]";
+ }
+ } else if (NetUtils.getLocalHostV6() != null) {
+ String ipv6Host = NetUtils.getLocalHostV6();
+ params.put(CommonConstants.IPV6_KEY, ipv6Host);
+ }
+
Integer port = findConfiguredPort(protocolConfig, provider, this.getExtensionLoader(Protocol.class), name, params);
URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params);
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
index 7687b04..241ba8c 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.extension.Activate;
+import static org.apache.dubbo.common.constants.CommonConstants.IPV6_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
@@ -37,9 +38,10 @@
private final String[] includedInstanceParams;
public DefaultMetadataParamsFilter() {
- this.includedInstanceParams = new String[]{HEARTBEAT_TIMEOUT_KEY, TIMESTAMP_KEY};
+ this.includedInstanceParams = new String[]{HEARTBEAT_TIMEOUT_KEY, TIMESTAMP_KEY, IPV6_KEY};
this.excludedServiceParams = new String[]{MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE,
- QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, PID_KEY, TIMESTAMP_KEY, HEARTBEAT_TIMEOUT_KEY};
+ QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, PID_KEY, TIMESTAMP_KEY, HEARTBEAT_TIMEOUT_KEY,
+ IPV6_KEY};
}
@Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
index 913c0cc..05387f5 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
@@ -78,6 +78,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.IPV6_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -136,7 +137,7 @@
public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = {
APPLICATION_KEY, CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, CLUSTER_KEY, CONNECTIONS_KEY, DEPRECATED_KEY,
GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, TOKEN_KEY, VERSION_KEY, WARMUP_KEY,
- WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY, SIDE_KEY
+ WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY, SIDE_KEY, IPV6_KEY
};
public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = {
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
index 1f0c3f7..025e902 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
@@ -19,6 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.resource.GlobalResourceInitializer;
@@ -149,8 +150,33 @@
@Override
protected void doConnect() throws Throwable {
+ try {
+ String ipv6Address = NetUtils.getLocalHostV6();
+ InetSocketAddress connectAddress;
+ //first try ipv6 address
+ if (ipv6Address != null && getUrl().getParameter(CommonConstants.IPV6_KEY) != null) {
+ connectAddress = new InetSocketAddress(getUrl().getParameter(CommonConstants.IPV6_KEY), getUrl().getPort());
+ try {
+ doConnect(connectAddress);
+ return;
+ } catch (Throwable throwable) {
+ //ignore
+ }
+ }
+
+ connectAddress = getConnectAddress();
+ doConnect(connectAddress);
+ } finally {
+ // just add new valid channel to NettyChannel's cache
+ if (!isConnected()) {
+ //future.cancel(true);
+ }
+ }
+ }
+
+ private void doConnect(InetSocketAddress serverAddress) throws RemotingException {
long start = System.currentTimeMillis();
- ChannelFuture future = bootstrap.connect(getConnectAddress());
+ ChannelFuture future = bootstrap.connect(serverAddress);
try {
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
@@ -192,7 +218,7 @@
// 6-1 Failed to connect to provider server by other reason.
RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
- + getRemoteAddress() + ", error message is:" + cause.getMessage(), cause);
+ + serverAddress + ", error message is:" + cause.getMessage(), cause);
logger.error("6-1", "network disconnected", "",
"Failed to connect to provider server by other reason.", cause);
@@ -204,7 +230,7 @@
// 6-2 Client-side timeout
RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
- + getRemoteAddress() + " client-side timeout "
+ + serverAddress + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
index 356995d..6fc453a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
@@ -97,7 +97,9 @@
RpcContext context = RpcContext.getServerAttachment();
// .setAttachments(attachments) // merged from dubbox
- context.setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
+ if (context.getLocalAddress() == null) {
+ context.setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
+ }
String remoteApplication = invocation.getAttachment(REMOTE_APPLICATION_KEY);
if (StringUtils.isNotEmpty(remoteApplication)) {