From 3375ec827845a0b133eae1fd4aad78aeb203361a Mon Sep 17 00:00:00 2001 From: xujingfeng <250577914@qq.com> Date: Thu, 17 Jan 2019 16:47:53 +0800 Subject: [PATCH] feature:import TagRouter (#3065) --- .../com/alibaba/dubbo/rpc/cluster/Router.java | 9 +- .../cluster/directory/AbstractDirectory.java | 2 + .../rpc/cluster/router/AbstractRouter.java | 40 ++ .../cluster/router/MockInvokersSelector.java | 20 +- .../router/condition/ConditionRouter.java | 9 +- .../cluster/router/script/ScriptRouter.java | 27 +- .../rpc/cluster/router/tag/TagRouter.java | 79 +++ .../cluster/loadbalance/LoadBalanceTest.java | 14 +- .../rpc/cluster/router/tag/TagRouterTest.java | 153 ++++++ .../com/alibaba/dubbo/common/Constants.java | 4 + .../dubbo/config/AbstractServiceConfig.java | 498 +++++++++--------- .../dubbo/config/annotation/Service.java | 2 + .../dubbo/config/spring/AnnotationBean.java | 3 + .../main/resources/META-INF/compat/dubbo.xsd | 5 + .../src/main/resources/META-INF/dubbo.xsd | 5 + .../integration/RegistryDirectory.java | 3 +- .../registry/dubbo/RegistryDirectoryTest.java | 10 +- .../dubbo/DubboInvokerAvilableTest.java | 13 +- 18 files changed, 594 insertions(+), 302 deletions(-) create mode 100644 dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/AbstractRouter.java create mode 100644 dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouter.java create mode 100644 dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/tag/TagRouterTest.java 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 a5eea1c9bf6..f4471839716 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 @@ * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory) * @see com.alibaba.dubbo.rpc.cluster.Directory#list(Invocation) */ -public interface Router extends Comparable { +public interface Router extends Comparable{ /** * get the router url. @@ -51,4 +51,11 @@ public interface Router extends Comparable { */ List> route(List> 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 6934069ee94..31764af25fe 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.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 @@ protected void setRouters(List routers) { } // 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 00000000000..6b5cbcfbb22 --- /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 d406f78da4f..089c8dadf88 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.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 @@ /** * 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 List> route(final List> invokers, @@ -87,14 +91,4 @@ private boolean hasMockProviders(final List> invokers) { 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 05f1d7136dc..961b66a05f5 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.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 @@ /** * ConditionRouter - * */ -public class ConditionRouter implements Router, Comparable { +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 whenCondition; private final Map 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 74938c6439e..89241ad722e 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.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 @@ /** * 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 engines = new ConcurrentHashMap(); 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; @@ -79,11 +76,6 @@ public ScriptRouter(URL url) { this.rule = rule; } - @Override - public URL getUrl() { - return url; - } - @Override @SuppressWarnings("unchecked") public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { @@ -114,13 +106,4 @@ public List> route(List> invokers, URL url, Invocation } } - @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 00000000000..b1b0d4f7a9d --- /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 List> route(List> invokers, URL url, Invocation invocation) throws RpcException { + // filter + List> result = new ArrayList>(); + // Dynamic param + String tag = RpcContext.getContext().getAttachment(Constants.TAG_KEY); + // Tag request + if (!StringUtils.isEmpty(tag)) { + // Select tag invokers first + for (Invoker 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 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 d231c63c3b2..088f781b94f 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 @@ private AbstractLoadBalance getLoadBalance(String loadbalanceName) { @Test public void testRoundRobinLoadBalance_select() { - int runs = 10000; + int runs = 1000; Map counter = getInvokeCounter(runs, RoundRobinLoadBalance.NAME); for (Invoker minvoker : counter.keySet()) { Long count = counter.get(minvoker).get(); @@ -146,7 +146,7 @@ private void assertStrictWRRResult(int runs, Map resultMa public void testRoundRobinLoadBalanceWithWeight() { final Map totalMap = new HashMap(); final AtomicBoolean shouldBegin = new AtomicBoolean(false); - final int runs = 10000; + final int runs = 1000; List threads = new ArrayList(); int threadNum = 10; for (int i = 0; i < threadNum; i ++) { @@ -188,7 +188,7 @@ public void run() { @Test public void testRoundRobinLoadBalanceWithWeightShouldNotRecycle() { - int runs = 10000; + int runs = 1000; //tmperately add a new invoker weightInvokers.add(weightInvokerTmp); try { @@ -226,7 +226,7 @@ public void testRoundRobinLoadBalanceWithWeightShouldRecycle() { 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 void testRoundRobinLoadBalanceWithWeightShouldRecycle() { 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 void testSelectByWeightRandom() { 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 void testRandomLoadBalance_select() { @Test public void testLeastActiveLoadBalance_select() { - int runs = 10000; + int runs = 1000; Map 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 00000000000..7b400b7174b --- /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> invokers = new ArrayList>(); + Invoker redInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red")); + Invoker yellowInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow")); + Invoker blueInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue")); + Invoker defaultInvoker = new MockInvoker(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> 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> invokers = new ArrayList>(); + Invoker redInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red")); + Invoker yellowInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow")); + Invoker blueInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue")); + Invoker defaultInvoker = new MockInvoker(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> 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> invokers = new ArrayList>(); + Invoker redInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red")); + Invoker yellowInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow")); + Invoker blueInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.3:20880/com.foo.BarService?dubbo.tag=blue")); + Invoker defaultInvoker = new MockInvoker(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> 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> invokers = new ArrayList>(); + Invoker redInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.1:20880/com.foo.BarService?dubbo.tag=red")); + Invoker yellowInvoker = new MockInvoker(URL.valueOf( + "dubbo://10.20.3.2:20880/com.foo.BarService?dubbo.tag=yellow")); + Invoker blueInvoker = new MockInvoker(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> 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 02d70ea809b..cedb20b688f 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 68903988da4..7d242ff4c29 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 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 getProtocols() { - return protocols; - } - - @SuppressWarnings({"unchecked"}) - public void setProtocols(List protocols) { - this.protocols = (List) 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 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 getProtocols() { + return protocols; + } + + @SuppressWarnings({"unchecked"}) + public void setProtocols(List protocols) { + this.protocols = (List) 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 4d682273171..0f094f98076 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 @@ 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 722c6d05213..84a01f8ea64 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 Object postProcessAfterInitialization(Object bean, String beanName) } 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 c48e2c3deab..ffa74ca3c5d 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 @@ + + + + + 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 40a7ac6c723..19361d69199 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 @@ + + + + + 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 4bd0908a1ed..68ecc0e9efb 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 @@ private List> route(List> invokers, String method) { List 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 3463407f928..fca493b0566 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 void testNotifyRouterUrls() { registryDirectory.notify(serviceUrls); List 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()); 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 void testNotifyRouterUrls_Clean() { 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 dcedfcf6526..dbbf197ccf3 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 @@ 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 @@ * 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 void test_normal_channel_close_wait_gracefully() throws Exception { 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());