You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/06/23 11:53:50 UTC

[dubbo] branch 3.0 updated: Optimize Service related issues (#8122)

This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new a39fffe  Optimize Service related issues (#8122)
a39fffe is described below

commit a39fffebe56291ce154c3011faf572e1678d6841
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Wed Jun 23 19:53:34 2021 +0800

    Optimize Service related issues (#8122)
---
 .../org/apache/dubbo/config/ServiceConfig.java     |  5 ++
 .../config/bootstrap/BootstrapTakeoverMode.java    | 30 +++++++++
 .../dubbo/config/bootstrap/DubboBootstrap.java     |  9 +++
 .../dubbo/config/utils/ConfigValidationUtils.java  | 78 +++++++++++++---------
 .../context/DubboBootstrapApplicationListener.java |  3 +
 .../dubbo/metadata/AbstractServiceNameMapping.java |  5 +-
 .../java/org/apache/dubbo/registry/Constants.java  | 11 +++
 .../client/migration/MigrationInvoker.java         | 13 ++--
 .../client/migration/MigrationRuleHandler.java     | 23 +++----
 .../client/migration/MigrationRuleListener.java    |  4 +-
 .../client/migration/model/MigrationRule.java      | 32 ++++++---
 11 files changed, 149 insertions(+), 64 deletions(-)

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 110d1ce..2463147 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
@@ -30,6 +30,7 @@ import org.apache.dubbo.common.utils.ConfigUtils;
 import org.apache.dubbo.common.utils.NamedThreadFactory;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.config.bootstrap.BootstrapTakeoverMode;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker;
 import org.apache.dubbo.config.support.Parameter;
@@ -222,6 +223,10 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
             } else {
                 doExport();
             }
+
+            if (this.bootstrap.getTakeoverMode() == BootstrapTakeoverMode.AUTO) {
+                this.bootstrap.start();
+            }
         }
     }
 
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/BootstrapTakeoverMode.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/BootstrapTakeoverMode.java
new file mode 100644
index 0000000..091de85
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/BootstrapTakeoverMode.java
@@ -0,0 +1,30 @@
+/*
+ * 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.config.bootstrap;
+
+import org.apache.dubbo.config.ServiceConfig;
+
+/**
+ * Mode of which of DubboBootstrap lifecycle being takeover
+ * SPRING: will be controlled by spring context
+ * MANUAL: will be controlled by users, after all services init, should call {@link DubboBootstrap#start()} to init app-level env
+ * AUTO: env will be init once {@link ServiceConfig#export()} finished
+ * SERVLET: will be controlled by tomcat
+ */
+public enum BootstrapTakeoverMode {
+    SPRING, MANUAL, AUTO, SERVLET
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index 394ce6e..85b4853 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -148,6 +148,8 @@ public class DubboBootstrap {
 
     private final AtomicBoolean awaited = new AtomicBoolean(false);
 
+    private volatile BootstrapTakeoverMode takeoverMode = BootstrapTakeoverMode.AUTO;
+
     private final Lock lock = new ReentrantLock();
 
     private final Condition condition = lock.newCondition();
@@ -1583,4 +1585,11 @@ public class DubboBootstrap {
         return configManager.getApplicationOrElseThrow();
     }
 
+    public void setTakeoverMode(BootstrapTakeoverMode takeoverMode) {
+        this.takeoverMode = takeoverMode;
+    }
+
+    public BootstrapTakeoverMode getTakeoverMode() {
+        return takeoverMode;
+    }
 }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index d946356..6c7a341 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -61,17 +61,20 @@ import org.apache.dubbo.rpc.InvokerListener;
 import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
+import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
 import org.apache.dubbo.rpc.support.MockInvoker;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
@@ -206,11 +209,11 @@ public class ConfigValidationUtils {
                     for (URL url : urls) {
 
                         url = URLBuilder.from(url)
-                                .addParameter(REGISTRY_KEY, url.getProtocol())
-                                .setProtocol(extractRegistryType(url))
-                                .build();
+                            .addParameter(REGISTRY_KEY, url.getProtocol())
+                            .setProtocol(extractRegistryType(url))
+                            .build();
                         if ((provider && url.getParameter(REGISTER_KEY, true))
-                                || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
+                            || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                             registryList.add(url);
                         }
                     }
@@ -233,11 +236,11 @@ public class ConfigValidationUtils {
                     }
                     result.add(registryURL);
                     if (DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode)
-                            && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {
+                        && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {
                         URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL)
-                                .setProtocol(REGISTRY_PROTOCOL)
-                                .removeParameter(REGISTRY_TYPE_KEY)
-                                .build();
+                            .setProtocol(REGISTRY_PROTOCOL)
+                            .removeParameter(REGISTRY_TYPE_KEY)
+                            .build();
                         result.add(interfaceCompatibleRegistryURL);
                     }
                 } else {
@@ -246,11 +249,11 @@ public class ConfigValidationUtils {
                         registerMode = DEFAULT_REGISTER_MODE_INTERFACE;
                     }
                     if ((DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(registerMode) || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(registerMode))
-                            && registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) {
+                        && registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)) {
                         URL serviceDiscoveryRegistryURL = URLBuilder.from(registryURL)
-                                .setProtocol(SERVICE_REGISTRY_PROTOCOL)
-                                .removeParameter(REGISTRY_TYPE_KEY)
-                                .build();
+                            .setProtocol(SERVICE_REGISTRY_PROTOCOL)
+                            .removeParameter(REGISTRY_TYPE_KEY)
+                            .build();
                         result.add(serviceDiscoveryRegistryURL);
                     }
 
@@ -270,15 +273,15 @@ public class ConfigValidationUtils {
 
     private static boolean isValidRegisterMode(String mode) {
         return StringUtils.isNotEmpty(mode)
-                && (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(mode)
-                || DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(mode)
-                || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(mode)
+            && (DEFAULT_REGISTER_MODE_INTERFACE.equalsIgnoreCase(mode)
+            || DEFAULT_REGISTER_MODE_INSTANCE.equalsIgnoreCase(mode)
+            || DEFAULT_REGISTER_MODE_ALL.equalsIgnoreCase(mode)
         );
     }
 
     private static boolean registryNotExists(URL registryURL, List<URL> registryList, String registryType) {
         return registryList.stream().noneMatch(
-                url -> registryType.equals(url.getProtocol()) && registryURL.getBackupAddress().equals(url.getBackupAddress())
+            url -> registryType.equals(url.getProtocol()) && registryURL.getBackupAddress().equals(url.getBackupAddress())
         );
     }
 
@@ -292,7 +295,7 @@ public class ConfigValidationUtils {
             hostToRegistry = NetUtils.getLocalHost();
         } else if (NetUtils.isInvalidLocalHost(hostToRegistry)) {
             throw new IllegalArgumentException("Specified invalid registry ip from property:" +
-                    DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
+                DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
         }
         map.put(REGISTER_IP_KEY, hostToRegistry);
 
@@ -317,13 +320,13 @@ public class ConfigValidationUtils {
             }
             return UrlUtils.parseURL(address, map);
         } else if (monitor != null &&
-                (REGISTRY_PROTOCOL.equals(monitor.getProtocol()) || SERVICE_REGISTRY_PROTOCOL.equals(monitor.getProtocol()))
-                && registryURL != null) {
+            (REGISTRY_PROTOCOL.equals(monitor.getProtocol()) || SERVICE_REGISTRY_PROTOCOL.equals(monitor.getProtocol()))
+            && registryURL != null) {
             return URLBuilder.from(registryURL)
-                    .setProtocol(DUBBO_PROTOCOL)
-                    .addParameter(PROTOCOL_KEY, monitor.getProtocol())
-                    .putAttribute(REFER_KEY, map)
-                    .build();
+                .setProtocol(DUBBO_PROTOCOL)
+                .addParameter(PROTOCOL_KEY, monitor.getProtocol())
+                .putAttribute(REFER_KEY, map)
+                .build();
         }
         return null;
     }
@@ -349,7 +352,7 @@ public class ConfigValidationUtils {
                 MockInvoker.parseMockValue(normalizedMock);
             } catch (Exception e) {
                 throw new IllegalStateException("Illegal mock return in <dubbo:service/reference ... " +
-                        "mock=\"" + mock + "\" />");
+                    "mock=\"" + mock + "\" />");
             }
         } else if (normalizedMock.startsWith(THROW_PREFIX)) {
             normalizedMock = normalizedMock.substring(THROW_PREFIX.length()).trim();
@@ -359,7 +362,7 @@ public class ConfigValidationUtils {
                     MockInvoker.getThrowable(normalizedMock);
                 } catch (Exception e) {
                     throw new IllegalStateException("Illegal mock throw in <dubbo:service/reference ... " +
-                            "mock=\"" + mock + "\" />");
+                        "mock=\"" + mock + "\" />");
                 }
             }
         } else {
@@ -375,7 +378,7 @@ public class ConfigValidationUtils {
 
         checkExtension(ProxyFactory.class, PROXY_KEY, config.getProxy());
         checkExtension(Cluster.class, CLUSTER_KEY, config.getCluster());
-        checkMultiExtension(Filter.class, FILE_KEY, config.getFilter());
+        checkMultiExtension(Arrays.asList(Filter.class, ClusterFilter.class), FILE_KEY, config.getFilter());
         checkNameHasSymbol(LAYER_KEY, config.getLayer());
 
         List<MethodConfig> methods = config.getMethods();
@@ -448,7 +451,7 @@ public class ConfigValidationUtils {
 
         if (!config.isValid()) {
             throw new IllegalStateException("No application config found or it's not a valid config! " +
-                    "Please add <dubbo:application name=\"...\" /> to your spring config.");
+                "Please add <dubbo:application name=\"...\" /> to your spring config.");
         }
 
         // backward compatibility
@@ -500,7 +503,7 @@ public class ConfigValidationUtils {
         if (config != null) {
             if (!config.isValid()) {
                 logger.info("There's no valid monitor config found, if you want to open monitor statistics for Dubbo, " +
-                        "please make sure your monitor is configured properly.");
+                    "please make sure your monitor is configured properly.");
             }
 
             checkParameterName(config.getParameters());
@@ -587,7 +590,7 @@ public class ConfigValidationUtils {
     public static void checkExtension(Class<?> type, String property, String value) {
         checkName(property, value);
         if (StringUtils.isNotEmpty(value)
-                && !ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
+            && !ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
             throw new IllegalStateException("No such extension " + value + " for " + property + "/" + type.getName());
         }
     }
@@ -601,6 +604,10 @@ public class ConfigValidationUtils {
      * @param value    The Extension name
      */
     public static void checkMultiExtension(Class<?> type, String property, String value) {
+        checkMultiExtension(Collections.singletonList(type), property, value);
+    }
+
+    public static void checkMultiExtension(List<Class<?>> types, String property, String value) {
         checkMultiName(property, value);
         if (StringUtils.isNotEmpty(value)) {
             String[] values = value.split("\\s*[,]+\\s*");
@@ -611,8 +618,15 @@ public class ConfigValidationUtils {
                 if (DEFAULT_KEY.equals(v)) {
                     continue;
                 }
-                if (!ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
-                    throw new IllegalStateException("No such extension " + v + " for " + property + "/" + type.getName());
+                boolean match = false;
+                for (Class<?> type : types) {
+                    if (ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
+                        match = true;
+                    }
+                }
+                if (!match) {
+                    throw new IllegalStateException("No such extension " + v + " for " + property + "/" +
+                        types.stream().map(Class::getName).collect(Collectors.joining(",")));
                 }
             }
         }
@@ -694,7 +708,7 @@ public class ConfigValidationUtils {
             Matcher matcher = pattern.matcher(value);
             if (!matcher.matches()) {
                 throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contains illegal " +
-                        "character, only digit, letter, '-', '_' or '.' is legal.");
+                    "character, only digit, letter, '-', '_' or '.' is legal.");
             }
         }
     }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
index 1911eb5..6a2ae27 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.config.spring.context;
 
 import org.apache.dubbo.config.DubboShutdownHook;
+import org.apache.dubbo.config.bootstrap.BootstrapTakeoverMode;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 
 import com.alibaba.spring.context.OnceApplicationContextEventListener;
@@ -46,11 +47,13 @@ public class DubboBootstrapApplicationListener extends OnceApplicationContextEve
 
     public DubboBootstrapApplicationListener() {
         this.dubboBootstrap = DubboBootstrap.getInstance();
+        this.dubboBootstrap.setTakeoverMode(BootstrapTakeoverMode.SPRING);
     }
 
     public DubboBootstrapApplicationListener(ApplicationContext applicationContext) {
         super(applicationContext);
         this.dubboBootstrap = DubboBootstrap.getInstance();
+        this.dubboBootstrap.setTakeoverMode(BootstrapTakeoverMode.SPRING);
         DubboBootstrapStartStopListenerSpringAdapter.applicationContext = applicationContext;
     }
 
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
index 9596821..da9b8e8 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
@@ -61,7 +61,10 @@ public abstract class AbstractServiceNameMapping implements ServiceNameMapping {
         }
 
         if (isEmpty(subscribedServices)) {
-            subscribedServices = WritableMetadataService.getDefaultExtension().getCachedMapping(ServiceNameMapping.buildMappingKey(subscribedURL));
+            Set<String> cachedServices = WritableMetadataService.getDefaultExtension().getCachedMapping(ServiceNameMapping.buildMappingKey(subscribedURL));
+            if(!isEmpty(cachedServices)) {
+                subscribedServices.addAll(cachedServices);
+            }
         }
 
         if (isEmpty(subscribedServices)) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
index eee0665..0720f14 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
@@ -98,4 +98,15 @@ public interface Constants {
      * Default value for check frequency: 60000 (ms)
      */
     int DEFAULT_ECHO_POLLING_CYCLE = 60000;
+
+    String MIGRATION_STEP_KEY = "migration.step";
+
+    String MIGRATION_DELAY_KEY = "migration.delay";
+
+    String MIGRATION_FORCE_KEY = "migration.force";
+
+    String MIGRATION_PROMOTION_KEY = "migration.promotion";
+
+    String MIGRATION_THRESHOLD_KEY = "migration.threshold";
+
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
index 0c6aff4..39612cc 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
@@ -38,7 +38,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
@@ -174,7 +173,7 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
         // wait and compare threshold
         waitAddressNotify(newRule, latch);
 
-        if (Boolean.TRUE.equals(newRule.getForce(consumerUrl))) {
+        if (newRule.getForce(consumerUrl)) {
             // force migrate, ignore threshold check
             this.currentAvailableInvoker = invoker;
             this.destroyServiceDiscoveryInvoker();
@@ -211,7 +210,7 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
         // wait and compare threshold
         waitAddressNotify(newRule, latch);
 
-        if (Boolean.TRUE.equals(newRule.getForce(consumerUrl))) {
+        if (newRule.getForce(consumerUrl)) {
             // force migrate, ignore threshold check
             this.currentAvailableInvoker = serviceDiscoveryInvoker;
             this.destroyInterfaceInvoker();
@@ -247,10 +246,10 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
 
     private void waitAddressNotify(MigrationRule newRule, CountDownLatch latch) {
         // wait and compare threshold
-        Integer delay = newRule.getDelay(consumerUrl);
-        if (delay != null) {
+        int delay = newRule.getDelay(consumerUrl);
+        if (delay > 0) {
             try {
-                Thread.sleep(delay * 1000);
+                Thread.sleep(delay * 1000L);
             } catch (InterruptedException e) {
                 logger.error("Interrupter when waiting for address notify!" + e);
             }
@@ -397,7 +396,7 @@ public class MigrationInvoker<T> implements MigrationClusterInvoker<T> {
     @Override
     public void setMigrationRule(MigrationRule rule) {
         this.rule = rule;
-        promotion = Optional.ofNullable(rule.getProportion(consumerUrl)).orElse(100);
+        promotion = rule.getProportion(consumerUrl);
     }
 
     protected void destroyServiceDiscoveryInvoker() {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
index 410776c..052f225 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.registry.client.migration;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.status.reporter.FrameworkStatusReporter;
@@ -26,7 +25,6 @@ import org.apache.dubbo.registry.client.migration.model.MigrationStep;
 
 public class MigrationRuleHandler<T> {
     public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.service-discovery.migration";
-    public static final String MIGRATION_KEY = "migration";
     private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class);
 
     private MigrationClusterInvoker<T> migrationInvoker;
@@ -45,20 +43,15 @@ public class MigrationRuleHandler<T> {
             return;
         }
 
-        // initial step : FORCE_INTERFACE
+        // initial step : APPLICATION_FIRST
         MigrationStep step = MigrationStep.APPLICATION_FIRST;
-        Float threshold = -1f;
-        if (rule == MigrationRule.INIT) {
-            step = Enum.valueOf(MigrationStep.class,
-                    consumerURL.getParameter(MIGRATION_KEY,
-                            ConfigurationUtils.getCachedDynamicProperty(DUBBO_SERVICEDISCOVERY_MIGRATION, step.name())));
-        } else {
-            try {
-                step = getMigrationStep(rule, step);
-                threshold = getMigrationThreshold(rule, threshold);
-            } catch (Exception e) {
-                logger.error("Failed to get step and threshold info from rule: " + rule, e);
-            }
+        float threshold = -1f;
+
+        try {
+            step = rule.getStep(consumerURL);
+            threshold = rule.getThreshold(consumerURL);
+        } catch (Exception e) {
+            logger.error("Failed to get step and threshold info from rule: " + rule, e);
         }
 
         if (refreshInvoker(step, threshold, rule)) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
index a1b9364..56f33af 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
@@ -103,7 +103,9 @@ public class MigrationRuleListener implements RegistryProtocolListener, Configur
     public synchronized void process(ConfigChangedEvent event) {
         String rawRule = event.getContent();
         if (StringUtils.isEmpty(rawRule)) {
-            logger.warn("Received empty migration rule, will ignore.");
+            // fail back to startup status
+            setRawRule(INIT);
+            //logger.warn("Received empty migration rule, will ignore.");
             return;
         }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java
index 48b7528..ce7e34b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry.client.migration.model;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.metadata.ServiceNameMapping;
 
@@ -30,6 +31,13 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.apache.dubbo.registry.Constants.MIGRATION_DELAY_KEY;
+import static org.apache.dubbo.registry.Constants.MIGRATION_FORCE_KEY;
+import static org.apache.dubbo.registry.Constants.MIGRATION_PROMOTION_KEY;
+import static org.apache.dubbo.registry.Constants.MIGRATION_STEP_KEY;
+import static org.apache.dubbo.registry.Constants.MIGRATION_THRESHOLD_KEY;
+import static org.apache.dubbo.registry.client.migration.MigrationRuleHandler.DUBBO_SERVICEDISCOVERY_MIGRATION;
+
 /**
  * # key = demo-consumer.migration
  * # group = DUBBO_SERVICEDISCOVERY_MIGRATION
@@ -153,6 +161,14 @@ public class MigrationRule {
             }
         }
 
+        if(step == null) {
+            // initial step : APPLICATION_FIRST
+            step = MigrationStep.APPLICATION_FIRST;
+            step = Enum.valueOf(MigrationStep.class,
+                consumerURL.getParameter(MIGRATION_STEP_KEY,
+                    ConfigurationUtils.getCachedDynamicProperty(DUBBO_SERVICEDISCOVERY_MIGRATION, step.name())));
+        }
+
         return step;
     }
 
@@ -160,7 +176,7 @@ public class MigrationRule {
         return step;
     }
 
-    public Float getThreshold(URL consumerURL) {
+    public float getThreshold(URL consumerURL) {
         if (interfaceRules != null) {
             SubMigrationRule rule = interfaceRules.get(consumerURL.getDisplayServiceKey());
             if (rule != null) {
@@ -181,7 +197,7 @@ public class MigrationRule {
             }
         }
 
-        return threshold;
+        return threshold == null ? consumerURL.getParameter(MIGRATION_THRESHOLD_KEY, -1f) : threshold;
     }
 
     public Float getThreshold() {
@@ -196,7 +212,7 @@ public class MigrationRule {
         return proportion;
     }
 
-    public Integer getProportion(URL consumerURL) {
+    public int getProportion(URL consumerURL) {
         if (interfaceRules != null) {
             SubMigrationRule rule = interfaceRules.get(consumerURL.getDisplayServiceKey());
             if (rule != null) {
@@ -217,7 +233,7 @@ public class MigrationRule {
             }
         }
 
-        return proportion;
+        return proportion == null ? consumerURL.getParameter(MIGRATION_PROMOTION_KEY, 100) : proportion;
     }
 
     public void setProportion(Integer proportion) {
@@ -228,7 +244,7 @@ public class MigrationRule {
         return delay;
     }
 
-    public Integer getDelay(URL consumerURL) {
+    public int getDelay(URL consumerURL) {
         if (interfaceRules != null) {
             SubMigrationRule rule = interfaceRules.get(consumerURL.getDisplayServiceKey());
             if (rule != null) {
@@ -249,7 +265,7 @@ public class MigrationRule {
             }
         }
 
-        return delay;
+        return delay == null ? consumerURL.getParameter(MIGRATION_DELAY_KEY, 0) : delay;
     }
 
     public void setDelay(Integer delay) {
@@ -264,7 +280,7 @@ public class MigrationRule {
         return force;
     }
 
-    public Boolean getForce(URL consumerURL) {
+    public boolean getForce(URL consumerURL) {
         if (interfaceRules != null) {
             SubMigrationRule rule = interfaceRules.get(consumerURL.getDisplayServiceKey());
             if (rule != null) {
@@ -285,7 +301,7 @@ public class MigrationRule {
             }
         }
 
-        return force;
+        return force == null ? consumerURL.getParameter(MIGRATION_FORCE_KEY, false) : force;
     }
 
     public void setForce(Boolean force) {