Ipv6 Support (#10603)

This commit is contained in:
qinliujie 2022-09-14 14:15:19 +08:00 committed by GitHub
parent 84f837b76d
commit cd7b7590ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 156 additions and 13 deletions

View File

@ -392,6 +392,8 @@ public interface CommonConstants {
String PROTOCOL_SERVER = "server";
String IPV6_KEY = "ipv6";
/**
* The parameter key for the class path of the ServiceNameMapping {@link Properties} file
*

View File

@ -86,7 +86,7 @@ public class URLAddress implements Serializable {
}
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 class URLAddress implements Serializable {
}
public URLAddress setPort(int port) {
return new URLAddress(host, port, rawAddress);
return new URLAddress(host, port, null);
}
public String getAddress() {

View File

@ -96,6 +96,7 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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 @@ public final class NetUtils {
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;
}
}
}

View File

@ -52,7 +52,6 @@ import java.lang.reflect.Modifier;
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 @@ public abstract class AbstractConfig implements Serializable {
*/
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<>();

View File

@ -43,6 +43,7 @@ public class URLStrParserTest {
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");

View File

@ -18,7 +18,6 @@ package org.apache.dubbo.common;
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 @@ public class URLTest {
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"));
}
}

View File

@ -28,6 +28,7 @@ import org.apache.dubbo.common.url.component.ServiceConfigURL;
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 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
// 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);

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.metadata;
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 @@ public class DefaultMetadataParamsFilter implements MetadataParamsFilter {
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

View File

@ -78,6 +78,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY;
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 class RegistryProtocol implements Protocol, ScopeModelAware {
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 = {

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.remoting.transport.netty4;
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 @@ public class NettyClient extends AbstractClient {
@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 @@ public class NettyClient extends AbstractClient {
// 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 @@ public class NettyClient extends AbstractClient {
// 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());

View File

@ -97,7 +97,9 @@ public class ContextFilter implements Filter, Filter.Listener {
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)) {