* Cluster
* Fault-Tolerant
*
*/
@SPI(Cluster.DEFAULT)
public interface Cluster {
String DEFAULT = "failover";
/**
* Merge the directory invokers to a virtual invoker.
*
* @param
* @param directory
* @return cluster invoker
* @throws RpcException
*/
@Adaptive
Invoker join(Directory directory, boolean buildFilterChain) throws RpcException;
static Cluster getCluster(ScopeModel scopeModel, String name) {
return getCluster(scopeModel, name, true);
}
static Cluster getCluster(ScopeModel scopeModel, String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
name = Cluster.DEFAULT;
}
return ScopeModelUtil.getApplicationModel(scopeModel)
.getExtensionLoader(Cluster.class)
.getExtension(name, wrap);
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invoker;
/**
* This is the final Invoker type referenced by the RPC proxy on Consumer side.
*
* A ClusterInvoker holds a group of normal invokers, stored in a Directory, mapping to one Registry.
* The ClusterInvoker implementation usually provides LB or HA policies, like FailoverClusterInvoker.
*
* In multi-registry subscription scenario, the final ClusterInvoker will refer to several sub ClusterInvokers, with each
* sub ClusterInvoker representing one Registry. Take ZoneAwareClusterInvoker as an example, it is specially customized for
* multi-registry use cases: first, pick up one ClusterInvoker, then do LB inside the chose ClusterInvoker.
*
* @param
*/
public interface ClusterInvoker extends Invoker {
URL getRegistryUrl();
Directory getDirectory();
boolean isDestroyed();
default boolean isServiceDiscovery() {
Directory directory = getDirectory();
if (directory == null) {
return false;
}
return directory.isServiceDiscovery();
}
default boolean hasProxyInvokers() {
Directory directory = getDirectory();
if (directory == null) {
return false;
}
return !directory.isEmpty();
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterScopeModelInitializer.java
================================================
/*
* 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;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ScopeModelInitializer;
public class ClusterScopeModelInitializer implements ScopeModelInitializer {
@Override
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
beanFactory.registerBean(RouterSnapshotSwitcher.class);
}
@Override
public void initializeApplicationModel(ApplicationModel applicationModel) {
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
beanFactory.registerBean(ClusterUtils.class);
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Configurator.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY;
/**
* Configurator. (SPI, Prototype, ThreadSafe)
*
*/
public interface Configurator extends Comparable {
/**
* Get the configurator url.
*
* @return configurator url.
*/
URL getUrl();
/**
* Configure the provider url.
*
* @param url - old provider url.
* @return new provider url.
*/
URL configure(URL url);
/**
* Convert override urls to map for use when re-refer. Send all rules every time, the urls will be reassembled and
* calculated
*
* URL contract:
*
*
override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... means global rules
* (all of the providers take effect)
*
override://ip:port...?anyhost=false Special rules (only for a certain provider)
*
override:// rule is not supported... ,needs to be calculated by registry itself
*
override://0.0.0.0/ without parameters means clearing the override
*
*
* @param urls URL list to convert
* @return converted configurator list
*/
static Optional> toConfigurators(List urls) {
if (CollectionUtils.isEmpty(urls)) {
return Optional.empty();
}
ConfiguratorFactory configuratorFactory = urls.get(0)
.getOrDefaultApplicationModel()
.getExtensionLoader(ConfiguratorFactory.class)
.getAdaptiveExtension();
List configurators = new ArrayList<>(urls.size());
for (URL url : urls) {
if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
configurators.clear();
break;
}
Map override = new HashMap<>(url.getParameters());
// The anyhost parameter of override may be added automatically, it can't change the judgement of changing
// url
override.remove(ANYHOST_KEY);
if (CollectionUtils.isEmptyMap(override)) {
continue;
}
configurators.add(configuratorFactory.getConfigurator(url));
}
Collections.sort(configurators);
return Optional.of(configurators);
}
/**
* Sort by host, then by priority
* 1. the url with a specific host ip should have higher priority than 0.0.0.0
* 2. if two url has the same host, compare by priority value;
*/
@Override
default int compareTo(Configurator o) {
if (o == null) {
return -1;
}
int ipCompare = getUrl().getHost().compareTo(o.getUrl().getHost());
// host is the same, sort by priority
if (ipCompare == 0) {
int i = getUrl().getParameter(PRIORITY_KEY, 0);
int j = o.getUrl().getParameter(PRIORITY_KEY, 0);
return Integer.compare(i, j);
} else {
return ipCompare;
}
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ConfiguratorFactory.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
/**
* ConfiguratorFactory. (SPI, Singleton, ThreadSafe)
*
*/
@SPI
public interface ConfiguratorFactory {
/**
* get the configurator instance.
*
* @param url - configurator url.
* @return configurator instance.
*/
@Adaptive(CommonConstants.PROTOCOL_KEY)
Configurator getConfigurator(URL url);
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java
================================================
/*
* 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;
public interface Constants {
String FAIL_BACK_TASKS_KEY = "failbacktasks";
int DEFAULT_FAILBACK_TASKS = 100;
int DEFAULT_FORKS = 2;
String WEIGHT_KEY = "weight";
int DEFAULT_WEIGHT = 100;
String MOCK_PROTOCOL = "mock";
String FORCE_KEY = "force";
String RAW_RULE_KEY = "rawRule";
String VALID_KEY = "valid";
String ENABLED_KEY = "enabled";
String DYNAMIC_KEY = "dynamic";
String SCOPE_KEY = "scope";
String KEY_KEY = "key";
String CONDITIONS_KEY = "conditions";
String AFFINITY_KEY = "affinityAware";
String TAGS_KEY = "tags";
/**
* To decide whether to exclude unavailable invoker from the cluster
*/
String CLUSTER_AVAILABLE_CHECK_KEY = "cluster.availablecheck";
/**
* The default value of cluster.availablecheck
*
* @see #CLUSTER_AVAILABLE_CHECK_KEY
*/
boolean DEFAULT_CLUSTER_AVAILABLE_CHECK = true;
/**
* To decide whether to enable sticky strategy for cluster
*/
String CLUSTER_STICKY_KEY = "sticky";
/**
* The default value of sticky
*
* @see #CLUSTER_STICKY_KEY
*/
boolean DEFAULT_CLUSTER_STICKY = false;
/**
* When this attribute appears in invocation's attachment, mock invoker will be used
*/
String INVOCATION_NEED_MOCK = "invocation.need.mock";
/**
* when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers
*/
String ROUTER_TYPE_CLEAR = "clean";
String DEFAULT_SCRIPT_TYPE_KEY = "javascript";
String PRIORITY_KEY = "priority";
String RULE_KEY = "rule";
String TYPE_KEY = "type";
String RUNTIME_KEY = "runtime";
String WARMUP_KEY = "warmup";
int DEFAULT_WARMUP = 10 * 60 * 1000;
String CONFIG_VERSION_KEY = "configVersion";
String OVERRIDE_PROVIDERS_KEY = "providerAddresses";
/**
* key for router type, for e.g., "script"/"file", corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME
*/
String ROUTER_KEY = "router";
/**
* The key name for reference URL in register center
*/
String REFER_KEY = "refer";
String ATTRIBUTE_KEY = "attribute";
/**
* The key name for export URL in register center
*/
String EXPORT_KEY = "export";
String PEER_KEY = "peer";
String CONSUMER_URL_KEY = "CONSUMER_URL";
/**
* prefix of arguments router key
*/
String ARGUMENTS = "arguments";
String NEED_REEXPORT = "need-reexport";
/**
* The key of shortestResponseSlidePeriod
*/
String SHORTEST_RESPONSE_SLIDE_PERIOD = "shortestResponseSlidePeriod";
String SHOULD_FAIL_FAST_KEY = "dubbo.router.should-fail-fast";
String RULE_VERSION_V27 = "v2.7";
String RULE_VERSION_V30 = "v3.0";
String RULE_VERSION_V31 = "v3.1";
public static final String TRAFFIC_DISABLE_KEY = "trafficDisable";
public static final String RATIO_KEY = "ratio";
public static final int DefaultRouteRatio = 0;
public static final int DefaultRouteConditionSubSetWeight = 100;
public static final int DefaultRoutePriority = 0;
public static final double DefaultAffinityRatio = 0;
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
================================================
/*
* 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;
import org.apache.dubbo.common.Node;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
/**
* Directory. (SPI, Prototype, ThreadSafe)
*
* Directory Service
*
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
*/
public interface Directory extends Node {
/**
* get service type.
*
* @return service type.
*/
Class getInterface();
/**
* list invokers.
* filtered by invocation
*
* @return invokers
*/
List> list(Invocation invocation) throws RpcException;
/**
* list invokers
* include all invokers from registry
*/
List> getAllInvokers();
URL getConsumerUrl();
boolean isDestroyed();
default boolean isEmpty() {
return CollectionUtils.isEmpty(getAllInvokers());
}
default boolean isServiceDiscovery() {
return false;
}
void discordAddresses();
RouterChain getRouterChain();
/**
* invalidate an invoker, add it into reconnect task, remove from list next time
* will be recovered by address refresh notification or reconnect success notification
*
* @param invoker invoker to invalidate
*/
void addInvalidateInvoker(Invoker invoker);
/**
* disable an invoker, remove from list next time
* will be removed when invoker is removed by address refresh notification
* using in service offline notification
*
* @param invoker invoker to invalidate
*/
void addDisabledInvoker(Invoker invoker);
/**
* recover a disabled invoker
*
* @param invoker invoker to invalidate
*/
void recoverDisabledInvoker(Invoker invoker);
default boolean isNotificationReceived() {
return false;
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/LoadBalance.java
================================================
/*
* 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;
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.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
import java.util.List;
/**
* LoadBalance. (SPI, Singleton, ThreadSafe)
*
* Load-Balancing
*
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
*/
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
/**
* select one invoker in list.
*
* @param invokers invokers.
* @param url refer url
* @param invocation invocation.
* @return selected invoker.
*/
@Adaptive("loadbalance")
Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException;
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/MergeableClusterScopeModelInitializer.java
================================================
/*
* 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;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.rpc.cluster.merger.MergerFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ScopeModelInitializer;
public class MergeableClusterScopeModelInitializer implements ScopeModelInitializer {
@Override
public void initializeApplicationModel(ApplicationModel applicationModel) {
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
beanFactory.registerBean(MergerFactory.class);
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Merger.java
================================================
/*
* 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;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface Merger {
T merge(T... items);
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ProviderURLMergeProcessor.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
import java.util.Map;
@SPI("default")
public interface ProviderURLMergeProcessor {
/**
* Merging the URL parameters of provider and consumer
*
* @param remoteUrl providerUrl
* @param localParametersMap consumer url parameters
* @return
*/
URL mergeUrl(URL remoteUrl, Map localParametersMap);
default Map mergeLocalParams(Map localMap) {
return localMap;
}
default boolean accept(URL providerUrl, Map localParametersMap) {
return true;
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Router.java
================================================
/*
* 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;
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.router.RouterResult;
import java.util.List;
/**
* Router. (SPI, Prototype, ThreadSafe)
*
* Routing
*
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory, boolean)
* @see org.apache.dubbo.rpc.cluster.Directory#list(Invocation)
*/
public interface Router 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.
*
* @param invokers invoker list
* @param url refer url
* @param invocation invocation
* @return routed invokers
* @throws RpcException
*/
@Deprecated
default List> route(List> invokers, URL url, Invocation invocation) throws RpcException {
return null;
}
/**
* ** This method can return the state of whether routerChain needed to continue route. **
* Filter invokers with current routing rule and only return the invokers that comply with the rule.
*
* @param invokers invoker list
* @param url refer url
* @param invocation invocation
* @param needToPrintMessage whether to print router state. Such as `use router branch a`.
* @return state with route result
* @throws RpcException
*/
default RouterResult> route(
List> invokers, URL url, Invocation invocation, boolean needToPrintMessage) throws RpcException {
return new RouterResult<>(route(invokers, url, invocation));
}
/**
* Notify the router the invoker list. Invoker list may change from time to time. This method gives the router a
* chance to prepare before {@link Router#route(List, URL, Invocation)} gets called.
*
* @param invokers invoker list
* @param invoker's type
*/
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();
/**
* To decide whether this router should take effect when none of the invoker can match the router rule, which
* means the {@link #route(List, URL, Invocation)} would be empty. Most of time, most router implementation would
* default this value to false.
*
* @return true to execute if none of invokers matches the current router
*/
boolean isForce();
/**
* Router's priority, used to sort routers.
*
* @return router's priority
*/
int getPriority();
default void stop() {
// do nothing by default
}
@Override
default int compareTo(Router o) {
if (o == null) {
throw new IllegalArgumentException();
}
return Integer.compare(this.getPriority(), o.getPriority());
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.LoggerCodeConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
/**
* Router chain
*/
public class RouterChain {
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RouterChain.class);
private volatile SingleRouterChain mainChain;
private volatile SingleRouterChain backupChain;
private volatile SingleRouterChain currentChain;
@SuppressWarnings({"rawtypes", "unchecked"})
public static RouterChain buildChain(Class interfaceClass, URL url) {
SingleRouterChain chain1 = buildSingleChain(interfaceClass, url);
SingleRouterChain chain2 = buildSingleChain(interfaceClass, url);
return new RouterChain<>(new SingleRouterChain[] {chain1, chain2});
}
public static SingleRouterChain buildSingleChain(Class interfaceClass, URL url) {
ModuleModel moduleModel = url.getOrDefaultModuleModel();
List extensionFactories =
moduleModel.getExtensionLoader(RouterFactory.class).getActivateExtension(url, ROUTER_KEY);
List routers = extensionFactories.stream()
.map(factory -> factory.getRouter(url))
.sorted(Router::compareTo)
.collect(Collectors.toList());
List> stateRouters =
moduleModel.getExtensionLoader(StateRouterFactory.class).getActivateExtension(url, ROUTER_KEY).stream()
.map(factory -> factory.getRouter(interfaceClass, url))
.collect(Collectors.toList());
boolean shouldFailFast = Boolean.parseBoolean(
ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true"));
RouterSnapshotSwitcher routerSnapshotSwitcher =
ScopeModelUtil.getFrameworkModel(moduleModel).getBeanFactory().getBean(RouterSnapshotSwitcher.class);
return new SingleRouterChain<>(routers, stateRouters, shouldFailFast, routerSnapshotSwitcher);
}
public RouterChain(SingleRouterChain[] chains) {
if (chains.length != 2) {
throw new IllegalArgumentException("chains' size should be 2.");
}
this.mainChain = chains[0];
this.backupChain = chains[1];
this.currentChain = this.mainChain;
}
private final AtomicReference>> notifyingInvokers = new AtomicReference<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public ReadWriteLock getLock() {
return lock;
}
public SingleRouterChain getSingleChain(URL url, BitList> availableInvokers, Invocation invocation) {
// If current is in:
// 1. `setInvokers` is in progress
// 2. Most of the invocation should use backup chain => currentChain == backupChain
// 3. Main chain has been update success => notifyingInvokers.get() != null
// If `availableInvokers` is created from origin invokers => use backup chain
// If `availableInvokers` is created from newly invokers => use main chain
BitList> notifying = notifyingInvokers.get();
if (notifying != null
&& currentChain == backupChain
&& availableInvokers.getOriginList() == notifying.getOriginList()) {
return mainChain;
}
return currentChain;
}
/**
* @deprecated use {@link RouterChain#getSingleChain(URL, BitList, Invocation)} and {@link SingleRouterChain#route(URL, BitList, Invocation)} instead
*/
@Deprecated
public List> route(URL url, BitList> availableInvokers, Invocation invocation) {
return getSingleChain(url, availableInvokers, invocation).route(url, availableInvokers, invocation);
}
/**
* Notify router chain of the initial addresses from registry at the first time.
* Notify whenever addresses in registry change.
*/
public synchronized void setInvokers(BitList> invokers, Runnable switchAction) {
try {
// Lock to prevent directory continue list
lock.writeLock().lock();
// Switch to back up chain. Will update main chain first.
currentChain = backupChain;
} finally {
// Release lock to minimize the impact for each newly created invocations as much as possible.
// Should not release lock until main chain update finished. Or this may cause long hang.
lock.writeLock().unlock();
}
// Refresh main chain.
// No one can request to use main chain. `currentChain` is backup chain. `route` method cannot access main
// chain.
try {
// Lock main chain to wait all invocation end
// To wait until no one is using main chain.
mainChain.getLock().writeLock().lock();
// refresh
mainChain.setInvokers(invokers);
} catch (Throwable t) {
logger.error(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when refreshing router chain.", t);
throw t;
} finally {
// Unlock main chain
mainChain.getLock().writeLock().unlock();
}
// Set the reference of newly invokers to temp variable.
// Reason: The next step will switch the invokers reference in directory, so we should check the
// `availableInvokers`
// argument when `route`. If the current invocation use newly invokers, we should use main chain to
// route, and
// this can prevent use newly invokers to route backup chain, which can only route origin invokers now.
notifyingInvokers.set(invokers);
// Switch the invokers reference in directory.
// Cannot switch before update main chain or after backup chain update success. Or that will cause state
// inconsistent.
switchAction.run();
try {
// Lock to prevent directory continue list
// The invokers reference in directory now should be the newly one and should always use the newly one once
// lock released.
lock.writeLock().lock();
// Switch to main chain. Will update backup chain later.
currentChain = mainChain;
// Clean up temp variable.
// `availableInvokers` check is useless now, because `route` method will no longer receive any
// `availableInvokers` related
// with the origin invokers. The getter of invokers reference in directory is locked now, and will return
// newly invokers
// once lock released.
notifyingInvokers.set(null);
} finally {
// Release lock to minimize the impact for each newly created invocations as much as possible.
// Will use newly invokers and main chain now.
lock.writeLock().unlock();
}
// Refresh main chain.
// No one can request to use main chain. `currentChain` is main chain. `route` method cannot access backup
// chain.
try {
// Lock main chain to wait all invocation end
backupChain.getLock().writeLock().lock();
// refresh
backupChain.setInvokers(invokers);
} catch (Throwable t) {
logger.error(LoggerCodeConstants.INTERNAL_ERROR, "", "", "Error occurred when refreshing router chain.", t);
throw t;
} finally {
// Unlock backup chain
backupChain.getLock().writeLock().unlock();
}
}
public synchronized void destroy() {
// 1. destroy another
backupChain.destroy();
// 2. switch
lock.writeLock().lock();
currentChain = backupChain;
lock.writeLock().unlock();
// 4. destroy
mainChain.destroy();
}
public void addRouters(List routers) {
mainChain.addRouters(routers);
backupChain.addRouters(routers);
}
public SingleRouterChain getCurrentChain() {
return currentChain;
}
public List getRouters() {
return currentChain.getRouters();
}
public StateRouter getHeadStateRouter() {
return currentChain.getHeadStateRouter();
}
@Deprecated
public List> getStateRouters() {
return currentChain.getStateRouters();
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterFactory.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
/**
* RouterFactory. (SPI, Singleton, ThreadSafe)
*
* Note Router has a different behaviour since 2.7.0, for each type of Router, there will only has one Router instance
* for each service. See {@link CacheableRouterFactory} and {@link RouterChain} for how to extend a new Router or how
* the Router instances are loaded.
*/
@SPI
public interface RouterFactory {
/**
* Create router.
* Since 2.7.0, most of the time, we will not use @Adaptive feature, so it's kept only for compatibility.
*
* @param url url
* @return router instance
*/
@Adaptive(CommonConstants.PROTOCOL_KEY)
Router getRouter(URL url);
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RuleConverter.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
import java.util.List;
@SPI
public interface RuleConverter {
List convert(URL subscribeUrl, Object source);
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/SingleRouterChain.java
================================================
/*
* 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;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.cluster.router.RouterResult;
import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
import org.apache.dubbo.rpc.cluster.router.RouterSnapshotSwitcher;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
import org.apache.dubbo.rpc.cluster.router.state.TailStateRouter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_STOP;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
/**
* Router chain
*/
public class SingleRouterChain {
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SingleRouterChain.class);
/**
* full list of addresses from registry, classified by method name.
*/
private volatile BitList> invokers = BitList.emptyList();
/**
* containing all routers, reconstruct every time 'route://' urls change.
*/
private volatile List routers = Collections.emptyList();
/**
* Fixed router instances: ConfigConditionRouter, TagRouter, e.g.,
* the rule for each instance may change but the instance will never delete or recreate.
*/
private volatile List builtinRouters = Collections.emptyList();
private volatile StateRouter headStateRouter;
private volatile List> stateRouters;
/**
* Should continue route if current router's result is empty
*/
private final boolean shouldFailFast;
private final RouterSnapshotSwitcher routerSnapshotSwitcher;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public SingleRouterChain(
List routers,
List> stateRouters,
boolean shouldFailFast,
RouterSnapshotSwitcher routerSnapshotSwitcher) {
initWithRouters(routers);
initWithStateRouters(stateRouters);
this.shouldFailFast = shouldFailFast;
this.routerSnapshotSwitcher = routerSnapshotSwitcher;
}
private void initWithStateRouters(List> stateRouters) {
StateRouter stateRouter = TailStateRouter.getInstance();
for (int i = stateRouters.size() - 1; i >= 0; i--) {
StateRouter nextStateRouter = stateRouters.get(i);
nextStateRouter.setNextRouter(stateRouter);
stateRouter = nextStateRouter;
}
this.headStateRouter = stateRouter;
this.stateRouters = Collections.unmodifiableList(stateRouters);
}
/**
* the resident routers must being initialized before address notification.
* only for ut
*/
public void initWithRouters(List builtinRouters) {
this.builtinRouters = builtinRouters;
this.routers = new LinkedList<>(builtinRouters);
}
/**
* If we use route:// protocol in version before 2.7.0, each URL will generate a Router instance, so we should
* keep the routers up to date, that is, each time router URLs changes, we should update the routers list, only
* keep the builtinRouters which are available all the time and the latest notified routers which are generated
* from URLs.
*
* @param routers routers from 'router://' rules in 2.6.x or before.
*/
public void addRouters(List routers) {
List newRouters = new LinkedList<>();
newRouters.addAll(builtinRouters);
newRouters.addAll(routers);
CollectionUtils.sort(newRouters);
this.routers = newRouters;
}
public List getRouters() {
return routers;
}
public StateRouter getHeadStateRouter() {
return headStateRouter;
}
public List> route(URL url, BitList> availableInvokers, Invocation invocation) {
if (invokers.getOriginList() != availableInvokers.getOriginList()) {
logger.error(
INTERNAL_ERROR,
"",
"Router's invoker size: " + invokers.getOriginList().size() + " Invocation's invoker size: "
+ availableInvokers.getOriginList().size(),
"Reject to route, because the invokers has changed.");
throw new IllegalStateException("reject to route, because the invokers has changed.");
}
if (RpcContext.getServiceContext().isNeedPrintRouterSnapshot()) {
return routeAndPrint(url, availableInvokers, invocation);
} else {
return simpleRoute(url, availableInvokers, invocation);
}
}
public List> routeAndPrint(URL url, BitList> availableInvokers, Invocation invocation) {
RouterSnapshotNode snapshot = buildRouterSnapshot(url, availableInvokers, invocation);
logRouterSnapshot(url, invocation, snapshot);
return snapshot.getChainOutputInvokers();
}
public List> simpleRoute(URL url, BitList> availableInvokers, Invocation invocation) {
BitList> resultInvokers = availableInvokers.clone();
// 1. route state router
resultInvokers = headStateRouter.route(resultInvokers, url, invocation, false, null);
if (resultInvokers.isEmpty() && (shouldFailFast || routers.isEmpty())) {
printRouterSnapshot(url, availableInvokers, invocation);
return BitList.emptyList();
}
if (routers.isEmpty()) {
return resultInvokers;
}
List> commonRouterResult = resultInvokers.cloneToArrayList();
// 2. route common router
for (Router router : routers) {
// Copy resultInvokers to a arrayList. BitList not support
RouterResult> routeResult = router.route(commonRouterResult, url, invocation, false);
commonRouterResult = routeResult.getResult();
if (CollectionUtils.isEmpty(commonRouterResult) && shouldFailFast) {
printRouterSnapshot(url, availableInvokers, invocation);
return BitList.emptyList();
}
// stop continue routing
if (!routeResult.isNeedContinueRoute()) {
return commonRouterResult;
}
}
if (commonRouterResult.isEmpty()) {
printRouterSnapshot(url, availableInvokers, invocation);
return BitList.emptyList();
}
return commonRouterResult;
}
/**
* store each router's input and output, log out if empty
*/
private void printRouterSnapshot(URL url, BitList> availableInvokers, Invocation invocation) {
if (logger.isWarnEnabled()) {
logRouterSnapshot(url, invocation, buildRouterSnapshot(url, availableInvokers, invocation));
}
}
/**
* Build each router's result
*/
public RouterSnapshotNode buildRouterSnapshot(
URL url, BitList> availableInvokers, Invocation invocation) {
BitList> resultInvokers = availableInvokers.clone();
RouterSnapshotNode parentNode = new RouterSnapshotNode<>("Parent", resultInvokers.clone());
parentNode.setNodeOutputInvokers(resultInvokers.clone());
// 1. route state router
Holder> nodeHolder = new Holder<>();
nodeHolder.set(parentNode);
resultInvokers = headStateRouter.route(resultInvokers, url, invocation, true, nodeHolder);
// result is empty, log out
if (routers.isEmpty() || (resultInvokers.isEmpty() && shouldFailFast)) {
parentNode.setChainOutputInvokers(resultInvokers.clone());
return parentNode;
}
RouterSnapshotNode commonRouterNode = new RouterSnapshotNode<>("CommonRouter", resultInvokers.clone());
parentNode.appendNode(commonRouterNode);
List> commonRouterResult = resultInvokers;
// 2. route common router
for (Router router : routers) {
// Copy resultInvokers to a arrayList. BitList not support
List> inputInvokers = new ArrayList<>(commonRouterResult);
RouterSnapshotNode currentNode =
new RouterSnapshotNode<>(router.getClass().getSimpleName(), inputInvokers);
// append to router node chain
commonRouterNode.appendNode(currentNode);
commonRouterNode = currentNode;
RouterResult> routeStateResult = router.route(inputInvokers, url, invocation, true);
List> routeResult = routeStateResult.getResult();
String routerMessage = routeStateResult.getMessage();
currentNode.setNodeOutputInvokers(routeResult);
currentNode.setRouterMessage(routerMessage);
commonRouterResult = routeResult;
// result is empty, log out
if (CollectionUtils.isEmpty(routeResult) && shouldFailFast) {
break;
}
if (!routeStateResult.isNeedContinueRoute()) {
break;
}
}
commonRouterNode.setChainOutputInvokers(commonRouterNode.getNodeOutputInvokers());
// 3. set router chain output reverse
RouterSnapshotNode currentNode = commonRouterNode;
while (currentNode != null) {
RouterSnapshotNode parent = currentNode.getParentNode();
if (parent != null) {
// common router only has one child invoke
parent.setChainOutputInvokers(currentNode.getChainOutputInvokers());
}
currentNode = parent;
}
return parentNode;
}
private void logRouterSnapshot(URL url, Invocation invocation, RouterSnapshotNode snapshotNode) {
if (snapshotNode.getChainOutputInvokers() == null
|| snapshotNode.getChainOutputInvokers().isEmpty()) {
if (logger.isWarnEnabled()) {
String message = "No provider available after route for the service " + url.getServiceKey()
+ " from registry " + url.getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Router snapshot is below: \n"
+ snapshotNode.toString();
if (routerSnapshotSwitcher.isEnable()) {
routerSnapshotSwitcher.setSnapshot(message);
}
logger.warn(
CLUSTER_NO_VALID_PROVIDER, "No provider available after route for the service", "", message);
}
} else {
if (logger.isInfoEnabled()) {
String message = "Router snapshot service " + url.getServiceKey()
+ " from registry " + url.getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + " is below: \n"
+ snapshotNode.toString();
if (routerSnapshotSwitcher.isEnable()) {
routerSnapshotSwitcher.setSnapshot(message);
}
logger.info(message);
}
}
}
/**
* Notify router chain of the initial addresses from registry at the first time.
* Notify whenever addresses in registry change.
*/
public void setInvokers(BitList> invokers) {
this.invokers = (invokers == null ? BitList.emptyList() : invokers);
routers.forEach(router -> router.notify(this.invokers));
stateRouters.forEach(router -> router.notify(this.invokers));
}
/**
* for uts only
*/
@Deprecated
public void setHeadStateRouter(StateRouter headStateRouter) {
this.headStateRouter = headStateRouter;
}
/**
* for uts only
*/
@Deprecated
public List> getStateRouters() {
return stateRouters;
}
public ReadWriteLock getLock() {
return lock;
}
public void destroy() {
invokers = BitList.emptyList();
for (Router router : routers) {
try {
router.stop();
} catch (Exception e) {
logger.error(
CLUSTER_FAILED_STOP,
"route stop failed",
"",
"Error trying to stop router " + router.getClass(),
e);
}
}
routers = Collections.emptyList();
builtinRouters = Collections.emptyList();
for (StateRouter router : stateRouters) {
try {
router.stop();
} catch (Exception e) {
logger.error(
CLUSTER_FAILED_STOP,
"StateRouter stop failed",
"",
"Error trying to stop StateRouter " + router.getClass(),
e);
}
}
stateRouters = Collections.emptyList();
headStateRouter = TailStateRouter.getInstance();
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
================================================
/*
* 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.configurator;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.cluster.Configurator;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConditionMatch;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACES;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.CONFIG_VERSION_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.RULE_VERSION_V30;
import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION;
public abstract class AbstractConfigurator implements Configurator {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigurator.class);
private static final String TILDE = "~";
private final URL configuratorUrl;
public AbstractConfigurator(URL url) {
if (url == null) {
throw new IllegalArgumentException("configurator url == null");
}
this.configuratorUrl = url;
}
@Override
public URL getUrl() {
return configuratorUrl;
}
@Override
public URL configure(URL url) {
// If override url is not enabled or is invalid, just return.
if (!configuratorUrl.getParameter(ENABLED_KEY, true)
|| configuratorUrl.getHost() == null
|| url == null
|| url.getHost() == null) {
logger.info("Cannot apply configurator rule, the rule is disabled or is invalid: \n" + configuratorUrl);
return url;
}
String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
if (StringUtils.isNotEmpty(apiVersion)) { // v2.7 or above
String currentSide = url.getSide();
String configuratorSide = configuratorUrl.getSide();
if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide)) {
url = configureIfMatch(NetUtils.getLocalHost(), url);
} else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide)) {
url = configureIfMatch(url.getHost(), url);
}
}
/*
* This else branch is deprecated and is left only to keep compatibility with versions before 2.7.0
*/
else {
url = configureDeprecated(url);
}
return url;
}
@Deprecated
private URL configureDeprecated(URL url) {
// If override url has port, means it is a provider address. We want to control a specific provider with this
// override url, it may take effect on the specific provider instance or on consumers holding this provider
// instance.
if (configuratorUrl.getPort() != 0) {
if (url.getPort() == configuratorUrl.getPort()) {
return configureIfMatch(url.getHost(), url);
}
} else {
/*
* override url don't have a port, means the ip override url specify is a consumer address or 0.0.0.0.
* 1.If it is a consumer ip address, the intention is to control a specific consumer instance, it must takes effect at the consumer side, any provider received this override url should ignore.
* 2.If the ip is 0.0.0.0, this override url can be used on consumer, and also can be used on provider.
*/
if (url.getSide(PROVIDER).equals(CONSUMER)) {
// NetUtils.getLocalHost is the ip address consumer registered to registry.
return configureIfMatch(NetUtils.getLocalHost(), url);
} else if (url.getSide(CONSUMER).equals(PROVIDER)) {
// take effect on all providers, so address must be 0.0.0.0, otherwise it won't flow to this if branch
return configureIfMatch(ANYHOST_VALUE, url);
}
}
return url;
}
private URL configureIfMatch(String host, URL url) {
if (ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) {
if (isV27ConditionMatchOrUnset(url)) {
Set conditionKeys = genConditionKeys();
String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
if (apiVersion != null && apiVersion.startsWith(RULE_VERSION_V30)) {
ConditionMatch matcher = (ConditionMatch) configuratorUrl.getAttribute(MATCH_CONDITION);
if (matcher != null) {
if (matcher.isMatch(host, url)) {
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
} else {
logger.debug("Cannot apply configurator rule, param mismatch, current params are " + url
+ ", params in rule is " + matcher);
}
} else {
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
}
} else if (isDeprecatedConditionMatch(conditionKeys, url)) {
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
}
}
} else {
logger.debug("Cannot apply configurator rule, host mismatch, current host is " + host + ", host in rule is "
+ configuratorUrl.getHost());
}
return url;
}
/**
* Check if v2.7 configurator rule is set and can be matched.
*
* @param url the configurator rule url
* @return true if v2.7 configurator rule is not set or the rule can be matched.
*/
private boolean isV27ConditionMatchOrUnset(URL url) {
String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
if (StringUtils.isNotEmpty(providers)) {
boolean match = false;
String[] providerAddresses = providers.split(CommonConstants.COMMA_SEPARATOR);
for (String address : providerAddresses) {
if (address.equals(url.getAddress())
|| address.equals(ANYHOST_VALUE)
|| address.equals(ANYHOST_VALUE + CommonConstants.GROUP_CHAR_SEPARATOR + ANY_VALUE)
|| address.equals(ANYHOST_VALUE + CommonConstants.GROUP_CHAR_SEPARATOR + url.getPort())
|| address.equals(url.getHost())) {
match = true;
}
}
if (!match) {
logger.debug("Cannot apply configurator rule, provider address mismatch, current address "
+ url.getAddress() + ", address in rule is " + providers);
return false;
}
}
String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
String currentApplication = url.getApplication(url.getUsername());
if (configApplication != null
&& !ANY_VALUE.equals(configApplication)
&& !configApplication.equals(currentApplication)) {
logger.debug("Cannot apply configurator rule, application name mismatch, current application is "
+ currentApplication + ", application in rule is " + configApplication);
return false;
}
String configServiceKey = configuratorUrl.getServiceKey();
String currentServiceKey = url.getServiceKey();
if (!ANY_VALUE.equals(configServiceKey) && !configServiceKey.equals(currentServiceKey)) {
logger.debug("Cannot apply configurator rule, service mismatch, current service is " + currentServiceKey
+ ", service in rule is " + configServiceKey);
return false;
}
return true;
}
private boolean isDeprecatedConditionMatch(Set conditionKeys, URL url) {
boolean result = true;
for (Map.Entry entry : configuratorUrl.getParameters().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
boolean startWithTilde = startWithTilde(key);
if (startWithTilde || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) {
if (startWithTilde) {
conditionKeys.add(key);
}
if (value != null
&& !ANY_VALUE.equals(value)
&& !value.equals(url.getParameter(startWithTilde ? key.substring(1) : key))) {
result = false;
break;
}
}
}
return result;
}
private Set genConditionKeys() {
Set conditionKeys = new HashSet<>();
conditionKeys.add(CATEGORY_KEY);
conditionKeys.add(Constants.CHECK_KEY);
conditionKeys.add(DYNAMIC_KEY);
conditionKeys.add(ENABLED_KEY);
conditionKeys.add(GROUP_KEY);
conditionKeys.add(VERSION_KEY);
conditionKeys.add(APPLICATION_KEY);
conditionKeys.add(SIDE_KEY);
conditionKeys.add(CONFIG_VERSION_KEY);
conditionKeys.add(COMPATIBLE_CONFIG_KEY);
conditionKeys.add(INTERFACES);
return conditionKeys;
}
private boolean startWithTilde(String key) {
return StringUtils.isNotEmpty(key) && key.startsWith(TILDE);
}
protected abstract URL doConfigure(URL currentUrl, URL configUrl);
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/absent/AbsentConfigurator.java
================================================
/*
* 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.configurator.absent;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.configurator.AbstractConfigurator;
public class AbsentConfigurator extends AbstractConfigurator {
public AbsentConfigurator(URL url) {
super(url);
}
@Override
public URL doConfigure(URL currentUrl, URL configUrl) {
return currentUrl.addParametersIfAbsent(configUrl.getParameters());
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/absent/AbsentConfiguratorFactory.java
================================================
/*
* 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.configurator.absent;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.Configurator;
import org.apache.dubbo.rpc.cluster.ConfiguratorFactory;
/**
* AbsentConfiguratorFactory
*
*/
public class AbsentConfiguratorFactory implements ConfiguratorFactory {
@Override
public Configurator getConfigurator(URL url) {
return new AbsentConfigurator(url);
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/override/OverrideConfigurator.java
================================================
/*
* 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.configurator.override;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.cluster.configurator.AbstractConfigurator;
public class OverrideConfigurator extends AbstractConfigurator {
public static final Logger logger = LoggerFactory.getLogger(OverrideConfigurator.class);
public OverrideConfigurator(URL url) {
super(url);
}
@Override
public URL doConfigure(URL currentUrl, URL configUrl) {
logger.info("Start overriding url " + currentUrl + " with override url " + configUrl);
return currentUrl.addParameters(configUrl.getParameters());
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/override/OverrideConfiguratorFactory.java
================================================
/*
* 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.configurator.override;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.Configurator;
import org.apache.dubbo.rpc.cluster.ConfiguratorFactory;
/**
* OverrideConfiguratorFactory
*
*/
public class OverrideConfiguratorFactory implements ConfiguratorFactory {
@Override
public Configurator getConfigurator(URL url) {
return new OverrideConfigurator(url);
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java
================================================
/*
* 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.configurator.parser;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem;
import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
import static org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig.MATCH_CONDITION;
/**
* Config parser
*/
public class ConfigParser {
public static List parseConfigurators(String rawConfig) {
// compatible url JsonArray, such as [ "override://xxx", "override://xxx" ]
List compatibleUrls = parseJsonArray(rawConfig);
if (CollectionUtils.isNotEmpty(compatibleUrls)) {
return compatibleUrls;
}
List urls = new ArrayList<>();
ConfiguratorConfig configuratorConfig = parseObject(rawConfig);
String scope = configuratorConfig.getScope();
List items = configuratorConfig.getConfigs();
if (ConfiguratorConfig.SCOPE_APPLICATION.equals(scope)) {
items.forEach(item -> urls.addAll(appItemToUrls(item, configuratorConfig)));
} else {
// service scope by default.
items.forEach(item -> urls.addAll(serviceItemToUrls(item, configuratorConfig)));
}
return urls;
}
private static List parseJsonArray(String rawConfig) {
List urls = new ArrayList<>();
try {
List list = JsonUtils.toJavaList(rawConfig, String.class);
if (!CollectionUtils.isEmpty(list)) {
list.forEach(u -> urls.add(URL.valueOf(u)));
}
} catch (Throwable t) {
return null;
}
return urls;
}
private static ConfiguratorConfig parseObject(String rawConfig) {
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
Map map = yaml.load(rawConfig);
return ConfiguratorConfig.parseFromMap(map);
}
private static List serviceItemToUrls(ConfigItem item, ConfiguratorConfig config) {
List urls = new ArrayList<>();
List addresses = parseAddresses(item);
addresses.forEach(addr -> {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("override://").append(addr).append('/');
urlBuilder.append(appendService(config.getKey()));
urlBuilder.append(toParameterString(item));
parseEnabled(item, config, urlBuilder);
urlBuilder.append("&configVersion=").append(config.getConfigVersion());
List apps = item.getApplications();
if (CollectionUtils.isNotEmpty(apps)) {
apps.forEach(app -> {
StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder);
urls.add(appendMatchCondition(
URL.valueOf(tmpUrlBuilder
.append("&application=")
.append(app)
.toString()),
item));
});
} else {
urls.add(appendMatchCondition(URL.valueOf(urlBuilder.toString()), item));
}
});
return urls;
}
private static List appItemToUrls(ConfigItem item, ConfiguratorConfig config) {
List urls = new ArrayList<>();
List addresses = parseAddresses(item);
for (String addr : addresses) {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("override://").append(addr).append('/');
List services = item.getServices();
if (services == null) {
services = new ArrayList<>();
}
if (services.isEmpty()) {
services.add("*");
}
for (String s : services) {
StringBuilder tmpUrlBuilder = new StringBuilder(urlBuilder);
tmpUrlBuilder.append(appendService(s));
tmpUrlBuilder.append(toParameterString(item));
tmpUrlBuilder.append("&application=").append(config.getKey());
parseEnabled(item, config, tmpUrlBuilder);
tmpUrlBuilder.append("&category=").append(APP_DYNAMIC_CONFIGURATORS_CATEGORY);
tmpUrlBuilder.append("&configVersion=").append(config.getConfigVersion());
urls.add(appendMatchCondition(URL.valueOf(tmpUrlBuilder.toString()), item));
}
}
return urls;
}
private static String toParameterString(ConfigItem item) {
StringBuilder sb = new StringBuilder();
sb.append("category=");
sb.append(DYNAMIC_CONFIGURATORS_CATEGORY);
if (item.getSide() != null) {
sb.append("&side=");
sb.append(item.getSide());
}
Map parameters = item.getParameters();
if (CollectionUtils.isEmptyMap(parameters)) {
throw new IllegalStateException("Invalid configurator rule, please specify at least one parameter "
+ "you want to change in the rule.");
}
parameters.forEach((k, v) -> {
sb.append('&');
sb.append(k);
sb.append('=');
sb.append(v);
});
if (CollectionUtils.isNotEmpty(item.getProviderAddresses())) {
sb.append('&');
sb.append(OVERRIDE_PROVIDERS_KEY);
sb.append('=');
sb.append(CollectionUtils.join(item.getProviderAddresses(), ","));
} else if (PROVIDER.equals(item.getSide())) {
sb.append('&');
sb.append(OVERRIDE_PROVIDERS_KEY);
sb.append('=');
sb.append(CollectionUtils.join(parseAddresses(item), ","));
}
return sb.toString();
}
private static String appendService(String serviceKey) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isEmpty(serviceKey)) {
throw new IllegalStateException("service field in configuration is null.");
}
String interfaceName = serviceKey;
int i = interfaceName.indexOf('/');
if (i > 0) {
sb.append("group=");
sb.append(interfaceName, 0, i);
sb.append('&');
interfaceName = interfaceName.substring(i + 1);
}
int j = interfaceName.indexOf(':');
if (j > 0) {
sb.append("version=");
sb.append(interfaceName.substring(j + 1));
sb.append('&');
interfaceName = interfaceName.substring(0, j);
}
sb.insert(0, interfaceName + "?");
return sb.toString();
}
private static void parseEnabled(ConfigItem item, ConfiguratorConfig config, StringBuilder urlBuilder) {
urlBuilder.append("&enabled=");
if (item.getType() == null || ConfigItem.GENERAL_TYPE.equals(item.getType())) {
urlBuilder.append(config.getEnabled());
} else {
urlBuilder.append(item.getEnabled());
}
}
private static List parseAddresses(ConfigItem item) {
List addresses = item.getAddresses();
if (addresses == null) {
addresses = new ArrayList<>();
}
if (addresses.isEmpty()) {
addresses.add(ANYHOST_VALUE);
}
return addresses;
}
private static URL appendMatchCondition(URL url, ConfigItem item) {
if (item.getMatch() != null) {
url = url.putAttribute(MATCH_CONDITION, item.getMatch());
}
return url;
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConditionMatch.java
================================================
/*
* 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.configurator.parser.model;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.AddressMatch;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.ListStringMatch;
import java.util.List;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
public class ConditionMatch {
private AddressMatch address;
private AddressMatch providerAddress;
private ListStringMatch service;
private ListStringMatch app;
private List param;
public AddressMatch getAddress() {
return address;
}
public void setAddress(AddressMatch address) {
this.address = address;
}
public AddressMatch getProviderAddress() {
return providerAddress;
}
public void setProviderAddress(AddressMatch providerAddress) {
this.providerAddress = providerAddress;
}
public ListStringMatch getService() {
return service;
}
public void setService(ListStringMatch service) {
this.service = service;
}
public ListStringMatch getApp() {
return app;
}
public void setApp(ListStringMatch app) {
this.app = app;
}
public List getParam() {
return param;
}
public void setParam(List param) {
this.param = param;
}
public boolean isMatch(String host, URL url) {
if (getAddress() != null && !getAddress().isMatch(host)) {
return false;
}
if (getProviderAddress() != null && !getProviderAddress().isMatch(url.getAddress())) {
return false;
}
if (getService() != null && !getService().isMatch(url.getServiceKey())) {
return false;
}
if (getApp() != null && !getApp().isMatch(url.getParameter(APPLICATION_KEY))) {
return false;
}
if (getParam() != null) {
for (ParamMatch match : param) {
if (!match.isMatch(url)) {
return false;
}
}
}
return true;
}
@Override
public String toString() {
return "ConditionMatch{" + "address='"
+ address + '\'' + "providerAddress='"
+ providerAddress + '\'' + ", service='"
+ service + '\'' + ", app='"
+ app + '\'' + ", param='"
+ param + '\'' + '}';
}
}
================================================
FILE: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java
================================================
/*
* 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.configurator.parser.model;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.PojoUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
public class ConfigItem {
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigItem.class);
public static final String GENERAL_TYPE = "general";
public static final String WEIGHT_TYPE = "weight";
public static final String BALANCING_TYPE = "balancing";
public static final String DISABLED_TYPE = "disabled";
public static final String CONFIG_ITEM_TYPE = "type";
public static final String ENABLED_KEY = "enabled";
public static final String ADDRESSES_KEY = "addresses";
public static final String PROVIDER_ADDRESSES_KEY = "providerAddresses";
public static final String SERVICES_KEY = "services";
public static final String APPLICATIONS_KEY = "applications";
public static final String PARAMETERS_KEY = "parameters";
public static final String MATCH_KEY = "match";
public static final String SIDE_KEY = "side";
private String type;
private Boolean enabled;
private List addresses;
private List providerAddresses;
private List services;
private List applications;
private Map parameters;
private ConditionMatch match;
private String side;
@SuppressWarnings("unchecked")
public static ConfigItem parseFromMap(Map map) {
ConfigItem configItem = new ConfigItem();
configItem.setType((String) map.get(CONFIG_ITEM_TYPE));
Object enabled = map.get(ENABLED_KEY);
if (enabled != null) {
configItem.setEnabled(Boolean.parseBoolean(enabled.toString()));
}
Object addresses = map.get(ADDRESSES_KEY);
if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) {
configItem.setAddresses(
((List