From dfa5d58f53ea39469851eac3b5d75da84778a1c9 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Sun, 12 Dec 2021 15:55:30 +0800 Subject: [PATCH] [3.0] Add router fail fast option --- .../apache/dubbo/rpc/cluster/Constants.java | 2 ++ .../apache/dubbo/rpc/cluster/RouterChain.java | 23 ++++++++++++++----- .../integration/DynamicDirectory.java | 18 ++++++++++++--- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java index ad9bd0d9427..c4936102f9d 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java @@ -135,4 +135,6 @@ public interface Constants { * The key of shortestResponseSlidePeriod */ String SHORTEST_RESPONSE_SLIDE_PERIOD = "shortestResponseSlidePeriod"; + + String SHOULD_FAIL_FAST_KEY = "dubbo.router.should-fail-fast"; } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java index 12e32c7b568..b4ca2c55ac3 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.CollectionUtils; @@ -30,6 +31,7 @@ import org.apache.dubbo.rpc.cluster.router.state.StateRouter; import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory; import org.apache.dubbo.rpc.cluster.router.state.StateRouterResult; +import org.apache.dubbo.rpc.model.ModuleModel; import java.util.ArrayList; import java.util.Collections; @@ -63,12 +65,19 @@ public class RouterChain { private volatile List> builtinStateRouters = Collections.emptyList(); private volatile List> stateRouters = Collections.emptyList(); + /** + * Should continue route if current router's result is empty + */ + private final boolean shouldFailFast; + public static RouterChain buildChain(Class interfaceClass, URL url) { return new RouterChain<>(interfaceClass, url); } private RouterChain(Class interfaceClass, URL url) { - List extensionFactories = url.getOrDefaultApplicationModel().getExtensionLoader(RouterFactory.class) + ModuleModel moduleModel = url.getOrDefaultModuleModel(); + + List extensionFactories = moduleModel.getExtensionLoader(RouterFactory.class) .getActivateExtension(url, ROUTER_KEY); List routers = extensionFactories.stream() @@ -78,7 +87,7 @@ private RouterChain(Class interfaceClass, URL url) { initWithRouters(routers); - List extensionStateRouterFactories = url.getOrDefaultApplicationModel() + List extensionStateRouterFactories = moduleModel .getExtensionLoader(StateRouterFactory.class) .getActivateExtension(url, ROUTER_KEY); @@ -89,6 +98,8 @@ private RouterChain(Class interfaceClass, URL url) { // init state routers initWithStateRouters(stateRouters); + + this.shouldFailFast = Boolean.parseBoolean(ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true")); } /** @@ -158,7 +169,7 @@ public List> route(URL url, BitList> availableInvokers, In for (StateRouter stateRouter : stateRouters) { StateRouterResult> routeResult = stateRouter.route(resultInvokers, url, invocation, false); resultInvokers = routeResult.getResult(); - if (resultInvokers.isEmpty()) { + if (resultInvokers.isEmpty() && shouldFailFast) { printRouterSnapshot(url, availableInvokers, invocation); return BitList.emptyList(); } @@ -179,7 +190,7 @@ public List> route(URL url, BitList> availableInvokers, In // Copy resultInvokers to a arrayList. BitList not support RouterResult> routeResult = router.route(commonRouterResult, url, invocation, false); commonRouterResult = routeResult.getResult(); - if (CollectionUtils.isEmpty(commonRouterResult)) { + if (CollectionUtils.isEmpty(commonRouterResult) && shouldFailFast) { printRouterSnapshot(url, availableInvokers, invocation); return BitList.emptyList(); } @@ -222,7 +233,7 @@ public RouterSnapshotNode buildRouterSnapshot(URL url, BitList> av currentNode.setRouterMessage(routerMessage); // result is empty, log out - if (resultInvokers.isEmpty()) { + if (resultInvokers.isEmpty() && shouldFailFast) { return snapshotNode; } @@ -248,7 +259,7 @@ public RouterSnapshotNode buildRouterSnapshot(URL url, BitList> av currentNode.setRouterMessage(routerMessage); // result is empty, log out - if (CollectionUtils.isEmpty(routeResult)) { + if (CollectionUtils.isEmpty(routeResult) && shouldFailFast) { return snapshotNode; } else { commonRouterResult = routeResult; diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java index 9a907d6f0a0..65b71a79165 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; +import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; @@ -34,10 +35,12 @@ import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.cluster.Cluster; import org.apache.dubbo.rpc.cluster.Configurator; +import org.apache.dubbo.rpc.cluster.Constants; import org.apache.dubbo.rpc.cluster.RouterChain; import org.apache.dubbo.rpc.cluster.RouterFactory; import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory; import org.apache.dubbo.rpc.cluster.router.state.BitList; +import org.apache.dubbo.rpc.model.ModuleModel; import java.util.List; @@ -107,11 +110,18 @@ public abstract class DynamicDirectory extends AbstractDirectory implement protected ServiceInstancesChangedListener serviceListener; + /** + * Should continue route if directory is empty + */ + private final boolean shouldFailFast; + public DynamicDirectory(Class serviceType, URL url) { super(url, true); - this.cluster = url.getOrDefaultApplicationModel().getExtensionLoader(Cluster.class).getAdaptiveExtension(); - this.routerFactory = url.getOrDefaultApplicationModel().getExtensionLoader(RouterFactory.class).getAdaptiveExtension(); + ModuleModel moduleModel = url.getOrDefaultModuleModel(); + + this.cluster = moduleModel.getExtensionLoader(Cluster.class).getAdaptiveExtension(); + this.routerFactory = moduleModel.getExtensionLoader(RouterFactory.class).getAdaptiveExtension(); if (serviceType == null) { throw new IllegalArgumentException("service type is null."); @@ -130,6 +140,8 @@ public DynamicDirectory(Class serviceType, URL url) { this.directoryUrl = consumerUrl; String group = directoryUrl.getGroup(""); this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(",")); + + this.shouldFailFast = Boolean.parseBoolean(ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true")); } @Override @@ -165,7 +177,7 @@ public void unSubscribe(URL url) { @Override public List> doList(BitList> invokers, Invocation invocation) { - if (forbidden) { + if (forbidden && shouldFailFast) { // 1. No service provider 2. Service providers are disabled throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +