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

victory pushed a commit to branch 2.6.x
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git


The following commit(s) were added to refs/heads/2.6.x by this push:
     new 3375ec8  feature:import TagRouter (#3065)
3375ec8 is described below

commit 3375ec827845a0b133eae1fd4aad78aeb203361a
Author: xujingfeng <[email protected]>
AuthorDate: Thu Jan 17 16:47:53 2019 +0800

    feature:import TagRouter (#3065)
---
 .../java/com/alibaba/dubbo/rpc/cluster/Router.java |   9 +-
 .../rpc/cluster/directory/AbstractDirectory.java   |   2 +
 .../dubbo/rpc/cluster/router/AbstractRouter.java   |  40 ++
 .../rpc/cluster/router/MockInvokersSelector.java   |  20 +-
 .../cluster/router/condition/ConditionRouter.java  |   9 +-
 .../rpc/cluster/router/script/ScriptRouter.java    |  27 +-
 .../dubbo/rpc/cluster/router/tag/TagRouter.java    |  79 ++++
 .../rpc/cluster/loadbalance/LoadBalanceTest.java   |  14 +-
 .../rpc/cluster/router/tag/TagRouterTest.java      | 153 +++++++
 .../java/com/alibaba/dubbo/common/Constants.java   |   4 +
 .../dubbo/config/AbstractServiceConfig.java        | 498 +++++++++++----------
 .../alibaba/dubbo/config/annotation/Service.java   |   2 +
 .../dubbo/config/spring/AnnotationBean.java        |   3 +
 .../src/main/resources/META-INF/compat/dubbo.xsd   |   5 +
 .../src/main/resources/META-INF/dubbo.xsd          |   5 +
 .../registry/integration/RegistryDirectory.java    |   3 +-
 .../registry/dubbo/RegistryDirectoryTest.java      |  10 +-
 .../protocol/dubbo/DubboInvokerAvilableTest.java   |  13 +-
 18 files changed, 594 insertions(+), 302 deletions(-)

diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
index a5eea1c..f447183 100644
--- a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
@@ -31,7 +31,7 @@ import java.util.List;
  * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
  * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation)
  */
-public interface Router extends Comparable<Router> {
+public interface Router extends Comparable<Router>{
 
     /**
      * get the router url.
@@ -51,4 +51,11 @@ public interface Router extends Comparable<Router> {
      */
     <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation 
invocation) throws RpcException;
 
+    /**
+     * Router's priority, used to sort routers.
+     *
+     * @return router's priority
+     */
+    int getPriority();
+
 }
\ No newline at end of file
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
index 6934069..31764af 100644
--- 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -28,6 +28,7 @@ import com.alibaba.dubbo.rpc.cluster.Directory;
 import com.alibaba.dubbo.rpc.cluster.Router;
 import com.alibaba.dubbo.rpc.cluster.RouterFactory;
 import com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector;
+import com.alibaba.dubbo.rpc.cluster.router.tag.TagRouter;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -107,6 +108,7 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
         }
         // append mock invoker selector
         routers.add(new MockInvokersSelector());
+        routers.add(new TagRouter());
         Collections.sort(routers);
         this.routers = routers;
     }
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/AbstractRouter.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/AbstractRouter.java
new file mode 100644
index 0000000..6b5cbcf
--- /dev/null
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/AbstractRouter.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.alibaba.dubbo.rpc.cluster.router;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+public abstract class AbstractRouter implements Router {
+
+    protected URL url;
+    protected int priority;
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public int compareTo(Router o) {
+        return (this.getPriority() < o.getPriority()) ? -1 : 
((this.getPriority() == o.getPriority()) ? 0 : 1);
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+}
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
index d406f78..089c8da 100644
--- 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/MockInvokersSelector.java
@@ -21,7 +21,6 @@ import com.alibaba.dubbo.common.URL;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.RpcException;
-import com.alibaba.dubbo.rpc.cluster.Router;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -29,9 +28,14 @@ import java.util.List;
 /**
  * A specific Router designed to realize mock feature.
  * If a request is configured to use mock, then this router guarantees that 
only the invokers with protocol MOCK appear in final the invoker list, all 
other invokers will be excluded.
- *
  */
-public class MockInvokersSelector implements Router {
+public class MockInvokersSelector extends AbstractRouter {
+
+    private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
+
+    public MockInvokersSelector() {
+        this.priority = DEFAULT_PRIORITY;
+    }
 
     @Override
     public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
@@ -87,14 +91,4 @@ public class MockInvokersSelector implements Router {
         return hasMockProvider;
     }
 
-    @Override
-    public URL getUrl() {
-        return null;
-    }
-
-    @Override
-    public int compareTo(Router o) {
-        return 1;
-    }
-
 }
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
index 05f1d71..961b66a 100644
--- 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/condition/ConditionRouter.java
@@ -27,6 +27,7 @@ import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.RpcException;
 import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.router.AbstractRouter;
 
 import java.text.ParseException;
 import java.util.ArrayList;
@@ -40,21 +41,19 @@ import java.util.regex.Pattern;
 
 /**
  * ConditionRouter
- *
  */
-public class ConditionRouter implements Router, Comparable<Router> {
+public class ConditionRouter extends AbstractRouter {
 
     private static final Logger logger = 
LoggerFactory.getLogger(ConditionRouter.class);
+    private static final int DEFAULT_PRIORITY = 2;
     private static Pattern ROUTE_PATTERN = 
Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
-    private final URL url;
-    private final int priority;
     private final boolean force;
     private final Map<String, MatchPair> whenCondition;
     private final Map<String, MatchPair> thenCondition;
 
     public ConditionRouter(URL url) {
         this.url = url;
-        this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
+        this.priority = url.getParameter(Constants.PRIORITY_KEY, 
DEFAULT_PRIORITY);
         this.force = url.getParameter(Constants.FORCE_KEY, false);
         try {
             String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
index 74938c6..89241ad 100644
--- 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/script/ScriptRouter.java
@@ -24,7 +24,7 @@ import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.RpcContext;
 import com.alibaba.dubbo.rpc.RpcException;
-import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.router.AbstractRouter;
 
 import javax.script.Bindings;
 import javax.script.Compilable;
@@ -40,26 +40,23 @@ 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);
 
+    private static final int DEFAULT_PRIORITY = 1;
+
     private static final Map<String, ScriptEngine> engines = new 
ConcurrentHashMap<String, ScriptEngine>();
 
     private final ScriptEngine engine;
 
-    private final int priority;
-
     private final String rule;
 
-    private final URL url;
-
     public ScriptRouter(URL url) {
         this.url = url;
         String type = url.getParameter(Constants.TYPE_KEY);
-        this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
+        this.priority = url.getParameter(Constants.PRIORITY_KEY, 
DEFAULT_PRIORITY);
         String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
         if (type == null || type.length() == 0) {
             type = Constants.DEFAULT_SCRIPT_TYPE_KEY;
@@ -80,11 +77,6 @@ public class ScriptRouter implements Router {
     }
 
     @Override
-    public URL getUrl() {
-        return url;
-    }
-
-    @Override
     @SuppressWarnings("unchecked")
     public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, 
Invocation invocation) throws RpcException {
         try {
@@ -114,13 +106,4 @@ public class ScriptRouter implements Router {
         }
     }
 
-    @Override
-    public int compareTo(Router o) {
-        if (o == null || o.getClass() != ScriptRouter.class) {
-            return 1;
-        }
-        ScriptRouter c = (ScriptRouter) o;
-        return this.priority == c.priority ? rule.compareTo(c.rule) : 
(this.priority > c.priority ? 1 : -1);
-    }
-
 }
diff --git 
a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouter.java
 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouter.java
new file mode 100644
index 0000000..b1b0d4f
--- /dev/null
+++ 
b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouter.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.alibaba.dubbo.rpc.cluster.router.tag;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.router.AbstractRouter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TagRouter
+ */
+public class TagRouter extends AbstractRouter {
+
+    private static final int DEFAULT_PRIORITY = 100;
+    private static final URL ROUTER_URL = new URL("tag", 
Constants.ANYHOST_VALUE, 0, 
Constants.ANY_VALUE).addParameters(Constants.RUNTIME_KEY, "true");
+
+    public TagRouter() {
+        this.url = ROUTER_URL;
+        this.priority = url.getParameter(Constants.PRIORITY_KEY, 
DEFAULT_PRIORITY);
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, 
Invocation invocation) throws RpcException {
+        // filter
+        List<Invoker<T>> result = new ArrayList<Invoker<T>>();
+        // Dynamic param
+        String tag = RpcContext.getContext().getAttachment(Constants.TAG_KEY);
+        // Tag request
+        if (!StringUtils.isEmpty(tag)) {
+            // Select tag invokers first
+            for (Invoker<T> invoker : invokers) {
+                if 
(tag.equals(invoker.getUrl().getParameter(Constants.TAG_KEY))) {
+                    result.add(invoker);
+                }
+            }
+        }
+        // If Constants.REQUEST_TAG_KEY unspecified or no invoker be selected, 
downgrade to normal invokers
+        if (result.isEmpty()) {
+            // Only forceTag = true force match, otherwise downgrade
+            String forceTag = 
RpcContext.getContext().getAttachment(Constants.FORCE_USE_TAG);
+            if (StringUtils.isEmpty(forceTag) || "false".equals(forceTag)) {
+                for (Invoker<T> invoker : invokers) {
+                    if 
(StringUtils.isEmpty(invoker.getUrl().getParameter(Constants.TAG_KEY))) {
+                        result.add(invoker);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+}
diff --git 
a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
index d231c63..088f781 100644
--- 
a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
+++ 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
@@ -123,7 +123,7 @@ public class LoadBalanceTest {
     
     @Test
     public void testRoundRobinLoadBalance_select() {
-        int runs = 10000;
+        int runs = 1000;
         Map<Invoker, AtomicLong> counter = getInvokeCounter(runs, 
RoundRobinLoadBalance.NAME);
         for (Invoker minvoker : counter.keySet()) {
             Long count = counter.get(minvoker).get();
@@ -146,7 +146,7 @@ public class LoadBalanceTest {
     public void testRoundRobinLoadBalanceWithWeight() {
         final Map<Invoker, InvokeResult> totalMap = new HashMap<Invoker, 
InvokeResult>();
         final AtomicBoolean shouldBegin = new AtomicBoolean(false);
-        final int runs = 10000;
+        final int runs = 1000;
         List<Thread> threads = new ArrayList<Thread>();
         int threadNum = 10;
         for (int i = 0; i < threadNum; i ++) {
@@ -188,7 +188,7 @@ public class LoadBalanceTest {
     
     @Test
     public void testRoundRobinLoadBalanceWithWeightShouldNotRecycle() {
-        int runs = 10000;
+        int runs = 1000;
         //tmperately add a new invoker
         weightInvokers.add(weightInvokerTmp);
         try {
@@ -226,7 +226,7 @@ public class LoadBalanceTest {
                 Assert.assertTrue("getField failed", true);
             }
         }
-        int runs = 10000;
+        int runs = 1000;
         //temporarily add a new invoker
         weightInvokers.add(weightInvokerTmp);
         try {
@@ -249,7 +249,7 @@ public class LoadBalanceTest {
     public void testSelectByWeightLeastActive() {
         int sumInvoker1 = 0;
         int sumInvoker2 = 0;
-        int loop = 10000;
+        int loop = 1000;
         LeastActiveLoadBalance lb = new LeastActiveLoadBalance();
         for (int i = 0; i < loop; i++) {
             Invoker selected = lb.select(weightInvokers, null, 
weightTestInvocation);
@@ -273,7 +273,7 @@ public class LoadBalanceTest {
         int sumInvoker1 = 0;
         int sumInvoker2 = 0;
         int sumInvoker3 = 0;
-        int loop = 10000;
+        int loop = 1000;
         RandomLoadBalance lb = new RandomLoadBalance();
         for (int i = 0; i < loop; i++) {
             Invoker selected = lb.select(weightInvokers, null, 
weightTestInvocation);
@@ -323,7 +323,7 @@ public class LoadBalanceTest {
 
     @Test
     public void testLeastActiveLoadBalance_select() {
-        int runs = 10000;
+        int runs = 1000;
         Map<Invoker, AtomicLong> counter = getInvokeCounter(runs, 
LeastActiveLoadBalance.NAME);
         for (Invoker minvoker : counter.keySet()) {
             Long count = counter.get(minvoker).get();
diff --git 
a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouterTest.java
 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouterTest.java
new file mode 100644
index 0000000..7b400b7
--- /dev/null
+++ 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouterTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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 com.alibaba.dubbo.rpc.cluster.router.tag;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.router.MockInvoker;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TagRouterTest {
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @Test
+    public void testRoute_matchTag() {
+        RpcContext.getContext().setAttachment(Constants.TAG_KEY, "red");
+
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        Invoker<String> redInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red"));
+        Invoker<String> yellowInvoker = new MockInvoker<String>(URL.valueOf(
+                
"dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow"));
+        Invoker<String> blueInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue"));
+        Invoker<String> defaultInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.4:20880/com.foo.BarService"));
+
+        invokers.add(redInvoker);
+        invokers.add(yellowInvoker);
+        invokers.add(blueInvoker);
+        invokers.add(defaultInvoker);
+
+        Router tagRouter = new TagRouter();
+        List<Invoker<String>> filteredInvokers = tagRouter.route(invokers, 
URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), 
new RpcInvocation());
+        Assert.assertTrue(filteredInvokers.contains(redInvoker));
+        Assert.assertFalse(filteredInvokers.contains(yellowInvoker));
+        Assert.assertFalse(filteredInvokers.contains(blueInvoker));
+        Assert.assertFalse(filteredInvokers.contains(defaultInvoker));
+    }
+
+    @Test
+    public void testRoute_matchDefault() {
+
+        RpcContext.getContext().setAttachment(Constants.TAG_KEY, "");
+
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        Invoker<String> redInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red"));
+        Invoker<String> yellowInvoker = new MockInvoker<String>(URL.valueOf(
+                
"dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow"));
+        Invoker<String> blueInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue"));
+        Invoker<String> defaultInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.4:20880/com.foo.BarService"));
+
+        invokers.add(redInvoker);
+        invokers.add(yellowInvoker);
+        invokers.add(blueInvoker);
+        invokers.add(defaultInvoker);
+
+        Router tagRouter = new TagRouter();
+        List<Invoker<String>> filteredInvokers = tagRouter.route(invokers, 
URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), 
new RpcInvocation());
+        Assert.assertTrue(filteredInvokers.contains(defaultInvoker));
+        Assert.assertFalse(filteredInvokers.contains(yellowInvoker));
+        Assert.assertFalse(filteredInvokers.contains(blueInvoker));
+        Assert.assertFalse(filteredInvokers.contains(redInvoker));
+    }
+
+    @Test
+    public void testRoute_requestWithTag_shouldDowngrade() {
+
+        RpcContext.getContext().setAttachment(Constants.TAG_KEY, "black");
+
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        Invoker<String> redInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red"));
+        Invoker<String> yellowInvoker = new MockInvoker<String>(URL.valueOf(
+                
"dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow"));
+        Invoker<String> blueInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue"));
+        Invoker<String> defaultInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.4:20880/com.foo.BarService"));
+
+        invokers.add(redInvoker);
+        invokers.add(yellowInvoker);
+        invokers.add(blueInvoker);
+        invokers.add(defaultInvoker);
+
+        Router tagRouter = new TagRouter();
+        List<Invoker<String>> filteredInvokers = tagRouter.route(invokers, 
URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), 
new RpcInvocation());
+        Assert.assertTrue(filteredInvokers.contains(defaultInvoker));
+        Assert.assertFalse(filteredInvokers.contains(yellowInvoker));
+        Assert.assertFalse(filteredInvokers.contains(blueInvoker));
+        Assert.assertFalse(filteredInvokers.contains(redInvoker));
+    }
+
+    @Test
+    public void testRoute_requestWithoutTag_shouldNotDowngrade() {
+
+        RpcContext.getContext().setAttachment(Constants.TAG_KEY, "");
+
+        List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+        Invoker<String> redInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red"));
+        Invoker<String> yellowInvoker = new MockInvoker<String>(URL.valueOf(
+                
"dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow"));
+        Invoker<String> blueInvoker = new MockInvoker<String>(URL.valueOf(
+                "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue"));
+
+        invokers.add(redInvoker);
+        invokers.add(yellowInvoker);
+        invokers.add(blueInvoker);
+
+        Router tagRouter = new TagRouter();
+        List<Invoker<String>> filteredInvokers = tagRouter.route(invokers, 
URL.valueOf("consumer://" + NetUtils.getLocalHost() + "/com.foo.BarService"), 
new RpcInvocation());
+        Assert.assertEquals(0, filteredInvokers.size());
+    }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java 
b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
index 02d70ea..cedb20b 100644
--- a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
@@ -637,6 +637,10 @@ public class Constants {
 
     public static final String SERVICE_IMPL_CLASS = "service.classimpl";
 
+    public static final String TAG_KEY = "dubbo.tag";
+
+    public static final String FORCE_USE_TAG = "dubbo.force.tag";
+
     /*
      * private Constants(){ }
      */
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
 
b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
index 6890398..7d242ff 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
@@ -1,243 +1,255 @@
-/*
- * 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 com.alibaba.dubbo.config;
-
-import com.alibaba.dubbo.common.Constants;
-import com.alibaba.dubbo.config.support.Parameter;
-import com.alibaba.dubbo.rpc.ExporterListener;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * AbstractServiceConfig
- *
- * @export
- */
-public abstract class AbstractServiceConfig extends AbstractInterfaceConfig {
-
-    private static final long serialVersionUID = 1L;
-
-    // version
-    protected String version;
-
-    // group
-    protected String group;
-
-    // whether the service is deprecated
-    protected Boolean deprecated;
-
-    // delay service exporting
-    protected Integer delay;
-
-    // whether to export the service
-    protected Boolean export;
-
-    // weight
-    protected Integer weight;
-
-    // document center
-    protected String document;
-
-    // whether to register as a dynamic service or not on register center
-    protected Boolean dynamic;
-
-    // whether to use token
-    protected String token;
-
-    // access log
-    protected String accesslog;
-    protected List<ProtocolConfig> protocols;
-    // max allowed execute times
-    private Integer executes;
-    // whether to register
-    private Boolean register;
-
-    // warm up period
-    private Integer warmup;
-
-    // serialization
-    private String serialization;
-
-    public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        checkKey("version", version);
-        this.version = version;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public void setGroup(String group) {
-        checkKey("group", group);
-        this.group = group;
-    }
-
-    public Integer getDelay() {
-        return delay;
-    }
-
-    public void setDelay(Integer delay) {
-        this.delay = delay;
-    }
-
-    public Boolean getExport() {
-        return export;
-    }
-
-    public void setExport(Boolean export) {
-        this.export = export;
-    }
-
-    public Integer getWeight() {
-        return weight;
-    }
-
-    public void setWeight(Integer weight) {
-        this.weight = weight;
-    }
-
-    @Parameter(escaped = true)
-    public String getDocument() {
-        return document;
-    }
-
-    public void setDocument(String document) {
-        this.document = document;
-    }
-
-    public String getToken() {
-        return token;
-    }
-
-    public void setToken(String token) {
-        checkName("token", token);
-        this.token = token;
-    }
-
-    public void setToken(Boolean token) {
-        if (token == null) {
-            setToken((String) null);
-        } else {
-            setToken(String.valueOf(token));
-        }
-    }
-
-    public Boolean isDeprecated() {
-        return deprecated;
-    }
-
-    public void setDeprecated(Boolean deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    public Boolean isDynamic() {
-        return dynamic;
-    }
-
-    public void setDynamic(Boolean dynamic) {
-        this.dynamic = dynamic;
-    }
-
-    public List<ProtocolConfig> getProtocols() {
-        return protocols;
-    }
-
-    @SuppressWarnings({"unchecked"})
-    public void setProtocols(List<? extends ProtocolConfig> protocols) {
-        this.protocols = (List<ProtocolConfig>) protocols;
-    }
-
-    public ProtocolConfig getProtocol() {
-        return protocols == null || protocols.isEmpty() ? null : 
protocols.get(0);
-    }
-
-    public void setProtocol(ProtocolConfig protocol) {
-        this.protocols = Arrays.asList(protocol);
-    }
-
-    public String getAccesslog() {
-        return accesslog;
-    }
-
-    public void setAccesslog(String accesslog) {
-        this.accesslog = accesslog;
-    }
-
-    public void setAccesslog(Boolean accesslog) {
-        if (accesslog == null) {
-            setAccesslog((String) null);
-        } else {
-            setAccesslog(String.valueOf(accesslog));
-        }
-    }
-
-    public Integer getExecutes() {
-        return executes;
-    }
-
-    public void setExecutes(Integer executes) {
-        this.executes = executes;
-    }
-
-    @Override
-    @Parameter(key = Constants.SERVICE_FILTER_KEY, append = true)
-    public String getFilter() {
-        return super.getFilter();
-    }
-
-    @Override
-    @Parameter(key = Constants.EXPORTER_LISTENER_KEY, append = true)
-    public String getListener() {
-        return listener;
-    }
-
-    @Override
-    public void setListener(String listener) {
-        checkMultiExtension(ExporterListener.class, "listener", listener);
-        this.listener = listener;
-    }
-
-    public Boolean isRegister() {
-        return register;
-    }
-
-    public void setRegister(Boolean register) {
-        this.register = register;
-    }
-
-    public Integer getWarmup() {
-        return warmup;
-    }
-
-    public void setWarmup(Integer warmup) {
-        this.warmup = warmup;
-    }
-
-    public String getSerialization() {
-        return serialization;
-    }
-
-    public void setSerialization(String serialization) {
-        this.serialization = serialization;
-    }
-
-}
+/*
+ * 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 com.alibaba.dubbo.config;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.config.support.Parameter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * AbstractServiceConfig
+ *
+ * @export
+ */
+public abstract class AbstractServiceConfig extends AbstractInterfaceConfig {
+
+    private static final long serialVersionUID = 1L;
+
+    // version
+    protected String version;
+
+    // group
+    protected String group;
+
+    // whether the service is deprecated
+    protected Boolean deprecated;
+
+    // delay service exporting
+    protected Integer delay;
+
+    // whether to export the service
+    protected Boolean export;
+
+    // weight
+    protected Integer weight;
+
+    // document center
+    protected String document;
+
+    // whether to register as a dynamic service or not on register center
+    protected Boolean dynamic;
+
+    // whether to use token
+    protected String token;
+
+    // access log
+    protected String accesslog;
+    protected List<ProtocolConfig> protocols;
+    // max allowed execute times
+    private Integer executes;
+    // whether to register
+    private Boolean register;
+
+    // warm up period
+    private Integer warmup;
+
+    // serialization
+    private String serialization;
+
+    // provider tag
+    protected String tag;
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        checkKey("version", version);
+        this.version = version;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        checkKey("group", group);
+        this.group = group;
+    }
+
+    public Integer getDelay() {
+        return delay;
+    }
+
+    public void setDelay(Integer delay) {
+        this.delay = delay;
+    }
+
+    public Boolean getExport() {
+        return export;
+    }
+
+    public void setExport(Boolean export) {
+        this.export = export;
+    }
+
+    public Integer getWeight() {
+        return weight;
+    }
+
+    public void setWeight(Integer weight) {
+        this.weight = weight;
+    }
+
+    @Parameter(escaped = true)
+    public String getDocument() {
+        return document;
+    }
+
+    public void setDocument(String document) {
+        this.document = document;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        checkName("token", token);
+        this.token = token;
+    }
+
+    public void setToken(Boolean token) {
+        if (token == null) {
+            setToken((String) null);
+        } else {
+            setToken(String.valueOf(token));
+        }
+    }
+
+    public Boolean isDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(Boolean deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public Boolean isDynamic() {
+        return dynamic;
+    }
+
+    public void setDynamic(Boolean dynamic) {
+        this.dynamic = dynamic;
+    }
+
+    public List<ProtocolConfig> getProtocols() {
+        return protocols;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void setProtocols(List<? extends ProtocolConfig> protocols) {
+        this.protocols = (List<ProtocolConfig>) protocols;
+    }
+
+    public ProtocolConfig getProtocol() {
+        return protocols == null || protocols.isEmpty() ? null : 
protocols.get(0);
+    }
+
+    public void setProtocol(ProtocolConfig protocol) {
+        this.protocols = Arrays.asList(protocol);
+    }
+
+    public String getAccesslog() {
+        return accesslog;
+    }
+
+    public void setAccesslog(String accesslog) {
+        this.accesslog = accesslog;
+    }
+
+    public void setAccesslog(Boolean accesslog) {
+        if (accesslog == null) {
+            setAccesslog((String) null);
+        } else {
+            setAccesslog(String.valueOf(accesslog));
+        }
+    }
+
+    public Integer getExecutes() {
+        return executes;
+    }
+
+    public void setExecutes(Integer executes) {
+        this.executes = executes;
+    }
+
+    @Override
+    @Parameter(key = Constants.SERVICE_FILTER_KEY, append = true)
+    public String getFilter() {
+        return super.getFilter();
+    }
+
+    @Override
+    @Parameter(key = Constants.EXPORTER_LISTENER_KEY, append = true)
+    public String getListener() {
+        return listener;
+    }
+
+    @Override
+    public void setListener(String listener) {
+        checkMultiExtension(ExporterListener.class, "listener", listener);
+        this.listener = listener;
+    }
+
+    public Boolean isRegister() {
+        return register;
+    }
+
+    public void setRegister(Boolean register) {
+        this.register = register;
+    }
+
+    public Integer getWarmup() {
+        return warmup;
+    }
+
+    public void setWarmup(Integer warmup) {
+        this.warmup = warmup;
+    }
+
+    public String getSerialization() {
+        return serialization;
+    }
+
+    public void setSerialization(String serialization) {
+        this.serialization = serialization;
+    }
+
+    @Parameter(key = "dubbo.tag")
+    public String getTag() {
+        return tag;
+    }
+
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+}
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
 
b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
index 4d68227..0f094f9 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/annotation/Service.java
@@ -120,4 +120,6 @@ public @interface Service {
 
     String[] registry() default {};
 
+    String tag() default "";
+
 }
diff --git 
a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
 
b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
index 722c6d0..84a01f8 100644
--- 
a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
+++ 
b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/AnnotationBean.java
@@ -185,6 +185,9 @@ public class AnnotationBean extends AbstractConfig 
implements DisposableBean, Be
                     }
                     serviceConfig.setProtocols(protocolConfigs);
                 }
+                if (service.tag().length() > 0) {
+                    serviceConfig.setTag(service.tag());
+                }
                 try {
                     serviceConfig.afterPropertiesSet();
                 } catch (RuntimeException e) {
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 c48e2c3..ffa74ca 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
@@ -312,6 +312,11 @@
                         <xsd:documentation><![CDATA[ The serialization 
protocol of service. ]]></xsd:documentation>
                     </xsd:annotation>
                 </xsd:attribute>
+                <xsd:attribute name="tag" type="xsd:string" use="optional">
+                    <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 40a7ac6..19361d6 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
@@ -312,6 +312,11 @@
                         <xsd:documentation><![CDATA[ The serialization 
protocol of service. ]]></xsd:documentation>
                     </xsd:annotation>
                 </xsd:attribute>
+                <xsd:attribute name="tag" type="xsd:string" use="optional">
+                    <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/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
index 4bd0908..68ecc0e 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/integration/RegistryDirectory.java
@@ -445,7 +445,8 @@ public class RegistryDirectory<T> extends 
AbstractDirectory<T> implements Notify
         List<Router> routers = getRouters();
         if (routers != null) {
             for (Router router : routers) {
-                if (router.getUrl() != null) {
+                // If router's url not null and is not route by runtime,we 
filter invokers here
+                if (router.getUrl() != null && 
!router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
                     invokers = router.route(invokers, getConsumerUrl(), 
invocation);
                 }
             }
diff --git 
a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
 
b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
index 3463407..fca493b 100644
--- 
a/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
+++ 
b/dubbo-registry/dubbo-registry-default/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -545,19 +545,19 @@ public class RegistryDirectoryTest {
         registryDirectory.notify(serviceUrls);
         List<Router> routers = registryDirectory.getRouters();
         //default invocation selector
-        Assert.assertEquals(1 + 1, routers.size());
+        Assert.assertEquals(3, routers.size());
         Assert.assertTrue(ScriptRouter.class == routers.get(1).getClass() || 
ScriptRouter.class == routers.get(0).getClass());
 
         registryDirectory.notify(new ArrayList<URL>());
         routers = registryDirectory.getRouters();
-        Assert.assertEquals(1 + 1, routers.size());
+        Assert.assertEquals(3, routers.size());
         Assert.assertTrue(ScriptRouter.class == routers.get(1).getClass() || 
ScriptRouter.class == routers.get(0).getClass());
 
         serviceUrls.clear();
         serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, 
Constants.ROUTER_TYPE_CLEAR));
         registryDirectory.notify(serviceUrls);
         routers = registryDirectory.getRouters();
-        Assert.assertEquals(0 + 1, routers.size());
+        Assert.assertEquals(2, routers.size());
     }
 
     /**
@@ -923,13 +923,13 @@ public class RegistryDirectoryTest {
         serviceUrls.add(routerurl);
         registryDirectory.notify(serviceUrls);
         List routers = registryDirectory.getRouters();
-        Assert.assertEquals(1 + 1, routers.size());
+        Assert.assertEquals(3, routers.size());
 
         serviceUrls.clear();
         serviceUrls.add(routerurl.addParameter(Constants.ROUTER_KEY, 
Constants.ROUTER_TYPE_CLEAR));
         registryDirectory.notify(serviceUrls);
         routers = registryDirectory.getRouters();
-        Assert.assertEquals(0 + 1, routers.size());
+        Assert.assertEquals(2, routers.size());
     }
 
     /**
diff --git 
a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
 
b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
index dcedfcf..dbbf197 100644
--- 
a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
+++ 
b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvokerAvilableTest.java
@@ -20,13 +20,12 @@ package com.alibaba.dubbo.rpc.protocol.dubbo;
 import com.alibaba.dubbo.common.Constants;
 import com.alibaba.dubbo.common.URL;
 import com.alibaba.dubbo.common.extension.ExtensionLoader;
-import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
 import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
-import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
 import com.alibaba.dubbo.rpc.Exporter;
 import com.alibaba.dubbo.rpc.ProxyFactory;
 import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
-
 import junit.framework.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -40,6 +39,9 @@ import static org.junit.Assert.fail;
  * Check available status for dubboInvoker
  */
 public class DubboInvokerAvilableTest {
+
+    private final static Logger logger = 
LoggerFactory.getLogger(DubboInvokerAvilableTest.class);
+
     private static DubboProtocol protocol = DubboProtocol.getDubboProtocol();
     private static ProxyFactory proxy = 
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
 
@@ -89,14 +91,15 @@ public class DubboInvokerAvilableTest {
 
         long start = System.currentTimeMillis();
 
-        try{
+        try {
             System.setProperty(Constants.SHUTDOWN_WAIT_KEY, "2000");
             protocol.destroy();
-        }finally {
+        } finally {
             System.getProperties().remove(Constants.SHUTDOWN_WAIT_KEY);
         }
 
         long waitTime = System.currentTimeMillis() - start;
+        logger.info("test_normal_channel_close_wait_gracefully wait for " + 
waitTime + " ms");
 
         Assert.assertTrue(waitTime >= 2000);
         Assert.assertEquals(false, invoker.isAvailable());

Reply via email to