>> addrPool) {
+ this.addrPool = addrPool;
+ }
+
+ public Object getAddrMetadata() {
+ return addrMetadata;
+ }
+
+ public void setAddrMetadata(Object addrMetadata) {
+ this.addrMetadata = addrMetadata;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouter.java
new file mode 100644
index 0000000000..ee28459837
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouter.java
@@ -0,0 +1,100 @@
+/*
+ * 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.state;
+
+import java.util.List;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Directory;
+
+/**
+ * State Router. (SPI, Prototype, ThreadSafe)
+ *
+ * Routing
+ *
+ * @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
+ * @see Directory#list(Invocation)
+ * @since 3.0
+ */
+public interface StateRouter extends Comparable {
+
+ int DEFAULT_PRIORITY = Integer.MAX_VALUE;
+
+ /**
+ * Get the router url.
+ *
+ * @return url
+ */
+ URL getUrl();
+
+ /***
+ * Filter invokers with current routing rule and only return the invokers that comply with the rule.
+ * Caching address lists in BitMap mode improves routing performance.
+ * @param invokers invoker bit list
+ * @param cache router address cache
+ * @param url refer url
+ * @param invocation invocation
+ * @param
+ * @return routed invokers
+ * @throws RpcException
+ * @Since 3.0
+ */
+ BitList> route(BitList> invokers, RouterCache cache, URL url, Invocation invocation)
+ throws
+ RpcException;
+
+ default void notify(List> invokers) {
+
+ }
+
+ /**
+ * To decide whether this router need to execute every time an RPC comes or should only execute when addresses or
+ * rule change.
+ *
+ * @return true if the router need to execute every time.
+ */
+ boolean isRuntime();
+
+ boolean isEnable();
+
+ boolean isForce();
+
+ int getPriority();
+
+ @Override
+ default int compareTo(StateRouter o) {
+ if (o == null) {
+ throw new IllegalArgumentException();
+ }
+ return Integer.compare(this.getPriority(), o.getPriority());
+ }
+
+ String getName();
+
+ boolean shouldRePool();
+
+ RouterCache pool(List> invokers);
+
+ void pool();
+
+ default void stop() {
+ //do nothing by default
+ }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouterFactory.java
new file mode 100644
index 0000000000..9780c367c4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/state/StateRouterFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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.state;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+
+@SPI
+public interface StateRouterFactory {
+ /**
+ * Create state router.
+ *
+ * @param url url
+ * @return router instance
+ * @since 3.0
+ */
+ @Adaptive("protocol")
+ StateRouter getRouter(URL url, RouterChain chain);
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouter.java
new file mode 100644
index 0000000000..231aa2c85b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouter.java
@@ -0,0 +1,267 @@
+/*
+ * 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 java.net.UnknownHostException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+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.NetUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+import org.apache.dubbo.rpc.cluster.router.state.RouterCache;
+import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
+import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;
+
+import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+import static org.apache.dubbo.rpc.Constants.FORCE_USE_TAG;
+
+/**
+ * TagDynamicStateRouter, "application.tag-router"
+ */
+public class TagDynamicStateRouter extends AbstractStateRouter implements ConfigurationListener {
+ public static final String NAME = "TAG_ROUTER";
+ private static final int TAG_ROUTER_DEFAULT_PRIORITY = 100;
+ private static final Logger logger = LoggerFactory.getLogger(TagDynamicStateRouter.class);
+ private static final String RULE_SUFFIX = ".tag-router";
+ private static final String NO_TAG = "noTag";
+
+ private TagRouterRule tagRouterRule;
+ private String application;
+
+ public TagDynamicStateRouter(URL url, RouterChain chain) {
+ super(url, chain);
+ this.priority = TAG_ROUTER_DEFAULT_PRIORITY;
+ }
+
+ @Override
+ public synchronized void process(ConfigChangedEvent event) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Notification of tag rule, change type is: " + event.getChangeType() + ", raw rule is:\n " +
+ event.getContent());
+ }
+
+ try {
+ if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
+ this.tagRouterRule = null;
+ } else {
+ this.tagRouterRule = TagRuleParser.parse(event.getContent());
+ }
+ } catch (Exception e) {
+ logger.error("Failed to parse the raw tag router rule and it will not take effect, please check if the " +
+ "rule matches with the template, the raw rule is:\n ", e);
+ }
+ }
+
+ @Override
+ public URL getUrl() {
+ return url;
+ }
+
+ @Override
+ public BitList> route(BitList> invokers, RouterCache cache, URL url,
+ Invocation invocation) throws RpcException {
+
+
+ final TagRouterRule tagRouterRuleCopy = (TagRouterRule)cache.getAddrMetadata();
+
+ String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :
+ invocation.getAttachment(TAG_KEY);
+
+ ConcurrentMap>> addrPool = cache.getAddrPool();
+
+ if (StringUtils.isEmpty(tag)) {
+ return invokers.intersect(addrPool.get(NO_TAG), invokers.getUnmodifiableList());
+ } else {
+ BitList> result = addrPool.get(tag);
+
+ if (CollectionUtils.isNotEmpty(result) || (tagRouterRuleCopy != null && tagRouterRuleCopy.isForce())
+ || isForceUseTag(invocation)) {
+ return invokers.intersect(result, invokers.getUnmodifiableList());
+ } else {
+ invocation.setAttachment(TAG_KEY, NO_TAG);
+ return invokers;
+ }
+ }
+ }
+
+ private boolean isForceUseTag(Invocation invocation) {
+ return Boolean.valueOf(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false")));
+ }
+
+ @Override
+ public boolean isRuntime() {
+ return tagRouterRule != null && tagRouterRule.isRuntime();
+ }
+
+ @Override
+ public boolean isEnable() {
+ return true;
+ }
+
+ @Override
+ public boolean isForce() {
+ return tagRouterRule != null && tagRouterRule.isForce();
+ }
+
+ @Override
+ public String getName() {
+ return "TagDynamic";
+ }
+
+ @Override
+ public boolean shouldRePool() {
+ return false;
+ }
+
+ @Override
+ public RouterCache pool(List> invokers) {
+
+ RouterCache routerCache = new RouterCache<>();
+ ConcurrentHashMap>> addrPool = new ConcurrentHashMap<>();
+
+ final TagRouterRule tagRouterRuleCopy = tagRouterRule;
+
+
+ if (tagRouterRuleCopy == null || !tagRouterRuleCopy.isValid() || !tagRouterRuleCopy.isEnabled()) {
+ BitList> noTagList = new BitList<>(invokers, true);
+
+ for (int index = 0; index < invokers.size(); index++) {
+ noTagList.addIndex(index);
+ }
+ addrPool.put(NO_TAG, noTagList);
+ routerCache.setAddrPool(addrPool);
+ return routerCache;
+ }
+
+ List tagNames = tagRouterRuleCopy.getTagNames();
+ Map> tagnameToAddresses = tagRouterRuleCopy.getTagnameToAddresses();
+
+ for (String tag : tagNames) {
+ List addresses = tagnameToAddresses.get(tag);
+ BitList> list = new BitList<>(invokers, true);
+
+ if (CollectionUtils.isEmpty(addresses)) {
+ list.addAll(invokers);
+ } else {
+ for (int index = 0; index < invokers.size(); index++) {
+ Invoker invoker = invokers.get(index);
+ if (addressMatches(invoker.getUrl(), addresses)) {
+ list.addIndex(index);
+ }
+ }
+ }
+
+ addrPool.put(tag, list);
+ }
+
+ List addresses = tagRouterRuleCopy.getAddresses();
+ BitList> noTagList = new BitList<>(invokers, true);
+
+ for (int index = 0; index < invokers.size(); index++) {
+ Invoker invoker = invokers.get(index);
+ if (addressNotMatches(invoker.getUrl(), addresses)) {
+ noTagList.addIndex(index);
+ }
+ }
+ addrPool.put(NO_TAG, noTagList);
+ routerCache.setAddrPool(addrPool);
+ routerCache.setAddrMetadata(tagRouterRuleCopy);
+
+ return routerCache;
+ }
+
+ private boolean addressMatches(URL url, List addresses) {
+ return addresses != null && checkAddressMatch(addresses, url.getHost(), url.getPort());
+ }
+
+ private boolean addressNotMatches(URL url, List addresses) {
+ return addresses == null || !checkAddressMatch(addresses, url.getHost(), url.getPort());
+ }
+
+ private boolean checkAddressMatch(List addresses, String host, int port) {
+ for (String address : addresses) {
+ try {
+ if (NetUtils.matchIpExpression(address, host, port)) {
+ return true;
+ }
+ if ((ANYHOST_VALUE + ":" + port).equals(address)) {
+ return true;
+ }
+ } catch (UnknownHostException e) {
+ logger.error("The format of ip address is invalid in tag route. Address :" + address, e);
+ } catch (Exception e) {
+ logger.error("The format of ip address is invalid in tag route. Address :" + address, e);
+ }
+ }
+ return false;
+ }
+
+ public void setApplication(String app) {
+ this.application = app;
+ }
+
+ @Override
+ public void notify(List> invokers) {
+ if (CollectionUtils.isEmpty(invokers)) {
+ return;
+ }
+
+ Invoker invoker = invokers.get(0);
+ URL url = invoker.getUrl();
+ String providerApplication = url.getRemoteApplication();
+
+ if (StringUtils.isEmpty(providerApplication)) {
+ logger.error("TagRouter must getConfig from or subscribe to a specific application, but the application " +
+ "in this TagRouter is not specified.");
+ return;
+ }
+
+ synchronized (this) {
+ if (!providerApplication.equals(application)) {
+ if (!StringUtils.isEmpty(application)) {
+ ruleRepository.removeListener(application + RULE_SUFFIX, this);
+ }
+ String key = providerApplication + RULE_SUFFIX;
+ ruleRepository.addListener(key, this);
+ application = providerApplication;
+ String rawRule = ruleRepository.getRule(key, DynamicConfiguration.DEFAULT_GROUP);
+ if (StringUtils.isNotEmpty(rawRule)) {
+ this.process(new ConfigChangedEvent(key, DynamicConfiguration.DEFAULT_GROUP, rawRule));
+ }
+ }
+ }
+ pool(invokers);
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouterFactory.java
new file mode 100644
index 0000000000..cdcdb8408c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagDynamicStateRouterFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory;
+import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
+
+/**
+ * Tag router factory
+ */
+@Activate(order = 99)
+public class TagDynamicStateRouterFactory extends CacheableStateRouterFactory {
+
+ public static final String NAME = "tag-dynamic";
+
+ @Override
+ protected StateRouter createRouter(URL url, RouterChain chain) {
+ return new TagDynamicStateRouter(url, chain);
+ }
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouter.java
new file mode 100644
index 0000000000..d15745f7e5
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouter.java
@@ -0,0 +1,155 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+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.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+import org.apache.dubbo.rpc.cluster.router.state.RouterCache;
+import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
+import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+
+/**
+ * TagStaticStateRouter, "application.tag-router"
+ */
+public class TagStaticStateRouter extends AbstractStateRouter {
+ public static final String NAME = "TAG_ROUTER";
+ private static final int TAG_ROUTER_DEFAULT_PRIORITY = 100;
+ private static final Logger logger = LoggerFactory.getLogger(TagStaticStateRouter.class);
+ private static final String NO_TAG = "noTag";
+
+ private TagRouterRule tagRouterRule;
+
+ public TagStaticStateRouter(URL url, RouterChain chain) {
+ super(url, chain);
+ this.priority = TAG_ROUTER_DEFAULT_PRIORITY;
+ }
+
+ @Override
+ public URL getUrl() {
+ return url;
+ }
+
+ @Override
+ public BitList> route(BitList> invokers, RouterCache routerCache, URL url, Invocation invocation)
+ throws RpcException {
+
+ String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :
+ invocation.getAttachment(TAG_KEY);
+ if (StringUtils.isEmpty(tag)) {
+ tag = NO_TAG;
+ }
+
+ ConcurrentMap>> pool = routerCache.getAddrPool();
+ BitList> res = pool.get(tag);
+ if (res == null) {
+ return invokers;
+ }
+ return invokers.intersect(res, invokers.getUnmodifiableList());
+ }
+
+ @Override
+ protected List getTags(URL url, Invocation invocation) {
+ List tags = new ArrayList<>();
+ String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :
+ invocation.getAttachment(TAG_KEY);
+ if (StringUtils.isEmpty(tag)) {
+ tag = NO_TAG;
+ }
+ tags.add(tag);
+ return tags;
+ }
+
+ @Override
+ public boolean isRuntime() {
+ return tagRouterRule != null && tagRouterRule.isRuntime();
+ }
+
+ @Override
+ public boolean isEnable() {
+ return true;
+ }
+
+ @Override
+ public boolean isForce() {
+ // FIXME
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "TagStatic";
+ }
+
+ @Override
+ public boolean shouldRePool() {
+ return false;
+ }
+
+ @Override
+ public RouterCache pool(List> invokers) {
+
+ RouterCache routerCache = new RouterCache<>();
+ ConcurrentHashMap>> addrPool = new ConcurrentHashMap<>();
+
+ for (int index = 0; index < invokers.size(); index++) {
+ Invoker invoker = invokers.get(index);
+ String tag = invoker.getUrl().getParameter(TAG_KEY);
+ if (StringUtils.isEmpty(tag)) {
+ BitList> noTagList = addrPool.putIfAbsent(NO_TAG, new BitList<>(invokers, true));
+ if (noTagList == null) {
+ noTagList = addrPool.get(NO_TAG);
+ }
+ noTagList.addIndex(index);
+ } else {
+ BitList> list = addrPool.putIfAbsent(tag, new BitList<>(invokers, true));
+ if (list == null) {
+ list = addrPool.get(tag);
+ }
+ list.addIndex(index);
+ }
+ }
+
+ routerCache.setAddrPool(addrPool);
+
+ return routerCache;
+ }
+
+
+ @Override
+ public void notify(List> invokers) {
+ if (CollectionUtils.isEmpty(invokers)) {
+ return;
+ }
+
+ pool(invokers);
+ }
+
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouterFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouterFactory.java
new file mode 100644
index 0000000000..21fe348682
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStaticStateRouterFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.router.state.CacheableStateRouterFactory;
+import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
+
+/**
+ * Tag router factory
+ */
+@Activate(order = 100)
+public class TagStaticStateRouterFactory extends CacheableStateRouterFactory {
+
+ public static final String NAME = "tag-static";
+
+ @Override
+ protected StateRouter createRouter(URL url, RouterChain chain) {
+ return new TagStaticStateRouter(url, chain);
+ }
+}
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory
new file mode 100644
index 0000000000..c69b3abdfb
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory
@@ -0,0 +1,2 @@
+tag-dynamic=org.apache.dubbo.rpc.cluster.router.tag.TagDynamicStateRouterFactory
+tag-static=org.apache.dubbo.rpc.cluster.router.tag.TagStaticStateRouterFactory
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
index b8eb41ee05..02bd6c977b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
@@ -16,20 +16,23 @@
*/
package org.apache.dubbo.common.threadpool.manager;
-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.threadpool.ThreadPool;
-import org.apache.dubbo.common.utils.NamedThreadFactory;
-
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+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.threadlocal.NamedInternalThreadFactory;
+import org.apache.dubbo.common.threadpool.ThreadPool;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY;
@@ -59,13 +62,25 @@ public class DefaultExecutorRepository implements ExecutorRepository {
private ConcurrentMap> data = new ConcurrentHashMap<>();
+ private ExecutorService poolRouterExecutor;
+
+ private static Ring executorServiceRing = new Ring();
+
public DefaultExecutorRepository() {
for (int i = 0; i < DEFAULT_SCHEDULER_SIZE; i++) {
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-scheduler"));
+ ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
+ new NamedThreadFactory("Dubbo-framework-scheduler"));
scheduledExecutors.addItem(scheduler);
+
+ executorServiceRing.addItem(new ThreadPoolExecutor(1, 1,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue(1024), new NamedInternalThreadFactory("Dubbo-state-router-loop", true)
+ , new ThreadPoolExecutor.AbortPolicy()));
}
//
// reconnectScheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-reconnect-scheduler"));
+ poolRouterExecutor = new ThreadPoolExecutor(1, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1024),
+ new NamedInternalThreadFactory("Dubbo-state-router-pool-router", true), new ThreadPoolExecutor.AbortPolicy());
serviceExporterExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-exporter-scheduler"));
serviceDiscveryAddressNotificationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-SD-address-refresh"));
registryNotificationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-registry-notification"));
@@ -157,6 +172,11 @@ public class DefaultExecutorRepository implements ExecutorRepository {
return scheduledExecutors.pollItem();
}
+ @Override
+ public ExecutorService nextExecutorExecutor() {
+ return executorServiceRing.pollItem();
+ }
+
@Override
public ScheduledExecutorService getServiceExporterExecutor() {
return serviceExporterExecutor;
@@ -185,4 +205,8 @@ public class DefaultExecutorRepository implements ExecutorRepository {
return (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
}
+ @Override
+ public ExecutorService getPoolRouterExecutor() {
+ return poolRouterExecutor;
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
index d00a7bb4e2..0a8b145ca8 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
@@ -55,6 +55,8 @@ public interface ExecutorRepository {
*/
ScheduledExecutorService nextScheduledExecutor();
+ ExecutorService nextExecutorExecutor();
+
ScheduledExecutorService getServiceExporterExecutor();
ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor();
@@ -68,7 +70,6 @@ public interface ExecutorRepository {
*/
ScheduledExecutorService getRegistryNotificationExecutor();
-
/**
* Get the default shared threadpool.
*
@@ -76,4 +77,6 @@ public interface ExecutorRepository {
*/
ExecutorService getSharedExecutor();
+ ExecutorService getPoolRouterExecutor();
+
}
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 675b7c74ec..5d59d8de3e 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -160,6 +160,7 @@
3.2.8
1.5.19
+ 0.9.0
2.0.1
5.2.0
2.8.5
@@ -338,6 +339,11 @@
protobuf-java-util
${protobuf-java_version}