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 2018/09/07 08:16:55 UTC

[incubator-dubbo] branch dev-metadata updated: Fix router problems: 1. TagRouter change from global to Application level 2. Add tag rule and condition rule support 3. Fix problem when building RouterChain 4. Change logic of getting invokers from router cache.

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

liujun pushed a commit to branch dev-metadata
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git


The following commit(s) were added to refs/heads/dev-metadata by this push:
     new 927a7e5  Fix router problems: 1. TagRouter change from global to Application level 2. Add tag rule and condition rule support 3. Fix problem when building RouterChain 4. Change logic of getting invokers from router cache.
927a7e5 is described below

commit 927a7e5afa18358174d244ec09d89d18b81e4a74
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Sep 7 16:16:38 2018 +0800

    Fix router problems:
    1. TagRouter change from global to Application level
    2. Add tag rule and condition rule support
    3. Fix problem when building RouterChain
    4. Change logic of getting invokers from router cache.
---
 .../org/apache/dubbo/rpc/cluster/RouterChain.java  |   1 -
 .../dubbo/rpc/cluster/router/AbstractRouter.java   |   7 +-
 .../rpc/cluster/router/AbstractRouterRule.java     |  45 ++++
 .../dubbo/rpc/cluster/router/InvokerTreeCache.java |  46 +++-
 .../cluster/router/condition/ConditionRouter.java  |   7 +-
 .../condition/config/ConfigConditionRouter.java    |  29 ++-
 .../config/ConfigConditionRouterFactory.java       |   2 +-
 .../config/model/ConditionRouterRule.java          |   4 +-
 .../dubbo/rpc/cluster/router/group/TagRouter.java  | 184 ---------------
 .../rpc/cluster/router/script/ScriptRouter.java    |  23 +-
 .../dubbo/rpc/cluster/router/tag/TagRouter.java    | 258 +++++++++++++++++++++
 .../router/{group => tag}/TagRouterFactory.java    |   8 +-
 .../GroupRuleParser.java => tag/model/Tag.java}    |  26 ++-
 .../cluster/router/tag/model/TagRouterRule.java    |  86 +++++++
 .../cluster/router/tag/model/TagRuleParser.java    |  45 ++++
 .../dubbo/rpc/cluster/support/ClusterUtils.java    |   5 +
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |   3 +-
 .../cluster/router/ConfigConditionRouterTest.java  | 134 +++++++++++
 .../dubbo/rpc/cluster/router/TagRouterTest.java    |  66 ++++++
 dubbo-cluster/src/test/resources/ConditionRule.yml |  37 +++
 dubbo-cluster/src/test/resources/TagRule.yml       |  12 +
 .../java/org/apache/dubbo/common/Constants.java    |   8 +-
 .../archaius/ArchaiusDynamicConfiguration.java     |   2 +-
 .../src/main/resources/META-INF/compat/dubbo.xsd   |   6 +
 .../src/main/resources/META-INF/dubbo.xsd          |   6 +
 .../registry/integration/RegistryDirectory.java    |   5 +
 .../registry/integration/RegistryProtocol.java     |   3 +-
 .../dubbo/registry/ConfigConditionRouterTest.java  |  62 +++++
 .../org/apache/dubbo/registry/TagRouterTest.java   |  38 +--
 .../src/test/resources/ConditionRule.yml           |   9 -
 30 files changed, 902 insertions(+), 265 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index c6be5e7..a1cb9d5 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -64,7 +64,6 @@ public class RouterChain<T> {
         this.url = url;
     }
 
-
     public void addRouter(Router router) {
         this.routers.add(router);
     }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouter.java
index ce8a65c..ff87e34 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouter.java
@@ -44,6 +44,10 @@ public abstract class AbstractRouter implements Router {
         return url;
     }
 
+    public void setUrl(URL url) {
+        this.url = url;
+    }
+
     @Override
     public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
         return invokers;
@@ -59,7 +63,8 @@ public abstract class AbstractRouter implements Router {
 
         if (isRuntime()) {
             map.put(TreeNode.FAILOVER_KEY, invokers);
-            return map;
+        } else {
+            map.put(TreeNode.FAILOVER_KEY, route(invokers, url, invocation));
         }
         return map;
     }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java
