You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by me...@apache.org on 2020/02/28 08:12:40 UTC
[dubbo] branch master updated: [Feature] To support the application
choose the preferred network interface (#5801)
This is an automated email from the ASF dual-hosted git repository.
mercyblitz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 0313381 [Feature] To support the application choose the preferred network interface (#5801)
0313381 is described below
commit 03133818ad657584d30d4ca04e280cdf836849ac
Author: Mercy Ma <me...@gmail.com>
AuthorDate: Fri Feb 28 16:12:23 2020 +0800
[Feature] To support the application choose the preferred network interface (#5801)
* Polish /apache/dubbo#5745 : Increasing the stack size in the start.sh
* Polish /apache/dubbo#5297 : Only one of the multiple registration centers using nacos can register
* Polish /apache/dubbo#5442 : VERSION_KEY和GROUP_KEY为空时,注册到NACOS的服务名与alibaba实现不一致,导致无法消费
* Polish /apache/dubbo#5442 : Merge upstream/master
* Polish /apache/dubbo##5239 : Mock字段注入异常
* Polish /apache/dubbo##5239 : Mock字段注入异常
* Polish /apache/dubbo#5770 : Removing the interinal JDK API from FileSystemDynamicConfiguration
* Polish /apache/dubbo#5771 : [Enhancement] Refactor the APT test-cases implementation of dubbo-metadata-processor in Java 9+
* Bugfix for the test-cases
* Polish /apache/dubbo#5779 : Rename the local variables
* Add FieldUtils
* Polish /apache/dubbo#5795 : [Feature] To support the application choose the preferred network interface
* Polish /apache/dubbo#5442 : VERSION_KEY和GROUP_KEY为空时,注册到NACOS的服务名与alibaba实现不一致,导致无法消费
---
.../dubbo/common/constants/CommonConstants.java | 10 ++
.../org/apache/dubbo/common/utils/FieldUtils.java | 148 +++++++++++++++++++++
.../org/apache/dubbo/common/utils/NetUtils.java | 140 ++++++++++++++-----
.../apache/dubbo/common/utils/FieldUtilsTest.java | 88 ++++++++++++
...onfigDefaultPropertyValueBeanPostProcessor.java | 14 +-
.../apache/dubbo/registry/nacos/NacosRegistry.java | 5 +-
6 files changed, 363 insertions(+), 42 deletions(-)
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 b0082e8..7924109 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
@@ -17,6 +17,7 @@
package org.apache.dubbo.common.constants;
+import java.net.NetworkInterface;
import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;
@@ -199,6 +200,15 @@ public interface CommonConstants {
String HOST_KEY = "host";
String PORT_KEY = "port";
String DUBBO_IP_TO_BIND = "DUBBO_IP_TO_BIND";
+
+ /**
+ * The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that
+ * the Dubbo application prefers
+ *
+ * @since 2.7.6
+ */
+ String DUBBO_PREFERRED_NETWORK_INTERFACE = "dubbo.network.interface.preferred";
+
@Deprecated
String SHUTDOWN_WAIT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds";
String SHUTDOWN_WAIT_KEY = "dubbo.service.shutdown.wait";
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java
new file mode 100644
index 0000000..a619239
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/FieldUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.utils;
+
+import java.lang.reflect.Field;
+
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes;
+
+/**
+ * The utilities class for Java Reflection {@link Field}
+ *
+ * @since 2.7.6
+ */
+public interface FieldUtils {
+
+ /**
+ * Like the {@link Class#getDeclaredField(String)} method without throwing any {@link Exception}
+ *
+ * @param declaredClass the declared class
+ * @param fieldName the name of {@link Field}
+ * @return if can't be found, return <code>null</code>
+ */
+ static Field getDeclaredField(Class<?> declaredClass, String fieldName) {
+ Field field = null;
+ try {
+ field = declaredClass.getDeclaredField(fieldName);
+ } catch (NoSuchFieldException ignored) {
+ field = null;
+ }
+ return field;
+ }
+
+ /**
+ * Find the {@link Field} by the name in the specified class and its inherited types
+ *
+ * @param declaredClass the declared class
+ * @param fieldName the name of {@link Field}
+ * @return if can't be found, return <code>null</code>
+ */
+ static Field findField(Class<?> declaredClass, String fieldName) {
+ Field field = getDeclaredField(declaredClass, fieldName);
+ if (field != null) {
+ return field;
+ }
+ for (Class superType : getAllInheritedTypes(declaredClass)) {
+ field = getDeclaredField(superType, fieldName);
+ if (field != null) {
+ break;
+ }
+ }
+ return field;
+ }
+
+ /**
+ * Find the {@link Field} by the name in the specified class and its inherited types
+ *
+ * @param object the object whose field should be modified
+ * @param fieldName the name of {@link Field}
+ * @return if can't be found, return <code>null</code>
+ */
+ static Field findField(Object object, String fieldName) {
+ return findField(object.getClass(), fieldName);
+ }
+
+ /**
+ * Get the value of the specified {@link Field}
+ *
+ * @param object the object whose field should be modified
+ * @param fieldName the name of {@link Field}
+ * @return the value of the specified {@link Field}
+ */
+ static Object getFieldValue(Object object, String fieldName) {
+ return getFieldValue(object, findField(object, fieldName));
+ }
+
+ /**
+ * Get the value of the specified {@link Field}
+ *
+ * @param object the object whose field should be modified
+ * @param field {@link Field}
+ * @return the value of the specified {@link Field}
+ */
+ static <T> T getFieldValue(Object object, Field field) {
+ boolean accessible = field.isAccessible();
+ Object value = null;
+ try {
+ if (!accessible) {
+ field.setAccessible(true);
+ }
+ value = field.get(object);
+ } catch (IllegalAccessException ignored) {
+ } finally {
+ field.setAccessible(accessible);
+ }
+ return (T) value;
+ }
+
+ /**
+ * Set the value for the specified {@link Field}
+ *
+ * @param object the object whose field should be modified
+ * @param fieldName the name of {@link Field}
+ * @param value the value of field to be set
+ * @return the previous value of the specified {@link Field}
+ */
+ static <T> T setFieldValue(Object object, String fieldName, T value) {
+ return setFieldValue(object, findField(object, fieldName), value);
+ }
+
+ /**
+ * Set the value for the specified {@link Field}
+ *
+ * @param object the object whose field should be modified
+ * @param field {@link Field}
+ * @param value the value of field to be set
+ * @return the previous value of the specified {@link Field}
+ */
+ static <T> T setFieldValue(Object object, Field field, T value) {
+ boolean accessible = field.isAccessible();
+ Object previousValue = null;
+ try {
+ if (!accessible) {
+ field.setAccessible(true);
+ }
+ previousValue = field.get(object);
+ field.set(object, value);
+ } catch (IllegalAccessException ignored) {
+ } finally {
+ field.setAccessible(accessible);
+ }
+ return (T) previousValue;
+ }
+
+}
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 a59d325..48c4f65 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
@@ -18,6 +18,7 @@ package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.logger.support.FailsafeLogger;
@@ -30,17 +31,24 @@ import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.ServerSocket;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
+import static java.util.Collections.emptyList;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_IP_TO_BIND;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PREFERRED_NETWORK_INTERFACE;
import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE;
+import static org.apache.dubbo.common.utils.CollectionUtils.first;
/**
* IP and Port Helper for RPC
@@ -259,6 +267,27 @@ public class NetUtils {
private static InetAddress getLocalAddress0() {
InetAddress localAddress = null;
+
+ // @since 2.7.6, choose the {@link NetworkInterface} first
+ try {
+ NetworkInterface networkInterface = findNetworkInterface();
+ Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
+ if (addressOp.isPresent()) {
+ try {
+ if (addressOp.get().isReachable(100)) {
+ return addressOp.get();
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e);
+ }
+
try {
localAddress = InetAddress.getLocalHost();
Optional<InetAddress> addressOp = toValidAddress(localAddress);
@@ -269,42 +298,86 @@ public class NetUtils {
logger.warn(e);
}
- try {
- Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
- if (null == interfaces) {
- return localAddress;
- }
- while (interfaces.hasMoreElements()) {
- try {
- NetworkInterface network = interfaces.nextElement();
- if (network.isLoopback() || network.isVirtual() || !network.isUp()) {
- continue;
- }
- Enumeration<InetAddress> addresses = network.getInetAddresses();
- while (addresses.hasMoreElements()) {
- try {
- Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
- if (addressOp.isPresent()) {
- try {
- if (addressOp.get().isReachable(100)) {
- return addressOp.get();
- }
- } catch (IOException e) {
- // ignore
- }
- }
- } catch (Throwable e) {
- logger.warn(e);
- }
- }
- } catch (Throwable e) {
- logger.warn(e);
- }
+
+ return localAddress;
+ }
+
+ /**
+ * @param networkInterface {@link NetworkInterface}
+ * @return if the specified {@link NetworkInterface} should be ignored, return <code>true</code>
+ * @throws SocketException SocketException if an I/O error occurs.
+ * @since 2.7.6
+ */
+ private static boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws SocketException {
+ return networkInterface == null
+ || networkInterface.isLoopback()
+ || networkInterface.isVirtual()
+ || !networkInterface.isUp();
+ }
+
+ /**
+ * Get the valid {@link NetworkInterface network interfaces}
+ *
+ * @return non-null
+ * @throws SocketException SocketException if an I/O error occurs.
+ * @since 2.7.6
+ */
+ private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
+ List<NetworkInterface> validNetworkInterfaces = new LinkedList<>();
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface networkInterface = interfaces.nextElement();
+ if (ignoreNetworkInterface(networkInterface)) { // ignore
+ continue;
}
+ validNetworkInterfaces.add(networkInterface);
+ }
+ return validNetworkInterfaces;
+ }
+
+ /**
+ * Is preferred {@link NetworkInterface} or not
+ *
+ * @param networkInterface {@link NetworkInterface}
+ * @return if the name of the specified {@link NetworkInterface} matches
+ * the property value from {@link CommonConstants#DUBBO_PREFERRED_NETWORK_INTERFACE}, return <code>true</code>,
+ * or <code>false</code>
+ */
+ public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) {
+ String preferredNetworkInterface = System.getProperty(DUBBO_PREFERRED_NETWORK_INTERFACE);
+ return Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface);
+ }
+
+ /**
+ * Get the suitable {@link NetworkInterface}
+ *
+ * @return If no {@ink NetworkInterface} is available , return <code>null</code>
+ * @since 2.7.6
+ */
+ public static NetworkInterface findNetworkInterface() {
+
+ List<NetworkInterface> validNetworkInterfaces = emptyList();
+ try {
+ validNetworkInterfaces = getValidNetworkInterfaces();
} catch (Throwable e) {
logger.warn(e);
}
- return localAddress;
+
+ NetworkInterface result = null;
+
+ // Try to find the preferred one
+ for (NetworkInterface networkInterface : validNetworkInterfaces) {
+ if (isPreferredNetworkInterface(networkInterface)) {
+ result = networkInterface;
+ break;
+ }
+ }
+
+ if (result == null) { // If not found, try to get the first one
+ result = first(validNetworkInterfaces);
+ }
+
+ return result;
}
public static String getHostName(String address) {
@@ -370,7 +443,8 @@ public class NetUtils {
return sb.toString();
}
- public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException {
+ public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws
+ IOException {
setInterface(multicastSocket, multicastAddress instanceof Inet6Address);
multicastSocket.setLoopbackMode(false);
multicastSocket.joinGroup(multicastAddress);
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/FieldUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/FieldUtilsTest.java
new file mode 100644
index 0000000..e85995a
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/FieldUtilsTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.utils.FieldUtils.findField;
+import static org.apache.dubbo.common.utils.FieldUtils.getDeclaredField;
+import static org.apache.dubbo.common.utils.FieldUtils.getFieldValue;
+import static org.apache.dubbo.common.utils.FieldUtils.setFieldValue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * {@link FieldUtils} Test-cases
+ *
+ * @since 2.7.6
+ */
+public class FieldUtilsTest {
+
+ @Test
+ public void testGetDeclaredField() {
+ assertEquals("a", getDeclaredField(A.class, "a").getName());
+ assertEquals("b", getDeclaredField(B.class, "b").getName());
+ assertEquals("c", getDeclaredField(C.class, "c").getName());
+ assertNull(getDeclaredField(B.class, "a"));
+ assertNull(getDeclaredField(C.class, "a"));
+ }
+
+ @Test
+ public void testFindField() {
+ assertEquals("a", findField(A.class, "a").getName());
+ assertEquals("a", findField(new A(), "a").getName());
+ assertEquals("a", findField(B.class, "a").getName());
+ assertEquals("b", findField(B.class, "b").getName());
+ assertEquals("a", findField(C.class, "a").getName());
+ assertEquals("b", findField(C.class, "b").getName());
+ assertEquals("c", findField(C.class, "c").getName());
+ }
+
+ @Test
+ public void testGetFieldValue() {
+ assertEquals("a", getFieldValue(new A(), "a"));
+ assertEquals("a", getFieldValue(new B(), "a"));
+ assertEquals("b", getFieldValue(new B(), "b"));
+ assertEquals("a", getFieldValue(new C(), "a"));
+ assertEquals("b", getFieldValue(new C(), "b"));
+ assertEquals("c", getFieldValue(new C(), "c"));
+ }
+
+ @Test
+ public void setSetFieldValue() {
+ A a = new A();
+ assertEquals("a", setFieldValue(a, "a", "x"));
+ assertEquals("x", getFieldValue(a, "a"));
+ }
+}
+
+class A {
+
+ private String a = "a";
+
+}
+
+class B extends A {
+
+ private String b = "b";
+
+}
+
+class C extends B {
+
+ private String c = "c";
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
index c513b13..ec99c1c 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
@@ -70,23 +70,23 @@ public class DubboConfigDefaultPropertyValueBeanPostProcessor extends GenericBea
if (propertyDescriptor != null) { // the property is present
- Method getNameMethod = propertyDescriptor.getReadMethod();
+ Method getterMethod = propertyDescriptor.getReadMethod();
- if (getNameMethod == null) { // if The getter method is absent
+ if (getterMethod == null) { // if The getter method is absent
return;
}
- Object propertyValue = invokeMethod(getNameMethod, bean);
+ Object propertyValue = invokeMethod(getterMethod, bean);
if (propertyValue != null) { // If The return value of "getName" method is not null
return;
}
- Method setNameMethod = propertyDescriptor.getWriteMethod();
- if (setNameMethod != null) { // the getter and setter methods are present
- if (Arrays.equals(of(String.class), setNameMethod.getParameterTypes())) { // the param type is String
+ Method setterMethod = propertyDescriptor.getWriteMethod();
+ if (setterMethod != null) { // the getter and setter methods are present
+ if (Arrays.equals(of(String.class), setterMethod.getParameterTypes())) { // the param type is String
// set bean name to the value of the the property
- invokeMethod(setNameMethod, bean, beanName);
+ invokeMethod(setterMethod, bean, beanName);
}
}
}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
index 2e61d22..070c526 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
@@ -167,11 +167,12 @@ public class NacosRegistry extends FailbackRegistry {
private void doSubscribe(final URL url, final NotifyListener listener, final Set<String> serviceNames) {
execute(namingService -> {
+ List<Instance> instances = new LinkedList();
for (String serviceName : serviceNames) {
- List<Instance> instances = namingService.getAllInstances(serviceName);
- notifySubscriber(url, listener, instances);
+ instances.addAll(namingService.getAllInstances(serviceName));
subscribeEventListener(serviceName, url, listener);
}
+ notifySubscriber(url, listener, instances);
});
}