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);
         });
     }