index 6dbbc95..cb6fa9c 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java
@@ -24,7 +24,12 @@ public abstract class AbstractRouterRule {
     private boolean runtime = false;
     private boolean force = false;
     private boolean valid = true;
+    private boolean enabled = true;
+    private int priority;
+    private boolean dynamic = false;
 
+    private String scope;
+    private String key;
 
     public String getRawRule() {
         return rawRule;
@@ -57,4 +62,44 @@ public abstract class AbstractRouterRule {
     public void setValid(boolean valid) {
         this.valid = valid;
     }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public boolean isDynamic() {
+        return dynamic;
+    }
+
+    public void setDynamic(boolean dynamic) {
+        this.dynamic = dynamic;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/InvokerTreeCache.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/InvokerTreeCache.java
index b8bfdc5..8575760 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/InvokerTreeCache.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/InvokerTreeCache.java
@@ -16,7 +16,10 @@
  */
 package org.apache.dubbo.rpc.cluster.router;
 
+import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 
@@ -32,39 +35,74 @@ public class InvokerTreeCache<T> {
 
     public TreeNode buildTree() {
         tree = new TreeNode<>();
+        tree.setRouterName("ROOT_ROUTER");
+        tree.setConditionValue("root");
+        tree.setConditionKey("root");
         return tree;
     }
 
     public List<Invoker<T>> getInvokers(TreeNode<T> node, URL url, Invocation invocation) {
+        // We have reached the leaf node.
         if (node.getChildren() == null || node.getChildren().size() == 0) {
             return node.getInvokers();
         }
 
-        if (node.getChildren().size() == 1) {
+        //
+       /* if (node.getChildren().size() == 1) {
             return getInvokers(node.getChildren().get(0), url, invocation);
-        }
+        }*/
 
         TreeNode<T> failoverNode = null;
         for (TreeNode<T> n : node.getChildren()) {
             String key = n.getConditionKey();
+            // if the key is FAILOVER, it indicates we have only one child in this level, just proceed on.
             if (TreeNode.FAILOVER_KEY.equals(key)) {
+                return getInvokers(n, url, invocation);
+            }
+            if (TreeNode.FAILOVER_KEY.equals(n.getConditionValue())) {
                 failoverNode = n;
                 continue;
             }
 
             //TODO key=method, but it will appear neither in url nor in attachments.
             String value = invocation.getAttachment(key, url.getParameter(key));
-            if (key.equals("method")) {
+            if (Constants.METHOD_KEY.equals(key)) {
                 value = invocation.getMethodName();
             }
 
+            // If we don't have a match condition in the request, then our goal would be find the failoverNode in this loop and continue match with failoverNode's children.
+            if (StringUtils.isEmpty(value)) {
+                if (failoverNode == null) {
+                    for (TreeNode<T> innerLoopNode : node.getChildren()) {
+                        if (innerLoopNode.getConditionValue().equals(TreeNode.FAILOVER_KEY)) {
+                            failoverNode = innerLoopNode;
+                        }
+                    }
+                }
+                // Router will guarantee that there's always a FAILOVER node.
+                // To make it more robust, we may need to add null check for failoverNode here.
+                return getInvokers(failoverNode, url, invocation);
+            }
+
+            // If the request condition matches with the node branch, go ahead.
             if (n.getConditionValue().equals(value)) {
-                if (n.getInvokers() != null || (n.getInvokers() == null && n.isForce()))
+                // If the invoker list in this node is empty, we need to check force to decide to return empty list or to seek for FAILOVER.
+                if (CollectionUtils.isNotEmpty(n.getInvokers()) || (CollectionUtils.isEmpty(n.getInvokers()) && n.isForce()))
                     return getInvokers(n, url, invocation);
             }
         }
 
+        // If we get here,
+        // 1. we must have several brothers in current node level.
+        // 2. there is a router match condition in the request.
+        // 3. the request value failed to match any of the values specified by the router rule.
         if (failoverNode != null) {
+            // What if force parameter comes from runtime? Use a convention format of 'force.xxx', for example, for TagRouter it would be 'force.tag'.
+            // FIXME check force logic here
+            String forceKey = "force." + failoverNode.getConditionKey();
+            if (Boolean.valueOf(invocation.getAttachment(forceKey, url.getParameter(forceKey, "false")))) {
+                return Collections.emptyList();
+            }
             return getInvokers(failoverNode, url, invocation);
         }
         return Collections.emptyList();
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java
index 3707f46..176aef9 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouter.java
@@ -184,7 +184,8 @@ public class ConditionRouter extends AbstractRouter implements Comparable<Router
 
     @Override
     public boolean isRuntime() {
-        // We always return true for previously defined Router, don't support cache.
+        // We always return true for previously defined Router, that is, old Router doesn't support cache anymore.
+//        return true;
         return this.url.getParameter(Constants.RUNTIME_KEY, false);
     }
 
@@ -224,6 +225,10 @@ public class ConditionRouter extends AbstractRouter implements Comparable<Router
             //get real invoked method name from invocation
             if (invocation != null && (Constants.METHOD_KEY.equals(key) || Constants.METHODS_KEY.equals(key))) {
                 sampleValue = invocation.getMethodName();
+            } else if (Constants.ADDRESS_KEY.equals(key)) {
+                sampleValue = url.getAddress();
+            } else if (Constants.HOST_KEY.equals(key)) {
+                sampleValue = url.getHost();
             } else {
                 sampleValue = sample.get(key);
                 if (sampleValue == null) {
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouter.java
index 370519b..68c51d6 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouter.java
@@ -53,11 +53,11 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
     private List<ConditionRouter> conditionRouters = new ArrayList<>();
     private List<ConditionRouter> appConditionRouters = new ArrayList<>();
 
-    public ConfigConditionRouter(DynamicConfiguration configuration) {
+    public ConfigConditionRouter(DynamicConfiguration configuration, URL url) {
         this.configuration = configuration;
         this.priority = -2;
         this.force = false;
-        this.url = configuration.getUrl();
+        this.url = url;
         try {
             String app = this.url.getParameter(Constants.APPLICATION_KEY);
             String serviceKey = this.url.getServiceKey();
@@ -110,11 +110,7 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
 
         if (CollectionUtils.isEmpty(invokers)
                 || (conditionRouters.size() == 0 && appConditionRouters.size() == 0)
-                ) {
-            return map;
-        }
-
-        if (isRuntime()) {
+                || isRuntime()) {
             map.put(TreeNode.FAILOVER_KEY, invokers);
             return map;
         }
@@ -133,6 +129,18 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
 
     @Override
     public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
+        if (CollectionUtils.isEmpty(invokers)
+                || (conditionRouters.size() == 0 && appConditionRouters.size() == 0)
+                ) {
+            return invokers;
+        }
+
+        for (Router router : appConditionRouters) {
+            invokers = router.route(invokers, url, invocation);
+        }
+        for (Router router : conditionRouters) {
+            invokers = router.route(invokers, url, invocation);
+        }
         return invokers;
     }
 
@@ -144,6 +152,7 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
 
     private void generateConditions() {
         if (routerRule != null && routerRule.isValid()) {
+            conditionRouters.clear();
             routerRule.getConditions().forEach(condition -> {
                 // All sub rules have the same force, runtime value.
                 ConditionRouter subRouter = new ConditionRouter(condition, routerRule.isForce());
@@ -154,6 +163,7 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
 
     private void generateAppConditions() {
         if (appRouterRule != null && appRouterRule.isValid()) {
+            appConditionRouters.clear();
             appRouterRule.getConditions().forEach(condition -> {
                 // All sub rules have the same force, runtime value.
                 ConditionRouter subRouter = new ConditionRouter(condition, appRouterRule.isForce());
@@ -164,12 +174,13 @@ public class ConfigConditionRouter extends AbstractRouter implements Configurati
 
     @Override
     public String getKey() {
-        return "";
+        return TreeNode.FAILOVER_KEY;
     }
 
     @Override
     public boolean isForce() {
-        return routerRule.isForce();
+        return (routerRule != null && routerRule.isForce())
+                || (appRouterRule != null && appRouterRule.isForce());
     }
 
     @Override
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouterFactory.java
index a833c5f..3eba3fc 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouterFactory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ConfigConditionRouterFactory.java
@@ -34,6 +34,6 @@ public class ConfigConditionRouterFactory implements RouterFactory {
 
     @Override
     public Router getRouter(DynamicConfiguration dynamicConfiguration, URL url) {
-        return new ConfigConditionRouter(dynamicConfiguration);
+        return new ConfigConditionRouter(dynamicConfiguration, url);
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java
index 0c426a3..7455585 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java
@@ -24,7 +24,9 @@ import java.util.List;
  *
  */
 public class ConditionRouterRule extends AbstractRouterRule {
-    private String scope;
+    public ConditionRouterRule() {
+    }
+
     private List<String> conditions;
 
     public List<String> getConditions() {
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouter.java
deleted file mode 100644
index de4dbf5..0000000
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouter.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.rpc.cluster.router.group;
-
-import org.apache.dubbo.common.Constants;
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.config.dynamic.ConfigChangeEvent;
-import org.apache.dubbo.config.dynamic.ConfigurationListener;
-import org.apache.dubbo.config.dynamic.DynamicConfiguration;
-import org.apache.dubbo.config.dynamic.DynamicConfigurationFactory;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Router;
-import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
-import org.apache.dubbo.rpc.cluster.router.TreeNode;
-import org.apache.dubbo.rpc.cluster.router.group.model.GroupRouterRule;
-import org.apache.dubbo.rpc.cluster.router.group.model.GroupRuleParser;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-/**
- *
- */
-public class TagRouter extends AbstractRouter implements Comparable<Router>, ConfigurationListener {
-    public static final String NAME = "TAG_ROUTER";
-    private static final Logger logger = LoggerFactory.getLogger(TagRouter.class);
-    private static final String TAGRULE_DATAID = ".tagrouters"; // acts
-    private static final String FAILOVER_TAG = "tag.failover";
-    private DynamicConfiguration configuration;
-    private GroupRouterRule groupRouterRule;
-
-    protected int priority = -1; // FIXME A fixed value considering other routers in the chain.
-
-    public TagRouter(URL url) {
-        this(ExtensionLoader.getExtensionLoader(DynamicConfigurationFactory.class).getAdaptiveExtension().getDynamicConfiguration(url));
-        this.url = url;
-    }
-
-    public TagRouter(DynamicConfiguration configuration) {
-        setConfiguration(configuration);
-    }
-
-    protected TagRouter() {
-    }
-
-    public void setConfiguration(DynamicConfiguration configuration) {
-        this.configuration = configuration;
-        this.url = configuration.getUrl();
-        init();
-    }
-
-    public void init() {
-        String rawRule = configuration.getConfig(url.getParameter(Constants.APPLICATION_KEY) + TAGRULE_DATAID, "dubbo", this);
-        this.groupRouterRule = GroupRuleParser.parse(rawRule);
-    }
-
-    @Override
-    public void process(ConfigChangeEvent event) {
-        String rawRule = event.getNewValue();
-        // remove, set groupRouterRule to null
-        // change, update groupRouterRule
-        routerChain.notifyRuleChanged();
-    }
-
-    @Override
-    public URL getUrl() {
-        return url;
-    }
-
-    @Override
-    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
-        if (CollectionUtils.isEmpty(invokers)) {
-            return invokers;
-        }
-        List<Invoker<T>> result = invokers;
-        String routeGroup = StringUtils.isEmpty(invocation.getAttachment(Constants.REQUEST_TAG_KEY)) ? url.getParameter(Constants.TAG_KEY) : invocation.getAttachment(Constants.REQUEST_TAG_KEY);
-        if (StringUtils.isNotEmpty(routeGroup)) {
-            String providerApp = invokers.get(0).getUrl().getParameter(Constants.APPLICATION_KEY);
-            List<String> addresses = groupRouterRule.filter(routeGroup, providerApp);
-            if (CollectionUtils.isNotEmpty(addresses)) {
-                result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));
-            } else {
-                result = filterInvoker(invokers, invoker -> invoker.getUrl().getParameter(Constants.TAG_KEY).equals(routeGroup));
-            }
-        }
-        if (StringUtils.isEmpty(routeGroup) || (CollectionUtils.isEmpty(result) && url.getParameter(FAILOVER_TAG, true))) {
-            result = filterInvoker(invokers, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(Constants.TAG_KEY)));
-        }
-        return result;
-    }
-
-    @Override
-    public <T> Map<String, List<Invoker<T>>> preRoute(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
-        Map<String, List<Invoker<T>>> map = new HashMap<>();
-
-        if (CollectionUtils.isEmpty(invokers) || groupRouterRule == null || !groupRouterRule.isValid()) {
-            return map;
-        }
-
-        if (isRuntime()) {
-            map.put(TreeNode.FAILOVER_KEY, invokers);
-            return map;
-        }
-
-        invokers.forEach(invoker -> {
-            String providerApp = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
-            String address = invoker.getUrl().getAddress();
-            String routeGroup = groupRouterRule.getIpAppToGroup().get(providerApp + address);
-            if (StringUtils.isEmpty(routeGroup)) {
-                routeGroup = invoker.getUrl().getParameter(Constants.TAG_KEY);
-            }
-            if (StringUtils.isEmpty(routeGroup)) {
-                routeGroup = TreeNode.FAILOVER_KEY;
-            }
-            List<Invoker<T>> subInvokers = map.computeIfAbsent(routeGroup, k -> new ArrayList<>());
-            subInvokers.add(invoker);
-        });
-
-        return map;
-    }
-
-    @Override
-    public String getName() {
-        return NAME;
-    }
-
-    @Override
-    public boolean isRuntime() {
-        return false;
-    }
-
-    public String getKey() {
-        return Constants.TAG_KEY;
-    }
-
-    @Override
-    public boolean isForce() {
-        return false;
-    }
-
-    public boolean isRuntime(Invocation invocation) {
-        return true;
-    }
-
-    private <T> List<Invoker<T>> filterInvoker(List<Invoker<T>> invokers, Predicate<Invoker<T>> predicate) {
-        return invokers.stream()
-                .filter(predicate)
-                .collect(Collectors.toList());
-    }
-
-    private boolean addressMatches(URL url, List<String> addresses) {
-        return addresses.contains(url.getAddress());
-    }
-
-    @Override
-    public int compareTo(Router o) {
-        return 0;
-    }
-}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
index c3f9815..25cf160 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java
@@ -20,12 +20,12 @@ import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
 import org.apache.dubbo.rpc.cluster.router.TreeNode;
 
 import javax.script.Bindings;
@@ -36,7 +36,6 @@ import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -45,7 +44,7 @@ import java.util.concurrent.ConcurrentHashMap;
  * ScriptRouter
  *
  */
-public class ScriptRouter implements Router {
+public class ScriptRouter extends AbstractRouter {
 
     private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
 
@@ -118,24 +117,8 @@ public class ScriptRouter implements Router {
     }
 
     @Override
-    public <T> Map<String, List<Invoker<T>>> preRoute(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
-        Map<String, List<Invoker<T>>> map = new HashMap<>();
-
-        if (CollectionUtils.isEmpty(invokers)) {
-            return map;
-        }
-
-        if (isRuntime()) {
-            map.put(TreeNode.FAILOVER_KEY, invokers);
-            return map;
-        }
-        return map;
-    }
-
-    @Override
     public boolean isRuntime() {
-        // ignore config in url
-        return true;
+        return this.url.getParameter(Constants.RUNTIME_KEY, false);
     }
 
     @Override
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java
new file mode 100644
index 0000000..008c1e5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java
@@ -0,0 +1,258 @@
+/*
+ * 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.rpc.cluster.router.tag;
+
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.config.dynamic.ConfigChangeEvent;
+import org.apache.dubbo.config.dynamic.ConfigChangeType;
+import org.apache.dubbo.config.dynamic.ConfigurationListener;
+import org.apache.dubbo.config.dynamic.DynamicConfiguration;
+import org.apache.dubbo.config.dynamic.DynamicConfigurationFactory;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
+import org.apache.dubbo.rpc.cluster.router.TreeNode;
+import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
+import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ *
+ */
+public class TagRouter extends AbstractRouter implements Comparable<Router>, ConfigurationListener {
+    public static final String NAME = "TAG_ROUTER";
+    private static final Logger logger = LoggerFactory.getLogger(TagRouter.class);
+    private static final String TAGROUTERRULES_DATAID = ".tagrouters"; // acts
+    private DynamicConfiguration configuration;
+    private TagRouterRule tagRouterRule;
+    private String application;
+
+    protected int priority = -1; // FIXME A fixed value considering other routers in the chain.
+
+    public TagRouter(URL url) {
+        this(ExtensionLoader.getExtensionLoader(DynamicConfigurationFactory.class).getAdaptiveExtension().getDynamicConfiguration(url), url);
+    }
+
+    public TagRouter(DynamicConfiguration configuration, URL url) {
+        setConfiguration(configuration);
+        this.url = url;
+    }
+
+    protected TagRouter() {
+    }
+
+    public void setConfiguration(DynamicConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    private void init() {
+        if (StringUtils.isEmpty(application)) {
+            logger.error("TagRouter must getConfig from or subscribe to a specific application, but the application in this TagRouter is not specified.");
+        }
+        String rawRule = this.configuration.getConfig(application + TAGROUTERRULES_DATAID, "dubbo", this);
+        if (StringUtils.isNotEmpty(rawRule)) {
+            this.tagRouterRule = TagRuleParser.parse(rawRule);
+        }
+    }
+
+    @Override
+    public void process(ConfigChangeEvent event) {
+        try {
+            if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
+                this.tagRouterRule = null;
+            } else {
+                this.tagRouterRule = TagRuleParser.parse(event.getNewValue());
+            }
+            routerChain.notifyRuleChanged();
+        } catch (Exception e) {
+            // TODO
+            logger.error(e);
+        }
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
+        if (CollectionUtils.isEmpty(invokers)) {
+            return invokers;
+        }
+
+        checkAndInit(invokers.get(0).getUrl());
+
+        if (tagRouterRule == null || !tagRouterRule.isValid()) {
+            return invokers;
+        }
+
+        List<Invoker<T>> result = invokers;
+        String tag = StringUtils.isEmpty(invocation.getAttachment(Constants.TAG_KEY)) ? url.getParameter(Constants.TAG_KEY) : invocation.getAttachment(Constants.TAG_KEY);
+        // if we are requesting for a Provider with a specific tag
+        if (StringUtils.isNotEmpty(tag)) {
+            List<String> addresses = tagRouterRule.getTagnameToAddresses().get(tag);
+            // filter by dynamic tag group first
+            if (CollectionUtils.isNotEmpty(addresses)) {
+                result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));
+                // if result is not null OR it's null but force=true, return result directly
+                if (CollectionUtils.isNotEmpty(result) || tagRouterRule.isForce()) {
+                    return result;
+                }
+            }
+            // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by dynamic tag group but force=false.
+            // check static tag
+            result = filterInvoker(invokers, invoker -> invoker.getUrl().getParameter(Constants.TAG_KEY).equals(tag));
+            if (CollectionUtils.isNotEmpty(result) || url.getParameter(Constants.FORCE_USE_TAG, true)) {
+                return result;
+            }
+            // FAILOVER: return all Providers without any tags.
+            else {
+                return filterInvoker(invokers, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(Constants.TAG_KEY)));
+            }
+        } else {
+            // List<String> addresses = tagRouterRule.filter(providerApp);
+            // return all addresses in dynamic tag group.
+            List<String> addresses = tagRouterRule.getAddresses();
+            if (CollectionUtils.isNotEmpty(addresses)) {
+                result = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(), addresses));
+                // 1. all addresses are in dynamic tag group, return empty list.
+                if (CollectionUtils.isEmpty(result)) {
+                    return result;
+                }
+                // 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the static tag group.
+            }
+            return filterInvoker(result, invoker -> {
+                String localTag = invoker.getUrl().getParameter(Constants.TAG_KEY);
+                if (StringUtils.isEmpty(localTag) || !tagRouterRule.getTagNames().contains(localTag)) {
+                    return true;
+                }
+                return false;
+            });
+        }
+    }
+
+    @Override
+    public <T> Map<String, List<Invoker<T>>> preRoute(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
+        Map<String, List<Invoker<T>>> map = new HashMap<>();
+
+        if (CollectionUtils.isEmpty(invokers)) {
+            return map;
+        }
+
+        checkAndInit(invokers.get(0).getUrl());
+
+        if (tagRouterRule == null || !tagRouterRule.isValid() || isRuntime()) {
+            map.put(TreeNode.FAILOVER_KEY, invokers);
+            return map;
+        }
+
+        invokers.forEach(invoker -> {
+            String address = invoker.getUrl().getAddress();
+            List<String> tags = tagRouterRule.getAddressToTagnames().get(address);
+            if (CollectionUtils.isEmpty(tags)) {
+                String tag = invoker.getUrl().getParameter(Constants.TAG_KEY);
+                // we have checked that this address is not included in any of the tag listed in dynamic tag group.
+                // so if found this address were grouped into one tag in dynamic tag group, we think it's invalid, which means, dynamic tag group will override static tag group (the dynamic config may have explicitly removed this address from one or another group).
+                if (tagRouterRule.getTagNames().contains(tag) || StringUtils.isEmpty(tag)) {
+                    tag = TreeNode.FAILOVER_KEY;
+                }
+                tags = new ArrayList<>();
+                tags.add(tag);
+            }
+
+            tags.forEach(tag -> {
+                List<Invoker<T>> subInvokers = map.computeIfAbsent(tag, k -> new ArrayList<>());
+                subInvokers.add(invoker);
+            });
+        });
+
+        // Now, FAILOVER key is required here.
+        map.putIfAbsent(TreeNode.FAILOVER_KEY, Collections.emptyList());
+
+        return map;
+    }
+
+    public void checkAndInit(URL providerUrl) {
+        if (StringUtils.isEmpty(application)) {
+            setApplication(providerUrl.getParameter(Constants.REMOTE_APPLICATION_KEY));
+        }
+        this.init();
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public boolean isRuntime() {
+        return tagRouterRule != null && tagRouterRule.isRuntime();
+    }
+
+    public String getKey() {
+        return Constants.TAG_KEY;
+    }
+
+    @Override
+    public boolean isForce() {
+        // FIXME
+        return tagRouterRule != null && tagRouterRule.isForce();
+    }
+
+    public boolean isRuntime(Invocation invocation) {
+        return true;
+    }
+
+    private <T> List<Invoker<T>> filterInvoker(List<Invoker<T>> invokers, Predicate<Invoker<T>> predicate) {
+        return invokers.stream()
+                .filter(predicate)
+                .collect(Collectors.toList());
+    }
+
+    private boolean addressMatches(URL url, List<String> addresses) {
+        return addresses.contains(url.getAddress());
+    }
+
+    private boolean addressNotMatches(URL url, List<String> addresses) {
+        return !addresses.contains(url.getAddress());
+    }
+
+    public void setApplication(String app) {
+        this.application = app;
+    }
+
+    @Override
+    public int compareTo(Router o) {
+        return 0;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouterFactory.java
similarity index 84%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouterFactory.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouterFactory.java
index 36b5f1e..cb103f3 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/TagRouterFactory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouterFactory.java
@@ -14,11 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.router.group;
+package org.apache.dubbo.rpc.cluster.router.tag;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.config.dynamic.DynamicConfiguration;
 import org.apache.dubbo.rpc.cluster.Router;
 import org.apache.dubbo.rpc.cluster.RouterFactory;
@@ -38,9 +37,10 @@ public class TagRouterFactory implements RouterFactory {
 
     @Override
     public Router getRouter(DynamicConfiguration dynamicConfiguration, URL url) {
-//        return new TagRouter(dynamicConfiguration);
-        TagRouter router = (TagRouter) ExtensionLoader.getExtensionLoader(Router.class).getExtension(NAME);
+        TagRouter router = new TagRouter(dynamicConfiguration, url);
+/*        TagRouter router = (TagRouter) ExtensionLoader.getExtensionLoader(Router.class).getExtension(NAME);
         router.setConfiguration(dynamicConfiguration);
+        router.setUrl(url);*/
         return router;
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java
similarity index 64%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRuleParser.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java
index 3be756f..28628a5 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRuleParser.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java
@@ -14,16 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.router.group.model;
+package org.apache.dubbo.rpc.cluster.router.tag.model;
+
+import java.util.List;
 
 /**
  *
  */
-public class GroupRuleParser {
+public class Tag {
+    private String name;
+    private List<String> addresses;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<String> getAddresses() {
+        return addresses;
+    }
 
-    public static GroupRouterRule parse(String rawRule) {
-        GroupRouterRule groupRouterRule = new GroupRouterRule();
-        groupRouterRule.setValid(true);
-        return groupRouterRule;
+    public void setAddresses(List<String> addresses) {
+        this.addresses = addresses;
     }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java
new file mode 100644
index 0000000..827518b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java
@@ -0,0 +1,86 @@
+/*
+ * 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.rpc.cluster.router.tag.model;
+
+import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * %YAML1.2
+ * ---
+ * force: true
+ * runtime: false
+ * enabled: true
+ * priority: 1
+ * key: demo-provider
+ * tags:
+ * - name: tag1
+ * addresses: [ip1, ip2]
+ * - name: tag2
+ * addresses: [ip3, ip4]
+ * ...
+ */
+public class TagRouterRule extends AbstractRouterRule {
+    private List<Tag> tags;
+
+    private Map<String, List<String>> addressToTagnames = new HashMap<>();
+    private Map<String, List<String>> tagnameToAddresses = new HashMap<>();
+
+    public void init() {
+        if (!isValid()) {
+            return;
+        }
+
+        tags.forEach(tag -> {
+            tagnameToAddresses.put(tag.getName(), tag.getAddresses());
+            tag.getAddresses().forEach(addr -> {
+                List<String> tagNames = addressToTagnames.computeIfAbsent(addr, k -> new ArrayList<>());
+                tagNames.add(tag.getName());
+            });
+        });
+    }
+
+    public List<String> getAddresses() {
+        return tags.stream().flatMap(tag -> tag.getAddresses().stream()).collect(Collectors.toList());
+    }
+
+    public List<String> getTagNames() {
+        return tags.stream().map(Tag::getName).collect(Collectors.toList());
+    }
+
+    public Map<String, List<String>> getAddressToTagnames() {
+        return addressToTagnames;
+    }
+
+
+    public Map<String, List<String>> getTagnameToAddresses() {
+        return tagnameToAddresses;
+    }
+
+    public List<Tag> getTags() {
+        return tags;
+    }
+
+    public void setTags(List<Tag> tags) {
+        this.tags = tags;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
new file mode 100644
index 0000000..4f6669f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java
@@ -0,0 +1,45 @@
+/*
+ * 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.rpc.cluster.router.tag.model;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+/**
+ *
+ */
+public class TagRuleParser {
+
+    public static TagRouterRule parse(String rawRule) {
+        Constructor constructor = new Constructor(TagRouterRule.class);
+        TypeDescription tagDescription = new TypeDescription(TagRouterRule.class);
+        tagDescription.addPropertyParameters("tags", Tag.class);
+        constructor.addTypeDescription(tagDescription);
+
+        Yaml yaml = new Yaml(constructor);
+        TagRouterRule rule = yaml.load(rawRule);
+        rule.setRawRule(rawRule);
+        if (CollectionUtils.isEmpty(rule.getTags())) {
+            rule.setValid(false);
+        }
+
+        rule.init();
+        return rule;
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
index 2b87555..1c62d9f 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -91,6 +91,11 @@ public class ClusterUtils {
             if (remoteTimestamp != null && remoteTimestamp.length() > 0) {
                 map.put(Constants.REMOTE_TIMESTAMP_KEY, remoteMap.get(Constants.TIMESTAMP_KEY));
             }
+
+            // TODO, for compatibility consideration, we cannot simply change the value behind APPLICATION_KEY from Consumer to Provider. So just add an extra key here.
+            // Reserve application name from provider.
+            map.put(Constants.REMOTE_APPLICATION_KEY, remoteMap.get(Constants.APPLICATION_KEY));
+
             // Combine filters and listeners on Provider and Consumer
             String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
             String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
index 178feb6..3c36311 100644
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
@@ -1,6 +1,5 @@
 file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory
 script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory
 condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
-tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
 configcondition=org.apache.dubbo.rpc.cluster.router.condition.config.ConfigConditionRouterFactory
-group=org.apache.dubbo.rpc.cluster.router.group.GroupRouterFactory
\ No newline at end of file
+tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/ConfigConditionRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/ConfigConditionRouterTest.java
new file mode 100644
index 0000000..70576d2
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/ConfigConditionRouterTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.rpc.cluster.router;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * FIXME This is not a formal UT
+ */
+public class ConfigConditionRouterTest {
+    private static CuratorFramework client;
+
+    @Before
+    public void init() {
+        client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+    }
+
+    @Test
+    public void normalConditionRuleApplicationLevelTest() {
+        String serviceStr = "---\n" +
+                "scope: application\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "enabled: true\n" +
+                "priority: 2\n" +
+                "key: demo-consumer\n" +
+                "conditions:\n" +
+                "  - method=sayHello => host=30.5.120.21\n" +
+                "  - method=routeMethod1 => host=30.5.120.21\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config/demo-consumer/routers";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void normalConditionRuleApplicationServiceLevelTest() {
+        String serviceStr = "---\n" +
+                "scope: application\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "enabled: true\n" +
+                "priority: 2\n" +
+                "key: demo-consumer\n" +
+                "conditions:\n" +
+                "  - interface=org.apache.dubbo.demo.DemoService&method=sayHello => host=30.5.120.21\n" +
+                "  - method=routeMethod1 => host=30.5.120.21\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config/demo-consumer/routers";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void normalConditionRuleServiceLevelTest() {
+        String serviceStr = "---\n" +
+                "scope: service\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "enabled: true\n" +
+                "priority: 1\n" +
+                "key: org.apache.dubbo.demo.DemoService\n" +
+                "conditions:\n" +
+                "  - method!=sayHello =>\n" +
+                "  - method=routeMethod1 => address=30.5.120.21:20880\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/routers";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void abnormalNoruleConditionRuleTest() {
+        String serviceStr = "---\n" +
+                "scope: service\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "enabled: true\n" +
+                "priority: 1\n" +
+                "key: org.apache.dubbo.demo.DemoService\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/routers";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void setData(String path, String data) throws Exception {
+        client.setData().forPath(path, data.getBytes());
+    }
+}
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
new file mode 100644
index 0000000..c3782e6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.rpc.cluster.router;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * FIXME This is not a formal UT
+ */
+public class TagRouterTest {
+    private static CuratorFramework client;
+
+    @Before
+    public void init() {
+        client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+    }
+
+    @Test
+    public void normalTagRuleTest() {
+        String serviceStr = "---\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "enabled: true\n" +
+                "priority: 1\n" +
+                "key: demo-provider\n" +
+                "tags:\n" +
+                "  - name: tag1\n" +
+                "    addresses: [\"30.5.120.21:20880\"]\n" +
+                "  - name: tag2\n" +
+                "    addresses: [\"30.5.120.21:20881\"]\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config/demo-provider/tagrouters";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void setData(String path, String data) throws Exception {
+        client.setData().forPath(path, data.getBytes());
+    }
+}
diff --git a/dubbo-cluster/src/test/resources/ConditionRule.yml b/dubbo-cluster/src/test/resources/ConditionRule.yml
new file mode 100644
index 0000000..081babc
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/ConditionRule.yml
@@ -0,0 +1,37 @@
+# application level, applies to all services
+---
+scope: application
+force: true
+runtime: false
+enabled: true
+priority: 1
+key: demo-consumer
+conditions:
+  - method!=sayHello => address=30.5.120.16:20880
+  - method=routeMethod1 =>
+...
+
+# application level, only applies to a specific service
+---
+scope: application
+force: true
+runtime: false
+enabled: true
+priority: 1
+key: demo-consumer
+conditions:
+  - interface=org.apache.dubbo.demo.DemoService&method!=sayHello => host=30.5.120.16
+  - interface=org.apache.dubbo.demo.DemoService&method=routeMethod1 => address=30.5.120.16:20880
+...
+
+---
+scope: service
+force: true
+runtime: false
+enabled: true
+priority: 1
+key: org.apache.dubbo.demo.DemoService
+conditions:
+  - method!=sayHello =>
+  - method=routeMethod1 => address=30.5.120.16:20880
+...
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/TagRule.yml b/dubbo-cluster/src/test/resources/TagRule.yml
new file mode 100644
index 0000000..2c222fe
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/TagRule.yml
@@ -0,0 +1,12 @@
+---
+force: true
+runtime: false
+enabled: true
+priority: 1
+key: demo-provider
+tags:
+  - name: tag1
+    addresses: [30.5.120.16:20880]
+  - name: tag2
+    addresses: [30.5.120.16:20881]
+...
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
index 73ce76d..0e82118 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Constants.java
@@ -204,6 +204,8 @@ public class Constants {
 
     public static final String APPLICATION_KEY = "application";
 
+    public static final String REMOTE_APPLICATION_KEY = "remote.application";
+
     public static final String LOCAL_KEY = "local";
 
     public static final String STUB_KEY = "stub";
@@ -661,7 +663,11 @@ public class Constants {
 
     public static final String TAG_KEY = "tag";
 
-    public static final String REQUEST_TAG_KEY = "request.tag";
+    public static final String FORCE_USE_TAG = "force.tag";
+
+    public static final String HOST_KEY = "host";
+
+    public static final String ADDRESS_KEY = "address";
 
     /*
      * private Constants(){ }
diff --git a/dubbo-config/dubbo-config-dynamic/src/main/java/org/apache/dubbo/config/dynamic/support/archaius/ArchaiusDynamicConfiguration.java b/dubbo-config/dubbo-config-dynamic/src/main/java/org/apache/dubbo/config/dynamic/support/archaius/ArchaiusDynamicConfiguration.java
index d0f68f6..979e884 100644
--- a/dubbo-config/dubbo-config-dynamic/src/main/java/org/apache/dubbo/config/dynamic/support/archaius/ArchaiusDynamicConfiguration.java
+++ b/dubbo-config/dubbo-config-dynamic/src/main/java/org/apache/dubbo/config/dynamic/support/archaius/ArchaiusDynamicConfiguration.java
@@ -105,7 +105,7 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration {
                 /**
                  * Works for any router rules:
                  * {@link Constants.ROUTERS_SUFFIX}
-                 * {@link org.apache.dubbo.rpc.cluster.router.group.TagRouter.TAGRULE_DATAID}
+                 * {@link org.apache.dubbo.rpc.cluster.router.tag.TagRouter.TAGRULE_DATAID}
                  */
                 type = ConfigType.ROUTERS;
             }
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
index 290c750..6263359 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
@@ -313,6 +313,12 @@
                             <![CDATA[ Defines the service tag]]></xsd:documentation>
                     </xsd:annotation>
                 </xsd:attribute>
+                <xsd:attribute name="enabledynamictag" type="xsd:boolean" default="false">
+                    <xsd:annotation>
+                        <xsd:documentation>
+                            <![CDATA[ Defines the service tag]]></xsd:documentation>
+                    </xsd:annotation>
+                </xsd:attribute>
                 <xsd:anyAttribute namespace="##other" processContents="lax"/>
             </xsd:extension>
         </xsd:complexContent>
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
index ecec23a..ed70850 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
@@ -313,6 +313,12 @@
                             <![CDATA[ Defines the service tag]]></xsd:documentation>
                     </xsd:annotation>
                 </xsd:attribute>
+                <xsd:attribute name="enabledynamictag" type="xsd:boolean" default="false">
+                    <xsd:annotation>
+                        <xsd:documentation>
+                            <![CDATA[ Defines the service tag]]></xsd:documentation>
+                    </xsd:annotation>
+                </xsd:attribute>
                 <xsd:anyAttribute namespace="##other" processContents="lax"/>
             </xsd:extension>
         </xsd:complexContent>
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index 4415bc2..c3403df 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -39,6 +39,7 @@ import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.Configurator;
 import org.apache.dubbo.rpc.cluster.ConfiguratorFactory;
 import org.apache.dubbo.rpc.cluster.Router;
+import org.apache.dubbo.rpc.cluster.RouterChain;
 import org.apache.dubbo.rpc.cluster.RouterFactory;
 import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
@@ -696,6 +697,10 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         this.dynamicConfiguration = dynamicConfiguration;
     }
 
+    public void buildRouterChain(DynamicConfiguration dynamicConfiguration) {
+        this.setRouterChain(RouterChain.buildChain(dynamicConfiguration, overrideDirectoryUrl));
+    }
+
     /**
      * Haomin: added for test purpose
      */
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 a46f006..dc86b55 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
@@ -44,7 +44,6 @@ import org.apache.dubbo.rpc.ProxyFactory;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.Configurator;
-import org.apache.dubbo.rpc.cluster.RouterChain;
 import org.apache.dubbo.rpc.protocol.InvokerWrapper;
 
 import java.util.ArrayList;
@@ -358,7 +357,7 @@ public class RegistryProtocol implements Protocol {
         directory.setRegistry(registry);
         directory.setProtocol(protocol);
         directory.setDynamicConfiguration(dynamicConfiguration);
-        directory.setRouterChain(RouterChain.buildChain(dynamicConfiguration, url));
+        directory.buildRouterChain(dynamicConfiguration);
         // all attributes of REFER_KEY
         Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
         URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ConfigConditionRouterTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ConfigConditionRouterTest.java
new file mode 100644
index 0000000..38aa554
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/ConfigConditionRouterTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.registry;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * FIXME This is not a formal UT
+ */
+public class ConfigConditionRouterTest {
+    private static CuratorFramework client;
+
+    @Before
+    public void init() {
+        client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+    }
+
+    @Test
+    public void normalConditionRuleTest() {
+        String serviceStr = "---\n" +
+                "scope: application\n" +
+                "force: true\n" +
+                "runtime: false\n" +
+                "conditions:\n" +
+                "  - method!=sayHello =>\n" +
+                "  - method=routeMethod1 => 30.5.121.156:20880\n" +
+                "...";
+        try {
+            String servicePath = "/dubbo/config//";
+            if (client.checkExists().forPath(servicePath) == null) {
+                client.create().creatingParentsIfNeeded().forPath(servicePath);
+            }
+            setData(servicePath, serviceStr);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void setData(String path, String data) throws Exception {
+        client.setData().forPath(path, data.getBytes());
+    }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRouterRule.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/TagRouterTest.java
similarity index 52%
rename from dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRouterRule.java
rename to dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/TagRouterTest.java
index 64516e6..42065bc 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/group/model/GroupRouterRule.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/TagRouterTest.java
@@ -14,31 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.cluster.router.group.model;
+package org.apache.dubbo.registry;
 
-import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
- * %YAML1.2
- *
+ * FIXME This is not a formal UT
  */
-public class GroupRouterRule extends AbstractRouterRule {
+public class TagRouterTest {
+    private static CuratorFramework client;
+
+    @Before
+    public void init() {
+        client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 60 * 1000, 60 * 1000,
+                new ExponentialBackoffRetry(1000, 3));
+        client.start();
+    }
 
-    // key: app+address, value: environment
-    private Map<String, String> ipAppToGroup;
+    @Test
+    public void normalTagRuleTest() {
 
-    public List<String> filter(String routeGroup, String app) {
-        return null;
     }
 
-    public Map<String, String> getIpAppToGroup() {
-        //FIXME
-        ipAppToGroup = new HashMap<>();
-        ipAppToGroup.put("demo-provider127.0.0.1", "test1");
-        return ipAppToGroup;
+    private void setData(String path, String data) throws Exception {
+        client.setData().forPath(path, data.getBytes());
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/test/resources/ConditionRule.yml b/dubbo-registry/dubbo-registry-api/src/test/resources/ConditionRule.yml
deleted file mode 100644
index 2b555d9..0000000
--- a/dubbo-registry/dubbo-registry-api/src/test/resources/ConditionRule.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-scope: application
-force: true
-runtime: false
-key: group/service:version
-conditions:
-  - method!=sayHello =>
-  - method=routeMethod1 => 30.5.121.156:20880
-...
\ No newline at end of file