[3.0] Code reformat (#8019)
* convert files, add checker * rename id * tab to space * change git attributes auto convert * fix end new line * fix end new line * fix end new line * ignore generated files * override tomcat-embed-core version * add ignore for codestyle/checkstyle.xml * add ignore for codestyle/checkstyle.xml * add rat ignore * add suppression * force update * disable zk dynamic configurator
This commit is contained in:
parent
724ec8b554
commit
0e66de1c61
|
|
@ -1,4 +1,20 @@
|
|||
# Git will understand that all files specified are not text,
|
||||
# and it should not try to change them.
|
||||
# This will prevent file formatting (such as `crlf` endings to `lf` endings) while commit.
|
||||
* -text
|
||||
# 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.
|
||||
#
|
||||
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
*.java text eol=lf
|
||||
|
|
@ -39,7 +39,25 @@ jobs:
|
|||
- name: "Build Dubbo with Maven"
|
||||
run: |
|
||||
cd ./dubbo
|
||||
./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress clean source:jar install -Pjacoco,rat,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
|
||||
./mvnw --batch-mode -U -e --no-transfer-progress clean source:jar install -Pjacoco,rat,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true
|
||||
- name: "Pack rat file if failure"
|
||||
if: failure()
|
||||
run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r
|
||||
- name: "Upload rat file if failure"
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "rat-file"
|
||||
path: ${{ github.workspace }}/rat.zip
|
||||
- name: "Pack checkstyle file if failure"
|
||||
if: failure()
|
||||
run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
|
||||
- name: "Upload checkstyle file if failure"
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "checkstyle-file"
|
||||
path: ${{ github.workspace }}/checkstyle.zip
|
||||
- name: "Calculate Dubbo Version"
|
||||
id: dubbo-version
|
||||
run: |
|
||||
|
|
@ -72,22 +90,13 @@ jobs:
|
|||
- name: "Test with Maven with Integration Tests"
|
||||
timeout-minutes: 40
|
||||
if: ${{ startsWith( matrix.os, 'ubuntu') }}
|
||||
run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true
|
||||
run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true
|
||||
- name: "Test with Maven without Integration Tests"
|
||||
env:
|
||||
DISABLE_FILE_SYSTEM_TEST: true
|
||||
timeout-minutes: 50
|
||||
if: ${{ startsWith( matrix.os, 'windows') }}
|
||||
run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true"
|
||||
- name: "Pack rat file if failure"
|
||||
if: failure()
|
||||
run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r
|
||||
- name: "Upload rat file if failure"
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "rat-file-${{ matrix.os }}-JDK${{ matrix.jdk }}"
|
||||
path: ${{ github.workspace }}/rat.zip
|
||||
run: ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -279,4 +279,4 @@ https://developers.google.com/protocol-buffers/ and is licensed under the follow
|
|||
Code generated by the Protocol Buffer compiler is owned by the owner
|
||||
of the input file used when generating it. This code is not
|
||||
standalone and requires a support library to be linked with it. This
|
||||
support library is itself covered by the above license.
|
||||
support library is itself covered by the above license.
|
||||
|
|
|
|||
28
NOTICE
28
NOTICE
|
|
@ -1,14 +1,14 @@
|
|||
Apache Dubbo
|
||||
Copyright 2018-2021 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
This product contains code form the Netty Project:
|
||||
|
||||
The Netty Project
|
||||
=================
|
||||
Please visit the Netty web site for more information:
|
||||
* http://netty.io/
|
||||
|
||||
Copyright 2014 The Netty Project
|
||||
Apache Dubbo
|
||||
Copyright 2018-2021 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
This product contains code form the Netty Project:
|
||||
|
||||
The Netty Project
|
||||
=================
|
||||
Please visit the Netty web site for more information:
|
||||
* http://netty.io/
|
||||
|
||||
Copyright 2014 The Netty Project
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files="[\\/]src[\\/]main[\\/]java[\\/]com[\\/]alibaba[\\/]com[\\/]caucho[\\/]hessian" checks=".*"/>
|
||||
<suppress files="[\\/]build[\\/]generated[\\/]source[\\/]proto" checks=".*"/>
|
||||
<suppress files="Yylex\.java" checks="AvoidEscapedUnicodeCharacters"/>
|
||||
</suppressions>
|
||||
|
|
@ -12,6 +12,10 @@
|
|||
<property name="fileExtensions" value="java"/>
|
||||
</module>
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<property name="fileExtensions" value="java,xml"/>
|
||||
</module>
|
||||
|
||||
<!-- TreeWalker Checks -->
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||
|
||||
<module name="Checker">
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf" />
|
||||
</module>
|
||||
</module>
|
||||
|
|
@ -39,4 +39,4 @@ public class DubboGenerator extends AbstractGenerator {
|
|||
protected String getClassSuffix() {
|
||||
return "Dubbo";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
<exclude>**/.classpath</exclude>
|
||||
<exclude>**/.project</exclude>
|
||||
<exclude>**/target/**</exclude>
|
||||
<exclude>**/generated/**</exclude>
|
||||
<exclude>**/*.log</exclude>
|
||||
<exclude>CONTRIBUTING.md</exclude>
|
||||
<exclude>README.md</exclude>
|
||||
|
|
|
|||
|
|
@ -38,4 +38,4 @@ public interface AddressListener {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,59 @@
|
|||
/*
|
||||
* 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.Adaptive;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
/**
|
||||
* Cluster. (SPI, Singleton, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Computer_cluster">Cluster</a>
|
||||
* <a href="http://en.wikipedia.org/wiki/Fault-tolerant_system">Fault-Tolerant</a>
|
||||
*
|
||||
*/
|
||||
@SPI(Cluster.DEFAULT)
|
||||
public interface Cluster {
|
||||
|
||||
String DEFAULT = "failover";
|
||||
|
||||
/**
|
||||
* Merge the directory invokers to a virtual invoker.
|
||||
*
|
||||
* @param <T>
|
||||
* @param directory
|
||||
* @return cluster invoker
|
||||
* @throws RpcException
|
||||
*/
|
||||
@Adaptive
|
||||
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
|
||||
|
||||
static Cluster getCluster(String name) {
|
||||
return getCluster(name, true);
|
||||
}
|
||||
|
||||
static Cluster getCluster(String name, boolean wrap) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
name = Cluster.DEFAULT;
|
||||
}
|
||||
return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.Adaptive;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
/**
|
||||
* Cluster. (SPI, Singleton, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Computer_cluster">Cluster</a>
|
||||
* <a href="http://en.wikipedia.org/wiki/Fault-tolerant_system">Fault-Tolerant</a>
|
||||
*
|
||||
*/
|
||||
@SPI(Cluster.DEFAULT)
|
||||
public interface Cluster {
|
||||
|
||||
String DEFAULT = "failover";
|
||||
|
||||
/**
|
||||
* Merge the directory invokers to a virtual invoker.
|
||||
*
|
||||
* @param <T>
|
||||
* @param directory
|
||||
* @return cluster invoker
|
||||
* @throws RpcException
|
||||
*/
|
||||
@Adaptive
|
||||
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
|
||||
|
||||
static Cluster getCluster(String name) {
|
||||
return getCluster(name, true);
|
||||
}
|
||||
|
||||
static Cluster getCluster(String name, boolean wrap) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
name = Cluster.DEFAULT;
|
||||
}
|
||||
return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +1,119 @@
|
|||
/*
|
||||
* 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.ExtensionLoader;
|
||||
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<Configurator> {
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <ol>
|
||||
* <li>override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... means global rules
|
||||
* (all of the providers take effect)</li>
|
||||
* <li>override://ip:port...?anyhost=false Special rules (only for a certain provider)</li>
|
||||
* <li>override:// rule is not supported... ,needs to be calculated by registry itself</li>
|
||||
* <li>override://0.0.0.0/ without parameters means clearing the override</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param urls URL list to convert
|
||||
* @return converted configurator list
|
||||
*/
|
||||
static Optional<List<Configurator>> toConfigurators(List<URL> urls) {
|
||||
if (CollectionUtils.isEmpty(urls)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
|
||||
.getAdaptiveExtension();
|
||||
|
||||
List<Configurator> configurators = new ArrayList<>(urls.size());
|
||||
for (URL url : urls) {
|
||||
if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
|
||||
configurators.clear();
|
||||
break;
|
||||
}
|
||||
Map<String, String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.ExtensionLoader;
|
||||
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<Configurator> {
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <ol>
|
||||
* <li>override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... means global rules
|
||||
* (all of the providers take effect)</li>
|
||||
* <li>override://ip:port...?anyhost=false Special rules (only for a certain provider)</li>
|
||||
* <li>override:// rule is not supported... ,needs to be calculated by registry itself</li>
|
||||
* <li>override://0.0.0.0/ without parameters means clearing the override</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param urls URL list to convert
|
||||
* @return converted configurator list
|
||||
*/
|
||||
static Optional<List<Configurator>> toConfigurators(List<URL> urls) {
|
||||
if (CollectionUtils.isEmpty(urls)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
|
||||
.getAdaptiveExtension();
|
||||
|
||||
List<Configurator> configurators = new ArrayList<>(urls.size());
|
||||
for (URL url : urls) {
|
||||
if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
|
||||
configurators.clear();
|
||||
break;
|
||||
}
|
||||
Map<String, String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* ConfiguratorFactory. (SPI, Singleton, ThreadSafe)
|
||||
*
|
||||
*/
|
||||
@SPI
|
||||
public interface ConfiguratorFactory {
|
||||
|
||||
/**
|
||||
* get the configurator instance.
|
||||
*
|
||||
* @param url - configurator url.
|
||||
* @return configurator instance.
|
||||
*/
|
||||
@Adaptive("protocol")
|
||||
Configurator getConfigurator(URL url);
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* ConfiguratorFactory. (SPI, Singleton, ThreadSafe)
|
||||
*
|
||||
*/
|
||||
@SPI
|
||||
public interface ConfiguratorFactory {
|
||||
|
||||
/**
|
||||
* get the configurator instance.
|
||||
*
|
||||
* @param url - configurator url.
|
||||
* @return configurator instance.
|
||||
*/
|
||||
@Adaptive("protocol")
|
||||
Configurator getConfigurator(URL url);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,72 +1,72 @@
|
|||
/*
|
||||
* 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)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Directory_service">Directory Service</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
*/
|
||||
public interface Directory<T> extends Node {
|
||||
|
||||
/**
|
||||
* get service type.
|
||||
*
|
||||
* @return service type.
|
||||
*/
|
||||
Class<T> getInterface();
|
||||
|
||||
/**
|
||||
* list invokers.
|
||||
*
|
||||
* @return invokers
|
||||
*/
|
||||
List<Invoker<T>> list(Invocation invocation) throws RpcException;
|
||||
|
||||
List<Invoker<T>> getAllInvokers();
|
||||
|
||||
URL getConsumerUrl();
|
||||
|
||||
boolean isDestroyed();
|
||||
|
||||
default boolean isEmpty() {
|
||||
return CollectionUtils.isEmpty(getAllInvokers());
|
||||
}
|
||||
|
||||
default boolean isServiceDiscovery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void discordAddresses();
|
||||
|
||||
RouterChain<T> getRouterChain();
|
||||
|
||||
default boolean isNotificationReceived() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Directory_service">Directory Service</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
*/
|
||||
public interface Directory<T> extends Node {
|
||||
|
||||
/**
|
||||
* get service type.
|
||||
*
|
||||
* @return service type.
|
||||
*/
|
||||
Class<T> getInterface();
|
||||
|
||||
/**
|
||||
* list invokers.
|
||||
*
|
||||
* @return invokers
|
||||
*/
|
||||
List<Invoker<T>> list(Invocation invocation) throws RpcException;
|
||||
|
||||
List<Invoker<T>> getAllInvokers();
|
||||
|
||||
URL getConsumerUrl();
|
||||
|
||||
boolean isDestroyed();
|
||||
|
||||
default boolean isEmpty() {
|
||||
return CollectionUtils.isEmpty(getAllInvokers());
|
||||
}
|
||||
|
||||
default boolean isServiceDiscovery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void discordAddresses();
|
||||
|
||||
RouterChain<T> getRouterChain();
|
||||
|
||||
default boolean isNotificationReceived() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,50 @@
|
|||
/*
|
||||
* 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)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
|
||||
*
|
||||
* @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")
|
||||
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
|
||||
|
||||
}
|
||||
/*
|
||||
* 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)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
|
||||
*
|
||||
* @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")
|
||||
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,103 +1,103 @@
|
|||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
/**
|
||||
* Router. (SPI, Prototype, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see org.apache.dubbo.rpc.cluster.Directory#list(Invocation)
|
||||
*/
|
||||
public interface Router extends Comparable<Router> {
|
||||
|
||||
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
|
||||
*/
|
||||
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
|
||||
|
||||
|
||||
/**
|
||||
* 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 <T> invoker's type
|
||||
*/
|
||||
default <T> void notify(List<Invoker<T>> 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());
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
/**
|
||||
* Router. (SPI, Prototype, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see org.apache.dubbo.rpc.cluster.Directory#list(Invocation)
|
||||
*/
|
||||
public interface Router extends Comparable<Router> {
|
||||
|
||||
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
|
||||
*/
|
||||
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
|
||||
|
||||
|
||||
/**
|
||||
* 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 <T> invoker's type
|
||||
*/
|
||||
default <T> void notify(List<Invoker<T>> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,47 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* RouterFactory. (SPI, Singleton, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see org.apache.dubbo.rpc.cluster.Directory#list(org.apache.dubbo.rpc.Invocation)
|
||||
* <p>
|
||||
* 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("protocol")
|
||||
Router getRouter(URL url);
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* RouterFactory. (SPI, Singleton, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see org.apache.dubbo.rpc.cluster.Directory#list(org.apache.dubbo.rpc.Invocation)
|
||||
* <p>
|
||||
* 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("protocol")
|
||||
Router getRouter(URL url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,170 +1,170 @@
|
|||
/*
|
||||
* 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.utils.NetUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.rpc.cluster.Configurator;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* AbstractOverrideConfigurator
|
||||
*/
|
||||
public abstract class AbstractConfigurator implements Configurator {
|
||||
|
||||
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) {
|
||||
return url;
|
||||
}
|
||||
/*
|
||||
* This if branch is created since 2.7.0.
|
||||
*/
|
||||
String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
|
||||
if (StringUtils.isNotEmpty(apiVersion)) {
|
||||
String currentSide = url.getSide();
|
||||
String configuratorSide = configuratorUrl.getSide();
|
||||
if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) {
|
||||
url = configureIfMatch(NetUtils.getLocalHost(), url);
|
||||
} else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) &&
|
||||
url.getPort() == configuratorUrl.getPort()) {
|
||||
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())) {
|
||||
// TODO, to support wildcards
|
||||
String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
|
||||
if (StringUtils.isEmpty(providers) || providers.contains(url.getAddress()) || providers.contains(ANYHOST_VALUE)) {
|
||||
String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
|
||||
String currentApplication = url.getApplication(url.getUsername());
|
||||
if (configApplication == null || ANY_VALUE.equals(configApplication)
|
||||
|| configApplication.equals(currentApplication)) {
|
||||
Set<String> conditionKeys = new HashSet<String>();
|
||||
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);
|
||||
for (Map.Entry<String, String> 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))) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
|
||||
}
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private boolean startWithTilde(String key) {
|
||||
if (StringUtils.isNotEmpty(key) && key.startsWith(TILDE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract URL doConfigure(URL currentUrl, URL configUrl);
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.utils.NetUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.rpc.cluster.Configurator;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* AbstractOverrideConfigurator
|
||||
*/
|
||||
public abstract class AbstractConfigurator implements Configurator {
|
||||
|
||||
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) {
|
||||
return url;
|
||||
}
|
||||
/*
|
||||
* This if branch is created since 2.7.0.
|
||||
*/
|
||||
String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
|
||||
if (StringUtils.isNotEmpty(apiVersion)) {
|
||||
String currentSide = url.getSide();
|
||||
String configuratorSide = configuratorUrl.getSide();
|
||||
if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) {
|
||||
url = configureIfMatch(NetUtils.getLocalHost(), url);
|
||||
} else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) &&
|
||||
url.getPort() == configuratorUrl.getPort()) {
|
||||
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())) {
|
||||
// TODO, to support wildcards
|
||||
String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
|
||||
if (StringUtils.isEmpty(providers) || providers.contains(url.getAddress()) || providers.contains(ANYHOST_VALUE)) {
|
||||
String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
|
||||
String currentApplication = url.getApplication(url.getUsername());
|
||||
if (configApplication == null || ANY_VALUE.equals(configApplication)
|
||||
|| configApplication.equals(currentApplication)) {
|
||||
Set<String> conditionKeys = new HashSet<String>();
|
||||
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);
|
||||
for (Map.Entry<String, String> 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))) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
|
||||
}
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private boolean startWithTilde(String key) {
|
||||
if (StringUtils.isNotEmpty(key) && key.startsWith(TILDE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract URL doConfigure(URL currentUrl, URL configUrl);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +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.configurator.absent;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.AbstractConfigurator;
|
||||
|
||||
/**
|
||||
* AbsentConfigurator
|
||||
*
|
||||
*/
|
||||
public class AbsentConfigurator extends AbstractConfigurator {
|
||||
|
||||
public AbsentConfigurator(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL doConfigure(URL currentUrl, URL configUrl) {
|
||||
return currentUrl.addParametersIfAbsent(configUrl.getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* AbsentConfigurator
|
||||
*
|
||||
*/
|
||||
public class AbsentConfigurator extends AbstractConfigurator {
|
||||
|
||||
public AbsentConfigurator(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL doConfigure(URL currentUrl, URL configUrl) {
|
||||
return currentUrl.addParametersIfAbsent(configUrl.getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +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.configurator.override;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.AbstractConfigurator;
|
||||
|
||||
/**
|
||||
* OverrideConfigurator
|
||||
*
|
||||
*/
|
||||
public class OverrideConfigurator extends AbstractConfigurator {
|
||||
|
||||
public OverrideConfigurator(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL doConfigure(URL currentUrl, URL configUrl) {
|
||||
return currentUrl.addParameters(configUrl.getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.AbstractConfigurator;
|
||||
|
||||
/**
|
||||
* OverrideConfigurator
|
||||
*
|
||||
*/
|
||||
public class OverrideConfigurator extends AbstractConfigurator {
|
||||
|
||||
public OverrideConfigurator(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL doConfigure(URL currentUrl, URL configUrl) {
|
||||
return currentUrl.addParameters(configUrl.getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,114 @@
|
|||
/*
|
||||
* 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.directory;
|
||||
|
||||
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.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.RouterChain;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* StaticDirectory
|
||||
*/
|
||||
public class StaticDirectory<T> extends AbstractDirectory<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StaticDirectory.class);
|
||||
|
||||
private final List<Invoker<T>> invokers;
|
||||
|
||||
public StaticDirectory(List<Invoker<T>> invokers) {
|
||||
this(null, invokers, null);
|
||||
}
|
||||
|
||||
public StaticDirectory(List<Invoker<T>> invokers, RouterChain<T> routerChain) {
|
||||
this(null, invokers, routerChain);
|
||||
}
|
||||
|
||||
public StaticDirectory(URL url, List<Invoker<T>> invokers) {
|
||||
this(url, invokers, null);
|
||||
}
|
||||
|
||||
public StaticDirectory(URL url, List<Invoker<T>> invokers, RouterChain<T> routerChain) {
|
||||
super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain, false);
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
throw new IllegalArgumentException("invokers == null");
|
||||
}
|
||||
this.invokers = invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getInterface() {
|
||||
return invokers.get(0).getInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Invoker<T>> getAllInvokers() {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (isDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
if (invoker.isAvailable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
super.destroy();
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
invoker.destroy();
|
||||
}
|
||||
invokers.clear();
|
||||
}
|
||||
|
||||
public void buildRouterChain() {
|
||||
RouterChain<T> routerChain = RouterChain.buildChain(getUrl());
|
||||
routerChain.setInvokers(invokers);
|
||||
routerChain.loop(true);
|
||||
this.setRouterChain(routerChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
|
||||
List<Invoker<T>> finalInvokers = invokers;
|
||||
if (routerChain != null) {
|
||||
try {
|
||||
finalInvokers = routerChain.route(getConsumerUrl(), invocation);
|
||||
} catch (Throwable t) {
|
||||
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
return finalInvokers == null ? Collections.emptyList() : finalInvokers;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.directory;
|
||||
|
||||
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.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.RouterChain;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* StaticDirectory
|
||||
*/
|
||||
public class StaticDirectory<T> extends AbstractDirectory<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StaticDirectory.class);
|
||||
|
||||
private final List<Invoker<T>> invokers;
|
||||
|
||||
public StaticDirectory(List<Invoker<T>> invokers) {
|
||||
this(null, invokers, null);
|
||||
}
|
||||
|
||||
public StaticDirectory(List<Invoker<T>> invokers, RouterChain<T> routerChain) {
|
||||
this(null, invokers, routerChain);
|
||||
}
|
||||
|
||||
public StaticDirectory(URL url, List<Invoker<T>> invokers) {
|
||||
this(url, invokers, null);
|
||||
}
|
||||
|
||||
public StaticDirectory(URL url, List<Invoker<T>> invokers, RouterChain<T> routerChain) {
|
||||
super(url == null && CollectionUtils.isNotEmpty(invokers) ? invokers.get(0).getUrl() : url, routerChain, false);
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
throw new IllegalArgumentException("invokers == null");
|
||||
}
|
||||
this.invokers = invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getInterface() {
|
||||
return invokers.get(0).getInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Invoker<T>> getAllInvokers() {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (isDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
if (invoker.isAvailable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
super.destroy();
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
invoker.destroy();
|
||||
}
|
||||
invokers.clear();
|
||||
}
|
||||
|
||||
public void buildRouterChain() {
|
||||
RouterChain<T> routerChain = RouterChain.buildChain(getUrl());
|
||||
routerChain.setInvokers(invokers);
|
||||
routerChain.loop(true);
|
||||
this.setRouterChain(routerChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
|
||||
List<Invoker<T>> finalInvokers = invokers;
|
||||
if (routerChain != null) {
|
||||
try {
|
||||
finalInvokers = routerChain.route(getConsumerUrl(), invocation);
|
||||
} catch (Throwable t) {
|
||||
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
return finalInvokers == null ? Collections.emptyList() : finalInvokers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,99 +1,99 @@
|
|||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
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.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
|
||||
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
|
||||
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_SERVICE_REFERENCE_PATH;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WARMUP;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WEIGHT;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
|
||||
|
||||
/**
|
||||
* AbstractLoadBalance
|
||||
*/
|
||||
public abstract class AbstractLoadBalance implements LoadBalance {
|
||||
/**
|
||||
* Calculate the weight according to the uptime proportion of warmup time
|
||||
* the new weight will be within 1(inclusive) to weight(inclusive)
|
||||
*
|
||||
* @param uptime the uptime in milliseconds
|
||||
* @param warmup the warmup time in milliseconds
|
||||
* @param weight the weight of an invoker
|
||||
* @return weight which takes warmup into account
|
||||
*/
|
||||
static int calculateWarmupWeight(int uptime, int warmup, int weight) {
|
||||
int ww = (int) ( uptime / ((float) warmup / weight));
|
||||
return ww < 1 ? 1 : (Math.min(ww, weight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
return null;
|
||||
}
|
||||
if (invokers.size() == 1) {
|
||||
return invokers.get(0);
|
||||
}
|
||||
return doSelect(invokers, url, invocation);
|
||||
}
|
||||
|
||||
protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
|
||||
|
||||
|
||||
/**
|
||||
* Get the weight of the invoker's invocation which takes warmup time into account
|
||||
* if the uptime is within the warmup time, the weight will be reduce proportionally
|
||||
*
|
||||
* @param invoker the invoker
|
||||
* @param invocation the invocation of this invoker
|
||||
* @return weight
|
||||
*/
|
||||
int getWeight(Invoker<?> invoker, Invocation invocation) {
|
||||
int weight;
|
||||
URL url = invoker.getUrl();
|
||||
// Multiple registry scenario, load balance among multiple registries.
|
||||
if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) {
|
||||
weight = url.getParameter(REGISTRY_KEY + "." + WEIGHT_KEY, DEFAULT_WEIGHT);
|
||||
} else {
|
||||
weight = url.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);
|
||||
if (weight > 0) {
|
||||
long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);
|
||||
if (timestamp > 0L) {
|
||||
long uptime = System.currentTimeMillis() - timestamp;
|
||||
if (uptime < 0) {
|
||||
return 1;
|
||||
}
|
||||
int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
|
||||
if (uptime > 0 && uptime < warmup) {
|
||||
weight = calculateWarmupWeight((int)uptime, warmup, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.max(weight, 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
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.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
|
||||
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
|
||||
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_SERVICE_REFERENCE_PATH;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WARMUP;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WEIGHT;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
|
||||
|
||||
/**
|
||||
* AbstractLoadBalance
|
||||
*/
|
||||
public abstract class AbstractLoadBalance implements LoadBalance {
|
||||
/**
|
||||
* Calculate the weight according to the uptime proportion of warmup time
|
||||
* the new weight will be within 1(inclusive) to weight(inclusive)
|
||||
*
|
||||
* @param uptime the uptime in milliseconds
|
||||
* @param warmup the warmup time in milliseconds
|
||||
* @param weight the weight of an invoker
|
||||
* @return weight which takes warmup into account
|
||||
*/
|
||||
static int calculateWarmupWeight(int uptime, int warmup, int weight) {
|
||||
int ww = (int) ( uptime / ((float) warmup / weight));
|
||||
return ww < 1 ? 1 : (Math.min(ww, weight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
return null;
|
||||
}
|
||||
if (invokers.size() == 1) {
|
||||
return invokers.get(0);
|
||||
}
|
||||
return doSelect(invokers, url, invocation);
|
||||
}
|
||||
|
||||
protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
|
||||
|
||||
|
||||
/**
|
||||
* Get the weight of the invoker's invocation which takes warmup time into account
|
||||
* if the uptime is within the warmup time, the weight will be reduce proportionally
|
||||
*
|
||||
* @param invoker the invoker
|
||||
* @param invocation the invocation of this invoker
|
||||
* @return weight
|
||||
*/
|
||||
int getWeight(Invoker<?> invoker, Invocation invocation) {
|
||||
int weight;
|
||||
URL url = invoker.getUrl();
|
||||
// Multiple registry scenario, load balance among multiple registries.
|
||||
if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) {
|
||||
weight = url.getParameter(REGISTRY_KEY + "." + WEIGHT_KEY, DEFAULT_WEIGHT);
|
||||
} else {
|
||||
weight = url.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);
|
||||
if (weight > 0) {
|
||||
long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);
|
||||
if (timestamp > 0L) {
|
||||
long uptime = System.currentTimeMillis() - timestamp;
|
||||
if (uptime < 0) {
|
||||
return 1;
|
||||
}
|
||||
int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
|
||||
if (uptime > 0 && uptime < warmup) {
|
||||
weight = calculateWarmupWeight((int)uptime, warmup, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.max(weight, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +1,131 @@
|
|||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.io.Bytes;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
|
||||
|
||||
/**
|
||||
* ConsistentHashLoadBalance
|
||||
*/
|
||||
public class ConsistentHashLoadBalance extends AbstractLoadBalance {
|
||||
public static final String NAME = "consistenthash";
|
||||
|
||||
/**
|
||||
* Hash nodes name
|
||||
*/
|
||||
public static final String HASH_NODES = "hash.nodes";
|
||||
|
||||
/**
|
||||
* Hash arguments name
|
||||
*/
|
||||
public static final String HASH_ARGUMENTS = "hash.arguments";
|
||||
|
||||
private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
String methodName = RpcUtils.getMethodName(invocation);
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
|
||||
// using the hashcode of list to compute the hash only pay attention to the elements in the list
|
||||
int invokersHashCode = invokers.hashCode();
|
||||
ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
|
||||
if (selector == null || selector.identityHashCode != invokersHashCode) {
|
||||
selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, invokersHashCode));
|
||||
selector = (ConsistentHashSelector<T>) selectors.get(key);
|
||||
}
|
||||
return selector.select(invocation);
|
||||
}
|
||||
|
||||
private static final class ConsistentHashSelector<T> {
|
||||
|
||||
private final TreeMap<Long, Invoker<T>> virtualInvokers;
|
||||
|
||||
private final int replicaNumber;
|
||||
|
||||
private final int identityHashCode;
|
||||
|
||||
private final int[] argumentIndex;
|
||||
|
||||
ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
|
||||
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
|
||||
this.identityHashCode = identityHashCode;
|
||||
URL url = invokers.get(0).getUrl();
|
||||
this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
|
||||
String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
|
||||
argumentIndex = new int[index.length];
|
||||
for (int i = 0; i < index.length; i++) {
|
||||
argumentIndex[i] = Integer.parseInt(index[i]);
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
String address = invoker.getUrl().getAddress();
|
||||
for (int i = 0; i < replicaNumber / 4; i++) {
|
||||
byte[] digest = Bytes.getMD5(address + i);
|
||||
for (int h = 0; h < 4; h++) {
|
||||
long m = hash(digest, h);
|
||||
virtualInvokers.put(m, invoker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Invoker<T> select(Invocation invocation) {
|
||||
String key = toKey(invocation.getArguments());
|
||||
byte[] digest = Bytes.getMD5(key);
|
||||
return selectForKey(hash(digest, 0));
|
||||
}
|
||||
|
||||
private String toKey(Object[] args) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i : argumentIndex) {
|
||||
if (i >= 0 && i < args.length) {
|
||||
buf.append(args[i]);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Invoker<T> selectForKey(long hash) {
|
||||
Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
|
||||
if (entry == null) {
|
||||
entry = virtualInvokers.firstEntry();
|
||||
}
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
private long hash(byte[] digest, int number) {
|
||||
return (((long) (digest[3 + number * 4] & 0xFF) << 24)
|
||||
| ((long) (digest[2 + number * 4] & 0xFF) << 16)
|
||||
| ((long) (digest[1 + number * 4] & 0xFF) << 8)
|
||||
| (digest[number * 4] & 0xFF))
|
||||
& 0xFFFFFFFFL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.io.Bytes;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
|
||||
|
||||
/**
|
||||
* ConsistentHashLoadBalance
|
||||
*/
|
||||
public class ConsistentHashLoadBalance extends AbstractLoadBalance {
|
||||
public static final String NAME = "consistenthash";
|
||||
|
||||
/**
|
||||
* Hash nodes name
|
||||
*/
|
||||
public static final String HASH_NODES = "hash.nodes";
|
||||
|
||||
/**
|
||||
* Hash arguments name
|
||||
*/
|
||||
public static final String HASH_ARGUMENTS = "hash.arguments";
|
||||
|
||||
private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
String methodName = RpcUtils.getMethodName(invocation);
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
|
||||
// using the hashcode of list to compute the hash only pay attention to the elements in the list
|
||||
int invokersHashCode = invokers.hashCode();
|
||||
ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
|
||||
if (selector == null || selector.identityHashCode != invokersHashCode) {
|
||||
selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, invokersHashCode));
|
||||
selector = (ConsistentHashSelector<T>) selectors.get(key);
|
||||
}
|
||||
return selector.select(invocation);
|
||||
}
|
||||
|
||||
private static final class ConsistentHashSelector<T> {
|
||||
|
||||
private final TreeMap<Long, Invoker<T>> virtualInvokers;
|
||||
|
||||
private final int replicaNumber;
|
||||
|
||||
private final int identityHashCode;
|
||||
|
||||
private final int[] argumentIndex;
|
||||
|
||||
ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
|
||||
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
|
||||
this.identityHashCode = identityHashCode;
|
||||
URL url = invokers.get(0).getUrl();
|
||||
this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
|
||||
String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
|
||||
argumentIndex = new int[index.length];
|
||||
for (int i = 0; i < index.length; i++) {
|
||||
argumentIndex[i] = Integer.parseInt(index[i]);
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
String address = invoker.getUrl().getAddress();
|
||||
for (int i = 0; i < replicaNumber / 4; i++) {
|
||||
byte[] digest = Bytes.getMD5(address + i);
|
||||
for (int h = 0; h < 4; h++) {
|
||||
long m = hash(digest, h);
|
||||
virtualInvokers.put(m, invoker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Invoker<T> select(Invocation invocation) {
|
||||
String key = toKey(invocation.getArguments());
|
||||
byte[] digest = Bytes.getMD5(key);
|
||||
return selectForKey(hash(digest, 0));
|
||||
}
|
||||
|
||||
private String toKey(Object[] args) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i : argumentIndex) {
|
||||
if (i >= 0 && i < args.length) {
|
||||
buf.append(args[i]);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Invoker<T> selectForKey(long hash) {
|
||||
Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
|
||||
if (entry == null) {
|
||||
entry = virtualInvokers.firstEntry();
|
||||
}
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
private long hash(byte[] digest, int number) {
|
||||
return (((long) (digest[3 + number * 4] & 0xFF) << 24)
|
||||
| ((long) (digest[2 + number * 4] & 0xFF) << 16)
|
||||
| ((long) (digest[1 + number * 4] & 0xFF) << 8)
|
||||
| (digest[number * 4] & 0xFF))
|
||||
& 0xFFFFFFFFL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,115 +1,115 @@
|
|||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* LeastActiveLoadBalance
|
||||
* <p>
|
||||
* Filter the number of invokers with the least number of active calls and count the weights and quantities of these invokers.
|
||||
* If there is only one invoker, use the invoker directly;
|
||||
* if there are multiple invokers and the weights are not the same, then random according to the total weight;
|
||||
* if there are multiple invokers and the same weight, then randomly called.
|
||||
*/
|
||||
public class LeastActiveLoadBalance extends AbstractLoadBalance {
|
||||
|
||||
public static final String NAME = "leastactive";
|
||||
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
// Number of invokers
|
||||
int length = invokers.size();
|
||||
// The least active value of all invokers
|
||||
int leastActive = -1;
|
||||
// The number of invokers having the same least active value (leastActive)
|
||||
int leastCount = 0;
|
||||
// The index of invokers having the same least active value (leastActive)
|
||||
int[] leastIndexes = new int[length];
|
||||
// the weight of every invokers
|
||||
int[] weights = new int[length];
|
||||
// The sum of the warmup weights of all the least active invokers
|
||||
int totalWeight = 0;
|
||||
// The weight of the first least active invoker
|
||||
int firstWeight = 0;
|
||||
// Every least active invoker has the same weight value?
|
||||
boolean sameWeight = true;
|
||||
|
||||
|
||||
// Filter out all the least active invokers
|
||||
for (int i = 0; i < length; i++) {
|
||||
Invoker<T> invoker = invokers.get(i);
|
||||
// Get the active number of the invoker
|
||||
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
|
||||
// Get the weight of the invoker's configuration. The default value is 100.
|
||||
int afterWarmup = getWeight(invoker, invocation);
|
||||
// save for later use
|
||||
weights[i] = afterWarmup;
|
||||
// If it is the first invoker or the active number of the invoker is less than the current least active number
|
||||
if (leastActive == -1 || active < leastActive) {
|
||||
// Reset the active number of the current invoker to the least active number
|
||||
leastActive = active;
|
||||
// Reset the number of least active invokers
|
||||
leastCount = 1;
|
||||
// Put the first least active invoker first in leastIndexes
|
||||
leastIndexes[0] = i;
|
||||
// Reset totalWeight
|
||||
totalWeight = afterWarmup;
|
||||
// Record the weight the first least active invoker
|
||||
firstWeight = afterWarmup;
|
||||
// Each invoke has the same weight (only one invoker here)
|
||||
sameWeight = true;
|
||||
// If current invoker's active value equals with leaseActive, then accumulating.
|
||||
} else if (active == leastActive) {
|
||||
// Record the index of the least active invoker in leastIndexes order
|
||||
leastIndexes[leastCount++] = i;
|
||||
// Accumulate the total weight of the least active invoker
|
||||
totalWeight += afterWarmup;
|
||||
// If every invoker has the same weight?
|
||||
if (sameWeight && afterWarmup != firstWeight) {
|
||||
sameWeight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Choose an invoker from all the least active invokers
|
||||
if (leastCount == 1) {
|
||||
// If we got exactly one invoker having the least active value, return this invoker directly.
|
||||
return invokers.get(leastIndexes[0]);
|
||||
}
|
||||
if (!sameWeight && totalWeight > 0) {
|
||||
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on
|
||||
// totalWeight.
|
||||
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
|
||||
// Return a invoker based on the random value.
|
||||
for (int i = 0; i < leastCount; i++) {
|
||||
int leastIndex = leastIndexes[i];
|
||||
offsetWeight -= weights[leastIndex];
|
||||
if (offsetWeight < 0) {
|
||||
return invokers.get(leastIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If all invokers have the same weight value or totalWeight=0, return evenly.
|
||||
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* LeastActiveLoadBalance
|
||||
* <p>
|
||||
* Filter the number of invokers with the least number of active calls and count the weights and quantities of these invokers.
|
||||
* If there is only one invoker, use the invoker directly;
|
||||
* if there are multiple invokers and the weights are not the same, then random according to the total weight;
|
||||
* if there are multiple invokers and the same weight, then randomly called.
|
||||
*/
|
||||
public class LeastActiveLoadBalance extends AbstractLoadBalance {
|
||||
|
||||
public static final String NAME = "leastactive";
|
||||
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
// Number of invokers
|
||||
int length = invokers.size();
|
||||
// The least active value of all invokers
|
||||
int leastActive = -1;
|
||||
// The number of invokers having the same least active value (leastActive)
|
||||
int leastCount = 0;
|
||||
// The index of invokers having the same least active value (leastActive)
|
||||
int[] leastIndexes = new int[length];
|
||||
// the weight of every invokers
|
||||
int[] weights = new int[length];
|
||||
// The sum of the warmup weights of all the least active invokers
|
||||
int totalWeight = 0;
|
||||
// The weight of the first least active invoker
|
||||
int firstWeight = 0;
|
||||
// Every least active invoker has the same weight value?
|
||||
boolean sameWeight = true;
|
||||
|
||||
|
||||
// Filter out all the least active invokers
|
||||
for (int i = 0; i < length; i++) {
|
||||
Invoker<T> invoker = invokers.get(i);
|
||||
// Get the active number of the invoker
|
||||
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
|
||||
// Get the weight of the invoker's configuration. The default value is 100.
|
||||
int afterWarmup = getWeight(invoker, invocation);
|
||||
// save for later use
|
||||
weights[i] = afterWarmup;
|
||||
// If it is the first invoker or the active number of the invoker is less than the current least active number
|
||||
if (leastActive == -1 || active < leastActive) {
|
||||
// Reset the active number of the current invoker to the least active number
|
||||
leastActive = active;
|
||||
// Reset the number of least active invokers
|
||||
leastCount = 1;
|
||||
// Put the first least active invoker first in leastIndexes
|
||||
leastIndexes[0] = i;
|
||||
// Reset totalWeight
|
||||
totalWeight = afterWarmup;
|
||||
// Record the weight the first least active invoker
|
||||
firstWeight = afterWarmup;
|
||||
// Each invoke has the same weight (only one invoker here)
|
||||
sameWeight = true;
|
||||
// If current invoker's active value equals with leaseActive, then accumulating.
|
||||
} else if (active == leastActive) {
|
||||
// Record the index of the least active invoker in leastIndexes order
|
||||
leastIndexes[leastCount++] = i;
|
||||
// Accumulate the total weight of the least active invoker
|
||||
totalWeight += afterWarmup;
|
||||
// If every invoker has the same weight?
|
||||
if (sameWeight && afterWarmup != firstWeight) {
|
||||
sameWeight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Choose an invoker from all the least active invokers
|
||||
if (leastCount == 1) {
|
||||
// If we got exactly one invoker having the least active value, return this invoker directly.
|
||||
return invokers.get(leastIndexes[0]);
|
||||
}
|
||||
if (!sameWeight && totalWeight > 0) {
|
||||
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on
|
||||
// totalWeight.
|
||||
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
|
||||
// Return a invoker based on the random value.
|
||||
for (int i = 0; i < leastCount; i++) {
|
||||
int leastIndex = leastIndexes[i];
|
||||
offsetWeight -= weights[leastIndex];
|
||||
if (offsetWeight < 0) {
|
||||
return invokers.get(leastIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If all invokers have the same weight value or totalWeight=0, return evenly.
|
||||
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +1,131 @@
|
|||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Round robin load balance.
|
||||
*/
|
||||
public class RoundRobinLoadBalance extends AbstractLoadBalance {
|
||||
public static final String NAME = "roundrobin";
|
||||
|
||||
private static final int RECYCLE_PERIOD = 60000;
|
||||
|
||||
protected static class WeightedRoundRobin {
|
||||
private int weight;
|
||||
private AtomicLong current = new AtomicLong(0);
|
||||
private long lastUpdate;
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(int weight) {
|
||||
this.weight = weight;
|
||||
current.set(0);
|
||||
}
|
||||
|
||||
public long increaseCurrent() {
|
||||
return current.addAndGet(weight);
|
||||
}
|
||||
|
||||
public void sel(int total) {
|
||||
current.addAndGet(-1 * total);
|
||||
}
|
||||
|
||||
public long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
public void setLastUpdate(long lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
|
||||
|
||||
/**
|
||||
* get invoker addr list cached for specified invocation
|
||||
* <p>
|
||||
* <b>for unit test only</b>
|
||||
*
|
||||
* @param invokers
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
|
||||
Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
|
||||
if (map != null) {
|
||||
return map.keySet();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
|
||||
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
|
||||
int totalWeight = 0;
|
||||
long maxCurrent = Long.MIN_VALUE;
|
||||
long now = System.currentTimeMillis();
|
||||
Invoker<T> selectedInvoker = null;
|
||||
WeightedRoundRobin selectedWRR = null;
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
String identifyString = invoker.getUrl().toIdentityString();
|
||||
int weight = getWeight(invoker, invocation);
|
||||
WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
|
||||
WeightedRoundRobin wrr = new WeightedRoundRobin();
|
||||
wrr.setWeight(weight);
|
||||
return wrr;
|
||||
});
|
||||
|
||||
if (weight != weightedRoundRobin.getWeight()) {
|
||||
//weight changed
|
||||
weightedRoundRobin.setWeight(weight);
|
||||
}
|
||||
long cur = weightedRoundRobin.increaseCurrent();
|
||||
weightedRoundRobin.setLastUpdate(now);
|
||||
if (cur > maxCurrent) {
|
||||
maxCurrent = cur;
|
||||
selectedInvoker = invoker;
|
||||
selectedWRR = weightedRoundRobin;
|
||||
}
|
||||
totalWeight += weight;
|
||||
}
|
||||
if (invokers.size() != map.size()) {
|
||||
map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
|
||||
}
|
||||
if (selectedInvoker != null) {
|
||||
selectedWRR.sel(totalWeight);
|
||||
return selectedInvoker;
|
||||
}
|
||||
// should not happen here
|
||||
return invokers.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.loadbalance;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Round robin load balance.
|
||||
*/
|
||||
public class RoundRobinLoadBalance extends AbstractLoadBalance {
|
||||
public static final String NAME = "roundrobin";
|
||||
|
||||
private static final int RECYCLE_PERIOD = 60000;
|
||||
|
||||
protected static class WeightedRoundRobin {
|
||||
private int weight;
|
||||
private AtomicLong current = new AtomicLong(0);
|
||||
private long lastUpdate;
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(int weight) {
|
||||
this.weight = weight;
|
||||
current.set(0);
|
||||
}
|
||||
|
||||
public long increaseCurrent() {
|
||||
return current.addAndGet(weight);
|
||||
}
|
||||
|
||||
public void sel(int total) {
|
||||
current.addAndGet(-1 * total);
|
||||
}
|
||||
|
||||
public long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
public void setLastUpdate(long lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
|
||||
|
||||
/**
|
||||
* get invoker addr list cached for specified invocation
|
||||
* <p>
|
||||
* <b>for unit test only</b>
|
||||
*
|
||||
* @param invokers
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
|
||||
Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
|
||||
if (map != null) {
|
||||
return map.keySet();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
|
||||
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
|
||||
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
|
||||
int totalWeight = 0;
|
||||
long maxCurrent = Long.MIN_VALUE;
|
||||
long now = System.currentTimeMillis();
|
||||
Invoker<T> selectedInvoker = null;
|
||||
WeightedRoundRobin selectedWRR = null;
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
String identifyString = invoker.getUrl().toIdentityString();
|
||||
int weight = getWeight(invoker, invocation);
|
||||
WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
|
||||
WeightedRoundRobin wrr = new WeightedRoundRobin();
|
||||
wrr.setWeight(weight);
|
||||
return wrr;
|
||||
});
|
||||
|
||||
if (weight != weightedRoundRobin.getWeight()) {
|
||||
//weight changed
|
||||
weightedRoundRobin.setWeight(weight);
|
||||
}
|
||||
long cur = weightedRoundRobin.increaseCurrent();
|
||||
weightedRoundRobin.setLastUpdate(now);
|
||||
if (cur > maxCurrent) {
|
||||
maxCurrent = cur;
|
||||
selectedInvoker = invoker;
|
||||
selectedWRR = weightedRoundRobin;
|
||||
}
|
||||
totalWeight += weight;
|
||||
}
|
||||
if (invokers.size() != map.size()) {
|
||||
map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
|
||||
}
|
||||
if (selectedInvoker != null) {
|
||||
selectedWRR.sel(totalWeight);
|
||||
return selectedInvoker;
|
||||
}
|
||||
// should not happen here
|
||||
return invokers.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,360 +1,360 @@
|
|||
/*
|
||||
* 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.condition;
|
||||
|
||||
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.NetUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.common.utils.UrlUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Constants;
|
||||
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.ADDRESS_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY;
|
||||
|
||||
/**
|
||||
* ConditionRouter
|
||||
* It supports the conditional routing configured by "override://", in 2.6.x,
|
||||
* refer to https://dubbo.apache.org/en/docs/v2.7/user/examples/routing-rule/ .
|
||||
* For 2.7.x and later, please refer to {@link org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouter}
|
||||
* and {@link org.apache.dubbo.rpc.cluster.router.condition.config.AppRouter}
|
||||
* refer to https://dubbo.apache.org/zh/docs/v2.7/user/examples/routing-rule/ .
|
||||
*/
|
||||
public class ConditionRouter extends AbstractRouter {
|
||||
public static final String NAME = "condition";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class);
|
||||
protected static final Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
|
||||
protected static Pattern ARGUMENTS_PATTERN = Pattern.compile("arguments\\[([0-9]+)\\]");
|
||||
protected Map<String, MatchPair> whenCondition;
|
||||
protected Map<String, MatchPair> thenCondition;
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
public ConditionRouter(String rule, boolean force, boolean enabled) {
|
||||
this.force = force;
|
||||
this.enabled = enabled;
|
||||
if (enabled) {
|
||||
this.init(rule);
|
||||
}
|
||||
}
|
||||
|
||||
public ConditionRouter(URL url) {
|
||||
this.url = url;
|
||||
this.priority = url.getParameter(PRIORITY_KEY, 0);
|
||||
this.force = url.getParameter(FORCE_KEY, false);
|
||||
this.enabled = url.getParameter(ENABLED_KEY, true);
|
||||
if (enabled) {
|
||||
init(url.getParameterAndDecoded(RULE_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
public void init(String rule) {
|
||||
try {
|
||||
if (rule == null || rule.trim().length() == 0) {
|
||||
throw new IllegalArgumentException("Illegal route rule!");
|
||||
}
|
||||
rule = rule.replace("consumer.", "").replace("provider.", "");
|
||||
int i = rule.indexOf("=>");
|
||||
String whenRule = i < 0 ? null : rule.substring(0, i).trim();
|
||||
String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
|
||||
Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
|
||||
Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
|
||||
// NOTE: It should be determined on the business level whether the `When condition` can be empty or not.
|
||||
this.whenCondition = when;
|
||||
this.thenCondition = then;
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, MatchPair> parseRule(String rule)
|
||||
throws ParseException {
|
||||
Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
|
||||
if (StringUtils.isBlank(rule)) {
|
||||
return condition;
|
||||
}
|
||||
// Key-Value pair, stores both match and mismatch conditions
|
||||
MatchPair pair = null;
|
||||
// Multiple values
|
||||
Set<String> values = null;
|
||||
final Matcher matcher = ROUTE_PATTERN.matcher(rule);
|
||||
while (matcher.find()) { // Try to match one by one
|
||||
String separator = matcher.group(1);
|
||||
String content = matcher.group(2);
|
||||
// Start part of the condition expression.
|
||||
if (StringUtils.isEmpty(separator)) {
|
||||
pair = new MatchPair();
|
||||
condition.put(content, pair);
|
||||
}
|
||||
// The KV part of the condition expression
|
||||
else if ("&".equals(separator)) {
|
||||
if (condition.get(content) == null) {
|
||||
pair = new MatchPair();
|
||||
condition.put(content, pair);
|
||||
} else {
|
||||
pair = condition.get(content);
|
||||
}
|
||||
}
|
||||
// The Value in the KV part.
|
||||
else if ("=".equals(separator)) {
|
||||
if (pair == null) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
|
||||
values = pair.matches;
|
||||
values.add(content);
|
||||
}
|
||||
// The Value in the KV part.
|
||||
else if ("!=".equals(separator)) {
|
||||
if (pair == null) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
|
||||
values = pair.mismatches;
|
||||
values.add(content);
|
||||
}
|
||||
// The Value in the KV part, if Value have more than one items.
|
||||
else if (",".equals(separator)) { // Should be separated by ','
|
||||
if (values == null || values.isEmpty()) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
values.add(content);
|
||||
} else {
|
||||
throw new ParseException("Illegal route rule \"" + rule
|
||||
+ "\", The error char '" + separator + "' at index "
|
||||
+ matcher.start() + " before \"" + content + "\".", matcher.start());
|
||||
}
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
|
||||
throws RpcException {
|
||||
if (!enabled) {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
return invokers;
|
||||
}
|
||||
try {
|
||||
if (!matchWhen(url, invocation)) {
|
||||
return invokers;
|
||||
}
|
||||
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
|
||||
if (thenCondition == null) {
|
||||
logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
|
||||
return result;
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
if (matchThen(invoker.getUrl(), url)) {
|
||||
result.add(invoker);
|
||||
}
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
return result;
|
||||
} else if (force) {
|
||||
logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
|
||||
return result;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRuntime() {
|
||||
// We always return true for previously defined Router, that is, old Router doesn't support cache anymore.
|
||||
// return true;
|
||||
return this.url.getParameter(RUNTIME_KEY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
boolean matchWhen(URL url, Invocation invocation) {
|
||||
return CollectionUtils.isEmptyMap(whenCondition) || matchCondition(whenCondition, url, null, invocation);
|
||||
}
|
||||
|
||||
private boolean matchThen(URL url, URL param) {
|
||||
return CollectionUtils.isNotEmptyMap(thenCondition) && matchCondition(thenCondition, url, param, null);
|
||||
}
|
||||
|
||||
private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
|
||||
Map<String, String> sample = url.toMap();
|
||||
boolean result = false;
|
||||
for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
|
||||
String key = matchPair.getKey();
|
||||
|
||||
if (key.startsWith(Constants.ARGUMENTS)) {
|
||||
if (!matchArguments(matchPair, invocation)) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String sampleValue;
|
||||
//get real invoked method name from invocation
|
||||
if (invocation != null && (METHOD_KEY.equals(key) || METHODS_KEY.equals(key))) {
|
||||
sampleValue = invocation.getMethodName();
|
||||
} else if (ADDRESS_KEY.equals(key)) {
|
||||
sampleValue = url.getAddress();
|
||||
} else if (HOST_KEY.equals(key)) {
|
||||
sampleValue = url.getHost();
|
||||
} else {
|
||||
sampleValue = sample.get(key);
|
||||
if (sampleValue == null) {
|
||||
sampleValue = sample.get(key);
|
||||
}
|
||||
}
|
||||
if (sampleValue != null) {
|
||||
if (!matchPair.getValue().isMatch(sampleValue, param)) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
//not pass the condition
|
||||
if (!matchPair.getValue().matches.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* analysis the arguments in the rule.
|
||||
* Examples would be like this:
|
||||
* "arguments[0]=1", whenCondition is that the first argument is equal to '1'.
|
||||
* "arguments[1]=a", whenCondition is that the second argument is equal to 'a'.
|
||||
* @param matchPair
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
public boolean matchArguments(Map.Entry<String, MatchPair> matchPair, Invocation invocation) {
|
||||
try {
|
||||
// split the rule
|
||||
String key = matchPair.getKey();
|
||||
String[] expressArray = key.split("\\.");
|
||||
String argumentExpress = expressArray[0];
|
||||
final Matcher matcher = ARGUMENTS_PATTERN.matcher(argumentExpress);
|
||||
if (!matcher.find()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//extract the argument index
|
||||
int index = Integer.parseInt(matcher.group(1));
|
||||
if (index < 0 || index > invocation.getArguments().length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//extract the argument value
|
||||
Object object = invocation.getArguments()[index];
|
||||
|
||||
if (matchPair.getValue().isMatch(String.valueOf(object), null)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Arguments match failed, matchPair[]" + matchPair + "] invocation[" + invocation + "]", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static final class MatchPair {
|
||||
final Set<String> matches = new HashSet<String>();
|
||||
final Set<String> mismatches = new HashSet<String>();
|
||||
|
||||
private boolean isMatch(String value, URL param) {
|
||||
if (!matches.isEmpty() && mismatches.isEmpty()) {
|
||||
for (String match : matches) {
|
||||
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mismatches.isEmpty() && matches.isEmpty()) {
|
||||
for (String mismatch : mismatches) {
|
||||
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!matches.isEmpty() && !mismatches.isEmpty()) {
|
||||
//when both mismatches and matches contain the same value, then using mismatches first
|
||||
for (String mismatch : mismatches) {
|
||||
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (String match : matches) {
|
||||
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.condition;
|
||||
|
||||
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.NetUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.common.utils.UrlUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Constants;
|
||||
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.ADDRESS_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY;
|
||||
|
||||
/**
|
||||
* ConditionRouter
|
||||
* It supports the conditional routing configured by "override://", in 2.6.x,
|
||||
* refer to https://dubbo.apache.org/en/docs/v2.7/user/examples/routing-rule/ .
|
||||
* For 2.7.x and later, please refer to {@link org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouter}
|
||||
* and {@link org.apache.dubbo.rpc.cluster.router.condition.config.AppRouter}
|
||||
* refer to https://dubbo.apache.org/zh/docs/v2.7/user/examples/routing-rule/ .
|
||||
*/
|
||||
public class ConditionRouter extends AbstractRouter {
|
||||
public static final String NAME = "condition";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class);
|
||||
protected static final Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
|
||||
protected static Pattern ARGUMENTS_PATTERN = Pattern.compile("arguments\\[([0-9]+)\\]");
|
||||
protected Map<String, MatchPair> whenCondition;
|
||||
protected Map<String, MatchPair> thenCondition;
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
public ConditionRouter(String rule, boolean force, boolean enabled) {
|
||||
this.force = force;
|
||||
this.enabled = enabled;
|
||||
if (enabled) {
|
||||
this.init(rule);
|
||||
}
|
||||
}
|
||||
|
||||
public ConditionRouter(URL url) {
|
||||
this.url = url;
|
||||
this.priority = url.getParameter(PRIORITY_KEY, 0);
|
||||
this.force = url.getParameter(FORCE_KEY, false);
|
||||
this.enabled = url.getParameter(ENABLED_KEY, true);
|
||||
if (enabled) {
|
||||
init(url.getParameterAndDecoded(RULE_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
public void init(String rule) {
|
||||
try {
|
||||
if (rule == null || rule.trim().length() == 0) {
|
||||
throw new IllegalArgumentException("Illegal route rule!");
|
||||
}
|
||||
rule = rule.replace("consumer.", "").replace("provider.", "");
|
||||
int i = rule.indexOf("=>");
|
||||
String whenRule = i < 0 ? null : rule.substring(0, i).trim();
|
||||
String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
|
||||
Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
|
||||
Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
|
||||
// NOTE: It should be determined on the business level whether the `When condition` can be empty or not.
|
||||
this.whenCondition = when;
|
||||
this.thenCondition = then;
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, MatchPair> parseRule(String rule)
|
||||
throws ParseException {
|
||||
Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
|
||||
if (StringUtils.isBlank(rule)) {
|
||||
return condition;
|
||||
}
|
||||
// Key-Value pair, stores both match and mismatch conditions
|
||||
MatchPair pair = null;
|
||||
// Multiple values
|
||||
Set<String> values = null;
|
||||
final Matcher matcher = ROUTE_PATTERN.matcher(rule);
|
||||
while (matcher.find()) { // Try to match one by one
|
||||
String separator = matcher.group(1);
|
||||
String content = matcher.group(2);
|
||||
// Start part of the condition expression.
|
||||
if (StringUtils.isEmpty(separator)) {
|
||||
pair = new MatchPair();
|
||||
condition.put(content, pair);
|
||||
}
|
||||
// The KV part of the condition expression
|
||||
else if ("&".equals(separator)) {
|
||||
if (condition.get(content) == null) {
|
||||
pair = new MatchPair();
|
||||
condition.put(content, pair);
|
||||
} else {
|
||||
pair = condition.get(content);
|
||||
}
|
||||
}
|
||||
// The Value in the KV part.
|
||||
else if ("=".equals(separator)) {
|
||||
if (pair == null) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
|
||||
values = pair.matches;
|
||||
values.add(content);
|
||||
}
|
||||
// The Value in the KV part.
|
||||
else if ("!=".equals(separator)) {
|
||||
if (pair == null) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
|
||||
values = pair.mismatches;
|
||||
values.add(content);
|
||||
}
|
||||
// The Value in the KV part, if Value have more than one items.
|
||||
else if (",".equals(separator)) { // Should be separated by ','
|
||||
if (values == null || values.isEmpty()) {
|
||||
throw new ParseException("Illegal route rule \""
|
||||
+ rule + "\", The error char '" + separator
|
||||
+ "' at index " + matcher.start() + " before \""
|
||||
+ content + "\".", matcher.start());
|
||||
}
|
||||
values.add(content);
|
||||
} else {
|
||||
throw new ParseException("Illegal route rule \"" + rule
|
||||
+ "\", The error char '" + separator + "' at index "
|
||||
+ matcher.start() + " before \"" + content + "\".", matcher.start());
|
||||
}
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
|
||||
throws RpcException {
|
||||
if (!enabled) {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
return invokers;
|
||||
}
|
||||
try {
|
||||
if (!matchWhen(url, invocation)) {
|
||||
return invokers;
|
||||
}
|
||||
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
|
||||
if (thenCondition == null) {
|
||||
logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
|
||||
return result;
|
||||
}
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
if (matchThen(invoker.getUrl(), url)) {
|
||||
result.add(invoker);
|
||||
}
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
return result;
|
||||
} else if (force) {
|
||||
logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
|
||||
return result;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRuntime() {
|
||||
// We always return true for previously defined Router, that is, old Router doesn't support cache anymore.
|
||||
// return true;
|
||||
return this.url.getParameter(RUNTIME_KEY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
boolean matchWhen(URL url, Invocation invocation) {
|
||||
return CollectionUtils.isEmptyMap(whenCondition) || matchCondition(whenCondition, url, null, invocation);
|
||||
}
|
||||
|
||||
private boolean matchThen(URL url, URL param) {
|
||||
return CollectionUtils.isNotEmptyMap(thenCondition) && matchCondition(thenCondition, url, param, null);
|
||||
}
|
||||
|
||||
private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
|
||||
Map<String, String> sample = url.toMap();
|
||||
boolean result = false;
|
||||
for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
|
||||
String key = matchPair.getKey();
|
||||
|
||||
if (key.startsWith(Constants.ARGUMENTS)) {
|
||||
if (!matchArguments(matchPair, invocation)) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String sampleValue;
|
||||
//get real invoked method name from invocation
|
||||
if (invocation != null && (METHOD_KEY.equals(key) || METHODS_KEY.equals(key))) {
|
||||
sampleValue = invocation.getMethodName();
|
||||
} else if (ADDRESS_KEY.equals(key)) {
|
||||
sampleValue = url.getAddress();
|
||||
} else if (HOST_KEY.equals(key)) {
|
||||
sampleValue = url.getHost();
|
||||
} else {
|
||||
sampleValue = sample.get(key);
|
||||
if (sampleValue == null) {
|
||||
sampleValue = sample.get(key);
|
||||
}
|
||||
}
|
||||
if (sampleValue != null) {
|
||||
if (!matchPair.getValue().isMatch(sampleValue, param)) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
//not pass the condition
|
||||
if (!matchPair.getValue().matches.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* analysis the arguments in the rule.
|
||||
* Examples would be like this:
|
||||
* "arguments[0]=1", whenCondition is that the first argument is equal to '1'.
|
||||
* "arguments[1]=a", whenCondition is that the second argument is equal to 'a'.
|
||||
* @param matchPair
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
public boolean matchArguments(Map.Entry<String, MatchPair> matchPair, Invocation invocation) {
|
||||
try {
|
||||
// split the rule
|
||||
String key = matchPair.getKey();
|
||||
String[] expressArray = key.split("\\.");
|
||||
String argumentExpress = expressArray[0];
|
||||
final Matcher matcher = ARGUMENTS_PATTERN.matcher(argumentExpress);
|
||||
if (!matcher.find()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//extract the argument index
|
||||
int index = Integer.parseInt(matcher.group(1));
|
||||
if (index < 0 || index > invocation.getArguments().length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//extract the argument value
|
||||
Object object = invocation.getArguments()[index];
|
||||
|
||||
if (matchPair.getValue().isMatch(String.valueOf(object), null)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Arguments match failed, matchPair[]" + matchPair + "] invocation[" + invocation + "]", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static final class MatchPair {
|
||||
final Set<String> matches = new HashSet<String>();
|
||||
final Set<String> mismatches = new HashSet<String>();
|
||||
|
||||
private boolean isMatch(String value, URL param) {
|
||||
if (!matches.isEmpty() && mismatches.isEmpty()) {
|
||||
for (String match : matches) {
|
||||
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mismatches.isEmpty() && matches.isEmpty()) {
|
||||
for (String mismatch : mismatches) {
|
||||
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!matches.isEmpty() && !mismatches.isEmpty()) {
|
||||
//when both mismatches and matches contain the same value, then using mismatches first
|
||||
for (String mismatch : mismatches) {
|
||||
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (String match : matches) {
|
||||
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.condition;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.RouterFactory;
|
||||
|
||||
/**
|
||||
* ConditionRouterFactory
|
||||
* Load when "override://" is configured {@link ConditionRouter}
|
||||
*/
|
||||
public class ConditionRouterFactory implements RouterFactory {
|
||||
|
||||
public static final String NAME = "condition";
|
||||
|
||||
@Override
|
||||
public Router getRouter(URL url) {
|
||||
return new ConditionRouter(url);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.condition;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.RouterFactory;
|
||||
|
||||
/**
|
||||
* ConditionRouterFactory
|
||||
* Load when "override://" is configured {@link ConditionRouter}
|
||||
*/
|
||||
public class ConditionRouterFactory implements RouterFactory {
|
||||
|
||||
public static final String NAME = "condition";
|
||||
|
||||
@Override
|
||||
public Router getRouter(URL url) {
|
||||
return new ConditionRouter(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,177 +1,177 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
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.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_SCRIPT_TYPE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY;
|
||||
|
||||
/**
|
||||
* ScriptRouter
|
||||
*/
|
||||
public class ScriptRouter extends AbstractRouter {
|
||||
public static final String NAME = "SCRIPT_ROUTER";
|
||||
private static final int SCRIPT_ROUTER_DEFAULT_PRIORITY = 0;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
|
||||
|
||||
private static final Map<String, ScriptEngine> ENGINES = new ConcurrentHashMap<>();
|
||||
|
||||
private final ScriptEngine engine;
|
||||
|
||||
private final String rule;
|
||||
|
||||
private CompiledScript function;
|
||||
|
||||
private AccessControlContext accessControlContext;
|
||||
|
||||
{
|
||||
//Just give permission of reflect to access member.
|
||||
Permissions perms = new Permissions();
|
||||
perms.add(new RuntimePermission("accessDeclaredMembers"));
|
||||
// Cast to Certificate[] required because of ambiguity:
|
||||
ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms);
|
||||
accessControlContext = new AccessControlContext(new ProtectionDomain[]{domain});
|
||||
}
|
||||
|
||||
public ScriptRouter(URL url) {
|
||||
this.url = url;
|
||||
this.priority = url.getParameter(PRIORITY_KEY, SCRIPT_ROUTER_DEFAULT_PRIORITY);
|
||||
|
||||
engine = getEngine(url);
|
||||
rule = getRule(url);
|
||||
try {
|
||||
Compilable compilable = (Compilable) engine;
|
||||
function = compilable.compile(rule);
|
||||
} catch (ScriptException e) {
|
||||
logger.error("route error, rule has been ignored. rule: " + rule +
|
||||
", url: " + RpcContext.getServiceContext().getUrl(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get rule from url parameters.
|
||||
*/
|
||||
private String getRule(URL url) {
|
||||
String vRule = url.getParameterAndDecoded(RULE_KEY);
|
||||
if (StringUtils.isEmpty(vRule)) {
|
||||
throw new IllegalStateException("route rule can not be empty.");
|
||||
}
|
||||
return vRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* create ScriptEngine instance by type from url parameters, then cache it
|
||||
*/
|
||||
private ScriptEngine getEngine(URL url) {
|
||||
String type = url.getParameter(TYPE_KEY, DEFAULT_SCRIPT_TYPE_KEY);
|
||||
|
||||
return ENGINES.computeIfAbsent(type, t -> {
|
||||
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(type);
|
||||
if (scriptEngine == null) {
|
||||
throw new IllegalStateException("unsupported route engine type: " + type);
|
||||
}
|
||||
return scriptEngine;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
|
||||
if (engine == null || function == null) {
|
||||
return invokers;
|
||||
}
|
||||
Bindings bindings = createBindings(invokers, invocation);
|
||||
return getRoutedInvokers(AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
try {
|
||||
return function.eval(bindings);
|
||||
} catch (ScriptException e) {
|
||||
logger.error("route error, rule has been ignored. rule: " + rule + ", method:" +
|
||||
invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
|
||||
return invokers;
|
||||
}
|
||||
}, accessControlContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* get routed invokers from result of script rule evaluation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> List<Invoker<T>> getRoutedInvokers(Object obj) {
|
||||
if (obj instanceof Invoker[]) {
|
||||
return Arrays.asList((Invoker<T>[]) obj);
|
||||
} else if (obj instanceof Object[]) {
|
||||
return Arrays.stream((Object[]) obj).map(item -> (Invoker<T>) item).collect(Collectors.toList());
|
||||
} else {
|
||||
return (List<Invoker<T>>) obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create bindings for script engine
|
||||
*/
|
||||
private <T> Bindings createBindings(List<Invoker<T>> invokers, Invocation invocation) {
|
||||
Bindings bindings = engine.createBindings();
|
||||
// create a new List of invokers
|
||||
bindings.put("invokers", new ArrayList<>(invokers));
|
||||
bindings.put("invocation", invocation);
|
||||
bindings.put("context", RpcContext.getClientAttachment());
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRuntime() {
|
||||
return this.url.getParameter(RUNTIME_KEY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForce() {
|
||||
return url.getParameter(FORCE_KEY, false);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.script;
|
||||
|
||||
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.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_SCRIPT_TYPE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.PRIORITY_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RUNTIME_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.TYPE_KEY;
|
||||
|
||||
/**
|
||||
* ScriptRouter
|
||||
*/
|
||||
public class ScriptRouter extends AbstractRouter {
|
||||
public static final String NAME = "SCRIPT_ROUTER";
|
||||
private static final int SCRIPT_ROUTER_DEFAULT_PRIORITY = 0;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
|
||||
|
||||
private static final Map<String, ScriptEngine> ENGINES = new ConcurrentHashMap<>();
|
||||
|
||||
private final ScriptEngine engine;
|
||||
|
||||
private final String rule;
|
||||
|
||||
private CompiledScript function;
|
||||
|
||||
private AccessControlContext accessControlContext;
|
||||
|
||||
{
|
||||
//Just give permission of reflect to access member.
|
||||
Permissions perms = new Permissions();
|
||||
perms.add(new RuntimePermission("accessDeclaredMembers"));
|
||||
// Cast to Certificate[] required because of ambiguity:
|
||||
ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms);
|
||||
accessControlContext = new AccessControlContext(new ProtectionDomain[]{domain});
|
||||
}
|
||||
|
||||
public ScriptRouter(URL url) {
|
||||
this.url = url;
|
||||
this.priority = url.getParameter(PRIORITY_KEY, SCRIPT_ROUTER_DEFAULT_PRIORITY);
|
||||
|
||||
engine = getEngine(url);
|
||||
rule = getRule(url);
|
||||
try {
|
||||
Compilable compilable = (Compilable) engine;
|
||||
function = compilable.compile(rule);
|
||||
} catch (ScriptException e) {
|
||||
logger.error("route error, rule has been ignored. rule: " + rule +
|
||||
", url: " + RpcContext.getServiceContext().getUrl(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get rule from url parameters.
|
||||
*/
|
||||
private String getRule(URL url) {
|
||||
String vRule = url.getParameterAndDecoded(RULE_KEY);
|
||||
if (StringUtils.isEmpty(vRule)) {
|
||||
throw new IllegalStateException("route rule can not be empty.");
|
||||
}
|
||||
return vRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* create ScriptEngine instance by type from url parameters, then cache it
|
||||
*/
|
||||
private ScriptEngine getEngine(URL url) {
|
||||
String type = url.getParameter(TYPE_KEY, DEFAULT_SCRIPT_TYPE_KEY);
|
||||
|
||||
return ENGINES.computeIfAbsent(type, t -> {
|
||||
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(type);
|
||||
if (scriptEngine == null) {
|
||||
throw new IllegalStateException("unsupported route engine type: " + type);
|
||||
}
|
||||
return scriptEngine;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
|
||||
if (engine == null || function == null) {
|
||||
return invokers;
|
||||
}
|
||||
Bindings bindings = createBindings(invokers, invocation);
|
||||
return getRoutedInvokers(AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
try {
|
||||
return function.eval(bindings);
|
||||
} catch (ScriptException e) {
|
||||
logger.error("route error, rule has been ignored. rule: " + rule + ", method:" +
|
||||
invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
|
||||
return invokers;
|
||||
}
|
||||
}, accessControlContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* get routed invokers from result of script rule evaluation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> List<Invoker<T>> getRoutedInvokers(Object obj) {
|
||||
if (obj instanceof Invoker[]) {
|
||||
return Arrays.asList((Invoker<T>[]) obj);
|
||||
} else if (obj instanceof Object[]) {
|
||||
return Arrays.stream((Object[]) obj).map(item -> (Invoker<T>) item).collect(Collectors.toList());
|
||||
} else {
|
||||
return (List<Invoker<T>>) obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create bindings for script engine
|
||||
*/
|
||||
private <T> Bindings createBindings(List<Invoker<T>> invokers, Invocation invocation) {
|
||||
Bindings bindings = engine.createBindings();
|
||||
// create a new List of invokers
|
||||
bindings.put("invokers", new ArrayList<>(invokers));
|
||||
bindings.put("invocation", invocation);
|
||||
bindings.put("context", RpcContext.getClientAttachment());
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRuntime() {
|
||||
return this.url.getParameter(RUNTIME_KEY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForce() {
|
||||
return url.getParameter(FORCE_KEY, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.RouterFactory;
|
||||
|
||||
/**
|
||||
* ScriptRouterFactory
|
||||
* <p>
|
||||
* Example URLS used by Script Router Factory:
|
||||
* <ol>
|
||||
* <li> script://registryAddress?type=js&rule=xxxx
|
||||
* <li> script:///path/to/routerfile.js?type=js&rule=xxxx
|
||||
* <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx
|
||||
* <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx
|
||||
* </ol>
|
||||
* The host value in URL points out the address of the source content of the Script Router,Registry、File etc
|
||||
*
|
||||
*/
|
||||
public class ScriptRouterFactory implements RouterFactory {
|
||||
|
||||
public static final String NAME = "script";
|
||||
|
||||
@Override
|
||||
public Router getRouter(URL url) {
|
||||
return new ScriptRouter(url);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.script;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.RouterFactory;
|
||||
|
||||
/**
|
||||
* ScriptRouterFactory
|
||||
* <p>
|
||||
* Example URLS used by Script Router Factory:
|
||||
* <ol>
|
||||
* <li> script://registryAddress?type=js&rule=xxxx
|
||||
* <li> script:///path/to/routerfile.js?type=js&rule=xxxx
|
||||
* <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx
|
||||
* <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx
|
||||
* </ol>
|
||||
* The host value in URL points out the address of the source content of the Script Router,Registry、File etc
|
||||
*
|
||||
*/
|
||||
public class ScriptRouterFactory implements RouterFactory {
|
||||
|
||||
public static final String NAME = "script";
|
||||
|
||||
@Override
|
||||
public Router getRouter(URL url) {
|
||||
return new ScriptRouter(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,4 +46,4 @@ public class RouterCache<T> {
|
|||
public void setAddrMetadata(Object addrMetadata) {
|
||||
this.addrMetadata = addrMetadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +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)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see Directory#list(Invocation)
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface StateRouter extends Comparable<StateRouter> {
|
||||
|
||||
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 <T>
|
||||
* @return routed invokers
|
||||
* @throws RpcException
|
||||
* @Since 3.0
|
||||
*/
|
||||
<T> BitList<Invoker<T>> route(BitList<Invoker<T>> invokers, RouterCache<T> cache, URL url, Invocation invocation)
|
||||
throws
|
||||
RpcException;
|
||||
|
||||
default <T> void notify(List<Invoker<T>> 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();
|
||||
|
||||
<T> RouterCache<T> pool(List<Invoker<T>> invokers);
|
||||
|
||||
void pool();
|
||||
|
||||
default void stop() {
|
||||
//do nothing by default
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* State Router. (SPI, Prototype, ThreadSafe)
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
|
||||
*
|
||||
* @see org.apache.dubbo.rpc.cluster.Cluster#join(Directory)
|
||||
* @see Directory#list(Invocation)
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface StateRouter extends Comparable<StateRouter> {
|
||||
|
||||
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 <T>
|
||||
* @return routed invokers
|
||||
* @throws RpcException
|
||||
* @Since 3.0
|
||||
*/
|
||||
<T> BitList<Invoker<T>> route(BitList<Invoker<T>> invokers, RouterCache<T> cache, URL url, Invocation invocation)
|
||||
throws
|
||||
RpcException;
|
||||
|
||||
default <T> void notify(List<Invoker<T>> 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();
|
||||
|
||||
<T> RouterCache<T> pool(List<Invoker<T>> invokers);
|
||||
|
||||
void pool();
|
||||
|
||||
default void stop() {
|
||||
//do nothing by default
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Cluster;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
/**
|
||||
* AvailableCluster
|
||||
*
|
||||
*/
|
||||
public class AvailableCluster implements Cluster {
|
||||
|
||||
public static final String NAME = "available";
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
|
||||
return new AvailableClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Cluster;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
/**
|
||||
* AvailableCluster
|
||||
*
|
||||
*/
|
||||
public class AvailableCluster implements Cluster {
|
||||
|
||||
public static final String NAME = "available";
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
|
||||
return new AvailableClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* BroadcastCluster
|
||||
*
|
||||
*/
|
||||
public class BroadcastCluster extends AbstractCluster {
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new BroadcastClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* BroadcastCluster
|
||||
*
|
||||
*/
|
||||
public class BroadcastCluster extends AbstractCluster {
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new BroadcastClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,116 +1,116 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
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.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BroadcastClusterInvoker
|
||||
*/
|
||||
public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);
|
||||
private static final String BROADCAST_FAIL_PERCENT_KEY = "broadcast.fail.percent";
|
||||
private static final int MAX_BROADCAST_FAIL_PERCENT = 100;
|
||||
private static final int MIN_BROADCAST_FAIL_PERCENT = 0;
|
||||
|
||||
public BroadcastClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
checkInvokers(invokers, invocation);
|
||||
RpcContext.getServiceContext().setInvokers((List) invokers);
|
||||
RpcException exception = null;
|
||||
Result result = null;
|
||||
URL url = getUrl();
|
||||
// The value range of broadcast.fail.threshold must be 0~100.
|
||||
// 100 means that an exception will be thrown last, and 0 means that as long as an exception occurs, it will be thrown.
|
||||
// see https://github.com/apache/dubbo/pull/7174
|
||||
int broadcastFailPercent = url.getParameter(BROADCAST_FAIL_PERCENT_KEY, MAX_BROADCAST_FAIL_PERCENT);
|
||||
|
||||
if (broadcastFailPercent < MIN_BROADCAST_FAIL_PERCENT || broadcastFailPercent > MAX_BROADCAST_FAIL_PERCENT) {
|
||||
logger.info(String.format("The value corresponding to the broadcast.fail.percent parameter must be between 0 and 100. " +
|
||||
"The current setting is %s, which is reset to 100.", broadcastFailPercent));
|
||||
broadcastFailPercent = MAX_BROADCAST_FAIL_PERCENT;
|
||||
}
|
||||
|
||||
int failThresholdIndex = invokers.size() * broadcastFailPercent / MAX_BROADCAST_FAIL_PERCENT;
|
||||
int failIndex = 0;
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
try {
|
||||
result = invokeWithContext(invoker, invocation);
|
||||
if (null != result && result.hasException()) {
|
||||
Throwable resultException = result.getException();
|
||||
if (null != resultException) {
|
||||
exception = getRpcException(result.getException());
|
||||
logger.warn(exception.getMessage(), exception);
|
||||
if (failIndex == failThresholdIndex) {
|
||||
break;
|
||||
} else {
|
||||
failIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
exception = getRpcException(e);
|
||||
logger.warn(exception.getMessage(), exception);
|
||||
if (failIndex == failThresholdIndex) {
|
||||
break;
|
||||
} else {
|
||||
failIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
if (failIndex == failThresholdIndex) {
|
||||
logger.debug(
|
||||
String.format("The number of BroadcastCluster call failures has reached the threshold %s", failThresholdIndex));
|
||||
} else {
|
||||
logger.debug(String.format("The number of BroadcastCluster call failures has not reached the threshold %s, fail size is %s",
|
||||
failIndex));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private RpcException getRpcException(Throwable throwable) {
|
||||
RpcException rpcException = null;
|
||||
if (throwable instanceof RpcException) {
|
||||
rpcException = (RpcException) throwable;
|
||||
} else {
|
||||
rpcException = new RpcException(throwable.getMessage(), throwable);
|
||||
}
|
||||
return rpcException;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
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.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BroadcastClusterInvoker
|
||||
*/
|
||||
public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);
|
||||
private static final String BROADCAST_FAIL_PERCENT_KEY = "broadcast.fail.percent";
|
||||
private static final int MAX_BROADCAST_FAIL_PERCENT = 100;
|
||||
private static final int MIN_BROADCAST_FAIL_PERCENT = 0;
|
||||
|
||||
public BroadcastClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
checkInvokers(invokers, invocation);
|
||||
RpcContext.getServiceContext().setInvokers((List) invokers);
|
||||
RpcException exception = null;
|
||||
Result result = null;
|
||||
URL url = getUrl();
|
||||
// The value range of broadcast.fail.threshold must be 0~100.
|
||||
// 100 means that an exception will be thrown last, and 0 means that as long as an exception occurs, it will be thrown.
|
||||
// see https://github.com/apache/dubbo/pull/7174
|
||||
int broadcastFailPercent = url.getParameter(BROADCAST_FAIL_PERCENT_KEY, MAX_BROADCAST_FAIL_PERCENT);
|
||||
|
||||
if (broadcastFailPercent < MIN_BROADCAST_FAIL_PERCENT || broadcastFailPercent > MAX_BROADCAST_FAIL_PERCENT) {
|
||||
logger.info(String.format("The value corresponding to the broadcast.fail.percent parameter must be between 0 and 100. " +
|
||||
"The current setting is %s, which is reset to 100.", broadcastFailPercent));
|
||||
broadcastFailPercent = MAX_BROADCAST_FAIL_PERCENT;
|
||||
}
|
||||
|
||||
int failThresholdIndex = invokers.size() * broadcastFailPercent / MAX_BROADCAST_FAIL_PERCENT;
|
||||
int failIndex = 0;
|
||||
for (Invoker<T> invoker : invokers) {
|
||||
try {
|
||||
result = invokeWithContext(invoker, invocation);
|
||||
if (null != result && result.hasException()) {
|
||||
Throwable resultException = result.getException();
|
||||
if (null != resultException) {
|
||||
exception = getRpcException(result.getException());
|
||||
logger.warn(exception.getMessage(), exception);
|
||||
if (failIndex == failThresholdIndex) {
|
||||
break;
|
||||
} else {
|
||||
failIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
exception = getRpcException(e);
|
||||
logger.warn(exception.getMessage(), exception);
|
||||
if (failIndex == failThresholdIndex) {
|
||||
break;
|
||||
} else {
|
||||
failIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
if (failIndex == failThresholdIndex) {
|
||||
logger.debug(
|
||||
String.format("The number of BroadcastCluster call failures has reached the threshold %s", failThresholdIndex));
|
||||
} else {
|
||||
logger.debug(String.format("The number of BroadcastCluster call failures has not reached the threshold %s, fail size is %s",
|
||||
failIndex));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private RpcException getRpcException(Throwable throwable) {
|
||||
RpcException rpcException = null;
|
||||
if (throwable instanceof RpcException) {
|
||||
rpcException = (RpcException) throwable;
|
||||
} else {
|
||||
rpcException = new RpcException(throwable.getMessage(), throwable);
|
||||
}
|
||||
return rpcException;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY;
|
||||
|
||||
|
||||
/**
|
||||
* ClusterUtils
|
||||
*/
|
||||
public class ClusterUtils {
|
||||
|
||||
private ClusterUtils() {
|
||||
}
|
||||
|
||||
public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
|
||||
|
||||
String ump = localMap.get(URL_MERGE_PROCESSOR_KEY);
|
||||
ProviderURLMergeProcessor providerURLMergeProcessor;
|
||||
|
||||
if (StringUtils.isNotEmpty(ump)) {
|
||||
providerURLMergeProcessor = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class).getExtension(ump);
|
||||
} else {
|
||||
providerURLMergeProcessor = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class).getExtension("default");
|
||||
}
|
||||
|
||||
return providerURLMergeProcessor.mergeUrl(remoteUrl, localMap);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY;
|
||||
|
||||
|
||||
/**
|
||||
* ClusterUtils
|
||||
*/
|
||||
public class ClusterUtils {
|
||||
|
||||
private ClusterUtils() {
|
||||
}
|
||||
|
||||
public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
|
||||
|
||||
String ump = localMap.get(URL_MERGE_PROCESSOR_KEY);
|
||||
ProviderURLMergeProcessor providerURLMergeProcessor;
|
||||
|
||||
if (StringUtils.isNotEmpty(ump)) {
|
||||
providerURLMergeProcessor = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class).getExtension(ump);
|
||||
} else {
|
||||
providerURLMergeProcessor = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class).getExtension("default");
|
||||
}
|
||||
|
||||
return providerURLMergeProcessor.mergeUrl(remoteUrl, localMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailbackClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailbackCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failback";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailbackClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailbackClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailbackCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failback";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailbackClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,168 +1,168 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.timer.HashedWheelTimer;
|
||||
import org.apache.dubbo.common.timer.Timeout;
|
||||
import org.apache.dubbo.common.timer.Timer;
|
||||
import org.apache.dubbo.common.timer.TimerTask;
|
||||
import org.apache.dubbo.common.utils.NamedThreadFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_FAILBACK_TIMES;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_FAILBACK_TASKS;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FAIL_BACK_TASKS_KEY;
|
||||
|
||||
/**
|
||||
* When fails, record failure requests and schedule for retry on a regular interval.
|
||||
* Especially useful for services of notification.
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Failback">Failback</a>
|
||||
*/
|
||||
public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class);
|
||||
|
||||
private static final long RETRY_FAILED_PERIOD = 5;
|
||||
|
||||
private final int retries;
|
||||
|
||||
private final int failbackTasks;
|
||||
|
||||
private volatile Timer failTimer;
|
||||
|
||||
public FailbackClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
|
||||
int retriesConfig = getUrl().getParameter(RETRIES_KEY, DEFAULT_FAILBACK_TIMES);
|
||||
if (retriesConfig <= 0) {
|
||||
retriesConfig = DEFAULT_FAILBACK_TIMES;
|
||||
}
|
||||
int failbackTasksConfig = getUrl().getParameter(FAIL_BACK_TASKS_KEY, DEFAULT_FAILBACK_TASKS);
|
||||
if (failbackTasksConfig <= 0) {
|
||||
failbackTasksConfig = DEFAULT_FAILBACK_TASKS;
|
||||
}
|
||||
retries = retriesConfig;
|
||||
failbackTasks = failbackTasksConfig;
|
||||
}
|
||||
|
||||
private void addFailed(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, Invoker<T> lastInvoker) {
|
||||
if (failTimer == null) {
|
||||
synchronized (this) {
|
||||
if (failTimer == null) {
|
||||
failTimer = new HashedWheelTimer(
|
||||
new NamedThreadFactory("failback-cluster-timer", true),
|
||||
1,
|
||||
TimeUnit.SECONDS, 32, failbackTasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
RetryTimerTask retryTimerTask = new RetryTimerTask(loadbalance, invocation, invokers, lastInvoker, retries, RETRY_FAILED_PERIOD);
|
||||
try {
|
||||
failTimer.newTimeout(retryTimerTask, RETRY_FAILED_PERIOD, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failback background works error,invocation->" + invocation + ", exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
Invoker<T> invoker = null;
|
||||
try {
|
||||
checkInvokers(invokers, invocation);
|
||||
invoker = select(loadbalance, invocation, invokers, null);
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
|
||||
+ e.getMessage() + ", ", e);
|
||||
addFailed(loadbalance, invocation, invokers, invoker);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if (failTimer != null) {
|
||||
failTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RetryTimerTask
|
||||
*/
|
||||
private class RetryTimerTask implements TimerTask {
|
||||
private final Invocation invocation;
|
||||
private final LoadBalance loadbalance;
|
||||
private final List<Invoker<T>> invokers;
|
||||
private final int retries;
|
||||
private final long tick;
|
||||
private Invoker<T> lastInvoker;
|
||||
private int retryTimes = 0;
|
||||
|
||||
RetryTimerTask(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, Invoker<T> lastInvoker, int retries, long tick) {
|
||||
this.loadbalance = loadbalance;
|
||||
this.invocation = invocation;
|
||||
this.invokers = invokers;
|
||||
this.retries = retries;
|
||||
this.tick = tick;
|
||||
this.lastInvoker=lastInvoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
Invoker<T> retryInvoker = select(loadbalance, invocation, invokers, Collections.singletonList(lastInvoker));
|
||||
lastInvoker = retryInvoker;
|
||||
invokeWithContext(retryInvoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.", e);
|
||||
if ((++retryTimes) >= retries) {
|
||||
logger.error("Failed retry times exceed threshold (" + retries + "), We have to abandon, invocation->" + invocation);
|
||||
} else {
|
||||
rePut(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rePut(Timeout timeout) {
|
||||
if (timeout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Timer timer = timeout.timer();
|
||||
if (timer.isStop() || timeout.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer.newTimeout(timeout.task(), tick, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.timer.HashedWheelTimer;
|
||||
import org.apache.dubbo.common.timer.Timeout;
|
||||
import org.apache.dubbo.common.timer.Timer;
|
||||
import org.apache.dubbo.common.timer.TimerTask;
|
||||
import org.apache.dubbo.common.utils.NamedThreadFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_FAILBACK_TIMES;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_FAILBACK_TASKS;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FAIL_BACK_TASKS_KEY;
|
||||
|
||||
/**
|
||||
* When fails, record failure requests and schedule for retry on a regular interval.
|
||||
* Especially useful for services of notification.
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Failback">Failback</a>
|
||||
*/
|
||||
public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class);
|
||||
|
||||
private static final long RETRY_FAILED_PERIOD = 5;
|
||||
|
||||
private final int retries;
|
||||
|
||||
private final int failbackTasks;
|
||||
|
||||
private volatile Timer failTimer;
|
||||
|
||||
public FailbackClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
|
||||
int retriesConfig = getUrl().getParameter(RETRIES_KEY, DEFAULT_FAILBACK_TIMES);
|
||||
if (retriesConfig <= 0) {
|
||||
retriesConfig = DEFAULT_FAILBACK_TIMES;
|
||||
}
|
||||
int failbackTasksConfig = getUrl().getParameter(FAIL_BACK_TASKS_KEY, DEFAULT_FAILBACK_TASKS);
|
||||
if (failbackTasksConfig <= 0) {
|
||||
failbackTasksConfig = DEFAULT_FAILBACK_TASKS;
|
||||
}
|
||||
retries = retriesConfig;
|
||||
failbackTasks = failbackTasksConfig;
|
||||
}
|
||||
|
||||
private void addFailed(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, Invoker<T> lastInvoker) {
|
||||
if (failTimer == null) {
|
||||
synchronized (this) {
|
||||
if (failTimer == null) {
|
||||
failTimer = new HashedWheelTimer(
|
||||
new NamedThreadFactory("failback-cluster-timer", true),
|
||||
1,
|
||||
TimeUnit.SECONDS, 32, failbackTasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
RetryTimerTask retryTimerTask = new RetryTimerTask(loadbalance, invocation, invokers, lastInvoker, retries, RETRY_FAILED_PERIOD);
|
||||
try {
|
||||
failTimer.newTimeout(retryTimerTask, RETRY_FAILED_PERIOD, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failback background works error,invocation->" + invocation + ", exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
Invoker<T> invoker = null;
|
||||
try {
|
||||
checkInvokers(invokers, invocation);
|
||||
invoker = select(loadbalance, invocation, invokers, null);
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
|
||||
+ e.getMessage() + ", ", e);
|
||||
addFailed(loadbalance, invocation, invokers, invoker);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if (failTimer != null) {
|
||||
failTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RetryTimerTask
|
||||
*/
|
||||
private class RetryTimerTask implements TimerTask {
|
||||
private final Invocation invocation;
|
||||
private final LoadBalance loadbalance;
|
||||
private final List<Invoker<T>> invokers;
|
||||
private final int retries;
|
||||
private final long tick;
|
||||
private Invoker<T> lastInvoker;
|
||||
private int retryTimes = 0;
|
||||
|
||||
RetryTimerTask(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, Invoker<T> lastInvoker, int retries, long tick) {
|
||||
this.loadbalance = loadbalance;
|
||||
this.invocation = invocation;
|
||||
this.invokers = invokers;
|
||||
this.retries = retries;
|
||||
this.tick = tick;
|
||||
this.lastInvoker=lastInvoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
Invoker<T> retryInvoker = select(loadbalance, invocation, invokers, Collections.singletonList(lastInvoker));
|
||||
lastInvoker = retryInvoker;
|
||||
invokeWithContext(retryInvoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.", e);
|
||||
if ((++retryTimes) >= retries) {
|
||||
logger.error("Failed retry times exceed threshold (" + retries + "), We have to abandon, invocation->" + invocation);
|
||||
} else {
|
||||
rePut(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rePut(Timeout timeout) {
|
||||
if (timeout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Timer timer = timeout.timer();
|
||||
if (timer.isStop() || timeout.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer.newTimeout(timeout.task(), tick, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailfastClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailfastCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failfast";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailfastClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailfastClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailfastCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failfast";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailfastClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,62 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.Version;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Execute exactly once, which means this policy will throw an exception immediately in case of an invocation error.
|
||||
* Usually used for non-idempotent write operations
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
|
||||
*
|
||||
*/
|
||||
public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
public FailfastClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
checkInvokers(invokers, invocation);
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
try {
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
|
||||
throw (RpcException) e;
|
||||
}
|
||||
throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0,
|
||||
"Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName()
|
||||
+ " select from all providers " + invokers + " for service " + getInterface().getName()
|
||||
+ " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost()
|
||||
+ " use dubbo version " + Version.getVersion()
|
||||
+ ", but no luck to perform the invocation. Last error is: " + e.getMessage(),
|
||||
e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.Version;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Execute exactly once, which means this policy will throw an exception immediately in case of an invocation error.
|
||||
* Usually used for non-idempotent write operations
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
|
||||
*
|
||||
*/
|
||||
public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
public FailfastClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
checkInvokers(invokers, invocation);
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
try {
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
|
||||
throw (RpcException) e;
|
||||
}
|
||||
throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0,
|
||||
"Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName()
|
||||
+ " select from all providers " + invokers + " for service " + getInterface().getName()
|
||||
+ " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost()
|
||||
+ " use dubbo version " + Version.getVersion()
|
||||
+ ", but no luck to perform the invocation. Last error is: " + e.getMessage(),
|
||||
e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailoverClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailoverCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failover";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailoverClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailoverClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailoverCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failover";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailoverClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,128 +1,128 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.Version;
|
||||
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.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
|
||||
|
||||
/**
|
||||
* When invoke fails, log the initial error and retry other invokers (retry n times, which means at most n different invokers will be invoked)
|
||||
* Note that retry causes latency.
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Failover">Failover</a>
|
||||
*
|
||||
*/
|
||||
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);
|
||||
|
||||
public FailoverClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
List<Invoker<T>> copyInvokers = invokers;
|
||||
checkInvokers(copyInvokers, invocation);
|
||||
String methodName = RpcUtils.getMethodName(invocation);
|
||||
int len = calculateInvokeTimes(methodName);
|
||||
// retry loop.
|
||||
RpcException le = null; // last exception.
|
||||
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
|
||||
Set<String> providers = new HashSet<String>(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
//Reselect before retry to avoid a change of candidate `invokers`.
|
||||
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
|
||||
if (i > 0) {
|
||||
checkWhetherDestroyed();
|
||||
copyInvokers = list(invocation);
|
||||
// check again
|
||||
checkInvokers(copyInvokers, invocation);
|
||||
}
|
||||
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
|
||||
invoked.add(invoker);
|
||||
RpcContext.getServiceContext().setInvokers((List) invoked);
|
||||
try {
|
||||
Result result = invokeWithContext(invoker, invocation);
|
||||
if (le != null && logger.isWarnEnabled()) {
|
||||
logger.warn("Although retry the method " + methodName
|
||||
+ " in the service " + getInterface().getName()
|
||||
+ " was successful by the provider " + invoker.getUrl().getAddress()
|
||||
+ ", but there have been failed providers " + providers
|
||||
+ " (" + providers.size() + "/" + copyInvokers.size()
|
||||
+ ") from the registry " + directory.getUrl().getAddress()
|
||||
+ " on the consumer " + NetUtils.getLocalHost()
|
||||
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
|
||||
+ le.getMessage(), le);
|
||||
}
|
||||
return result;
|
||||
} catch (RpcException e) {
|
||||
if (e.isBiz()) { // biz exception.
|
||||
throw e;
|
||||
}
|
||||
le = e;
|
||||
} catch (Throwable e) {
|
||||
le = new RpcException(e.getMessage(), e);
|
||||
} finally {
|
||||
providers.add(invoker.getUrl().getAddress());
|
||||
}
|
||||
}
|
||||
throw new RpcException(le.getCode(), "Failed to invoke the method "
|
||||
+ methodName + " in the service " + getInterface().getName()
|
||||
+ ". Tried " + len + " times of the providers " + providers
|
||||
+ " (" + providers.size() + "/" + copyInvokers.size()
|
||||
+ ") from the registry " + directory.getUrl().getAddress()
|
||||
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
|
||||
+ Version.getVersion() + ". Last error is: "
|
||||
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
|
||||
}
|
||||
|
||||
private int calculateInvokeTimes(String methodName) {
|
||||
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
|
||||
RpcContext rpcContext = RpcContext.getClientAttachment();
|
||||
Object retry = rpcContext.getObjectAttachment(RETRIES_KEY);
|
||||
if (retry instanceof Number) {
|
||||
len = ((Number) retry).intValue() + 1;
|
||||
rpcContext.removeAttachment(RETRIES_KEY);
|
||||
}
|
||||
if (len <= 0) {
|
||||
len = 1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.Version;
|
||||
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.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
|
||||
|
||||
/**
|
||||
* When invoke fails, log the initial error and retry other invokers (retry n times, which means at most n different invokers will be invoked)
|
||||
* Note that retry causes latency.
|
||||
* <p>
|
||||
* <a href="http://en.wikipedia.org/wiki/Failover">Failover</a>
|
||||
*
|
||||
*/
|
||||
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);
|
||||
|
||||
public FailoverClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
List<Invoker<T>> copyInvokers = invokers;
|
||||
checkInvokers(copyInvokers, invocation);
|
||||
String methodName = RpcUtils.getMethodName(invocation);
|
||||
int len = calculateInvokeTimes(methodName);
|
||||
// retry loop.
|
||||
RpcException le = null; // last exception.
|
||||
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
|
||||
Set<String> providers = new HashSet<String>(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
//Reselect before retry to avoid a change of candidate `invokers`.
|
||||
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
|
||||
if (i > 0) {
|
||||
checkWhetherDestroyed();
|
||||
copyInvokers = list(invocation);
|
||||
// check again
|
||||
checkInvokers(copyInvokers, invocation);
|
||||
}
|
||||
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
|
||||
invoked.add(invoker);
|
||||
RpcContext.getServiceContext().setInvokers((List) invoked);
|
||||
try {
|
||||
Result result = invokeWithContext(invoker, invocation);
|
||||
if (le != null && logger.isWarnEnabled()) {
|
||||
logger.warn("Although retry the method " + methodName
|
||||
+ " in the service " + getInterface().getName()
|
||||
+ " was successful by the provider " + invoker.getUrl().getAddress()
|
||||
+ ", but there have been failed providers " + providers
|
||||
+ " (" + providers.size() + "/" + copyInvokers.size()
|
||||
+ ") from the registry " + directory.getUrl().getAddress()
|
||||
+ " on the consumer " + NetUtils.getLocalHost()
|
||||
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
|
||||
+ le.getMessage(), le);
|
||||
}
|
||||
return result;
|
||||
} catch (RpcException e) {
|
||||
if (e.isBiz()) { // biz exception.
|
||||
throw e;
|
||||
}
|
||||
le = e;
|
||||
} catch (Throwable e) {
|
||||
le = new RpcException(e.getMessage(), e);
|
||||
} finally {
|
||||
providers.add(invoker.getUrl().getAddress());
|
||||
}
|
||||
}
|
||||
throw new RpcException(le.getCode(), "Failed to invoke the method "
|
||||
+ methodName + " in the service " + getInterface().getName()
|
||||
+ ". Tried " + len + " times of the providers " + providers
|
||||
+ " (" + providers.size() + "/" + copyInvokers.size()
|
||||
+ ") from the registry " + directory.getUrl().getAddress()
|
||||
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
|
||||
+ Version.getVersion() + ". Last error is: "
|
||||
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
|
||||
}
|
||||
|
||||
private int calculateInvokeTimes(String methodName) {
|
||||
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
|
||||
RpcContext rpcContext = RpcContext.getClientAttachment();
|
||||
Object retry = rpcContext.getObjectAttachment(RETRIES_KEY);
|
||||
if (retry instanceof Number) {
|
||||
len = ((Number) retry).intValue() + 1;
|
||||
rpcContext.removeAttachment(RETRIES_KEY);
|
||||
}
|
||||
if (len <= 0) {
|
||||
len = 1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailsafeClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailsafeCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failsafe";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailsafeClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link FailsafeClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class FailsafeCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "failsafe";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new FailsafeClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,56 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* When invoke fails, log the error message and ignore this error by returning an empty Result.
|
||||
* Usually used to write audit logs and other operations
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
|
||||
*
|
||||
*/
|
||||
public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
|
||||
|
||||
public FailsafeClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
try {
|
||||
checkInvokers(invokers, invocation);
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failsafe ignore exception: " + e.getMessage(), e);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.LoadBalance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* When invoke fails, log the error message and ignore this error by returning an empty Result.
|
||||
* Usually used to write audit logs and other operations
|
||||
*
|
||||
* <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
|
||||
*
|
||||
*/
|
||||
public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
|
||||
|
||||
public FailsafeClusterInvoker(Directory<T> directory) {
|
||||
super(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
|
||||
try {
|
||||
checkInvokers(invokers, invocation);
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failsafe ignore exception: " + e.getMessage(), e);
|
||||
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link ForkingClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class ForkingCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "forking";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new ForkingClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
|
||||
|
||||
/**
|
||||
* {@link ForkingClusterInvoker}
|
||||
*
|
||||
*/
|
||||
public class ForkingCluster extends AbstractCluster {
|
||||
|
||||
public final static String NAME = "forking";
|
||||
|
||||
@Override
|
||||
public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
|
||||
return new ForkingClusterInvoker<>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,4 +120,4 @@ public class DefaultProviderURLMergeProcessor implements ProviderURLMergeProcess
|
|||
return remoteUrl.clearParameters().addParameters(map);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,4 +30,4 @@ public class ZoneAwareCluster extends AbstractCluster {
|
|||
return new ZoneAwareClusterInvoker<T>(directory);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,4 +102,4 @@ public class ZoneAwareClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
return invokers.get(0).invoke(invocation);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,195 +1,195 @@
|
|||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
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.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.ClusterInvoker;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.support.MockInvoker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK;
|
||||
|
||||
public class MockClusterInvoker<T> implements ClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
|
||||
|
||||
private final Directory<T> directory;
|
||||
|
||||
private final Invoker<T> invoker;
|
||||
|
||||
public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
|
||||
this.directory = directory;
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return directory.getConsumerUrl();
|
||||
}
|
||||
|
||||
public URL getRegistryUrl() {
|
||||
return directory.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<T> getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return directory.isDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return directory.isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.invoker.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getInterface() {
|
||||
return directory.getInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invocation invocation) throws RpcException {
|
||||
Result result = null;
|
||||
|
||||
String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
|
||||
if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
|
||||
//no mock
|
||||
result = this.invoker.invoke(invocation);
|
||||
} else if (value.startsWith("force")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
|
||||
}
|
||||
//force:direct mock
|
||||
result = doMockInvoke(invocation, null);
|
||||
} else {
|
||||
//fail-mock
|
||||
try {
|
||||
result = this.invoker.invoke(invocation);
|
||||
|
||||
//fix:#4585
|
||||
if(result.getException() != null && result.getException() instanceof RpcException){
|
||||
RpcException rpcException= (RpcException)result.getException();
|
||||
if(rpcException.isBiz()){
|
||||
throw rpcException;
|
||||
}else {
|
||||
result = doMockInvoke(invocation, rpcException);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (RpcException e) {
|
||||
if (e.isBiz()) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(), e);
|
||||
}
|
||||
result = doMockInvoke(invocation, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private Result doMockInvoke(Invocation invocation, RpcException e) {
|
||||
Result result = null;
|
||||
Invoker<T> minvoker;
|
||||
|
||||
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
|
||||
if (CollectionUtils.isEmpty(mockInvokers)) {
|
||||
minvoker = (Invoker<T>) new MockInvoker(getUrl(), directory.getInterface());
|
||||
} else {
|
||||
minvoker = mockInvokers.get(0);
|
||||
}
|
||||
try {
|
||||
result = minvoker.invoke(invocation);
|
||||
} catch (RpcException me) {
|
||||
if (me.isBiz()) {
|
||||
result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation);
|
||||
} else {
|
||||
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
|
||||
}
|
||||
} catch (Throwable me) {
|
||||
throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getMockExceptionMessage(Throwable t, Throwable mt) {
|
||||
String msg = "mock error : " + mt.getMessage();
|
||||
if (t != null) {
|
||||
msg = msg + ", invoke error is :" + StringUtils.toString(t);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return MockInvoker
|
||||
* Contract:
|
||||
* directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return.
|
||||
* if directory.list() returns more than one mock invoker, only one of them will be used.
|
||||
*
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
private List<Invoker<T>> selectMockInvoker(Invocation invocation) {
|
||||
List<Invoker<T>> invokers = null;
|
||||
//TODO generic invoker?
|
||||
if (invocation instanceof RpcInvocation) {
|
||||
//Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachment needs to be improved)
|
||||
((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString());
|
||||
//directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return.
|
||||
try {
|
||||
invokers = directory.list(invocation);
|
||||
} catch (RpcException e) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Exception when try to invoke mock. Get mock invokers error for service:"
|
||||
+ getUrl().getServiceInterface() + ", method:" + invocation.getMethodName()
|
||||
+ ", will construct a new mock with 'new MockInvoker()'.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "invoker :" + this.invoker + ",directory: " + this.directory;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
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.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.ClusterInvoker;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.support.MockInvoker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK;
|
||||
|
||||
public class MockClusterInvoker<T> implements ClusterInvoker<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
|
||||
|
||||
private final Directory<T> directory;
|
||||
|
||||
private final Invoker<T> invoker;
|
||||
|
||||
public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
|
||||
this.directory = directory;
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return directory.getConsumerUrl();
|
||||
}
|
||||
|
||||
public URL getRegistryUrl() {
|
||||
return directory.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directory<T> getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return directory.isDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return directory.isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.invoker.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getInterface() {
|
||||
return directory.getInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invocation invocation) throws RpcException {
|
||||
Result result = null;
|
||||
|
||||
String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
|
||||
if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
|
||||
//no mock
|
||||
result = this.invoker.invoke(invocation);
|
||||
} else if (value.startsWith("force")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
|
||||
}
|
||||
//force:direct mock
|
||||
result = doMockInvoke(invocation, null);
|
||||
} else {
|
||||
//fail-mock
|
||||
try {
|
||||
result = this.invoker.invoke(invocation);
|
||||
|
||||
//fix:#4585
|
||||
if(result.getException() != null && result.getException() instanceof RpcException){
|
||||
RpcException rpcException= (RpcException)result.getException();
|
||||
if(rpcException.isBiz()){
|
||||
throw rpcException;
|
||||
}else {
|
||||
result = doMockInvoke(invocation, rpcException);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (RpcException e) {
|
||||
if (e.isBiz()) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(), e);
|
||||
}
|
||||
result = doMockInvoke(invocation, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private Result doMockInvoke(Invocation invocation, RpcException e) {
|
||||
Result result = null;
|
||||
Invoker<T> minvoker;
|
||||
|
||||
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
|
||||
if (CollectionUtils.isEmpty(mockInvokers)) {
|
||||
minvoker = (Invoker<T>) new MockInvoker(getUrl(), directory.getInterface());
|
||||
} else {
|
||||
minvoker = mockInvokers.get(0);
|
||||
}
|
||||
try {
|
||||
result = minvoker.invoke(invocation);
|
||||
} catch (RpcException me) {
|
||||
if (me.isBiz()) {
|
||||
result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation);
|
||||
} else {
|
||||
throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
|
||||
}
|
||||
} catch (Throwable me) {
|
||||
throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getMockExceptionMessage(Throwable t, Throwable mt) {
|
||||
String msg = "mock error : " + mt.getMessage();
|
||||
if (t != null) {
|
||||
msg = msg + ", invoke error is :" + StringUtils.toString(t);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return MockInvoker
|
||||
* Contract:
|
||||
* directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return.
|
||||
* if directory.list() returns more than one mock invoker, only one of them will be used.
|
||||
*
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
private List<Invoker<T>> selectMockInvoker(Invocation invocation) {
|
||||
List<Invoker<T>> invokers = null;
|
||||
//TODO generic invoker?
|
||||
if (invocation instanceof RpcInvocation) {
|
||||
//Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachment needs to be improved)
|
||||
((RpcInvocation) invocation).setAttachment(INVOCATION_NEED_MOCK, Boolean.TRUE.toString());
|
||||
//directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is absent or not true in invocation, otherwise, a list of mock invokers will return.
|
||||
try {
|
||||
invokers = directory.list(invocation);
|
||||
} catch (RpcException e) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Exception when try to invoke mock. Get mock invokers error for service:"
|
||||
+ getUrl().getServiceInterface() + ", method:" + invocation.getMethodName()
|
||||
+ ", will construct a new mock with 'new MockInvoker()'.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "invoker :" + this.invoker + ",directory: " + this.directory;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Cluster;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
/**
|
||||
* mock impl
|
||||
*
|
||||
*/
|
||||
public class MockClusterWrapper implements Cluster {
|
||||
|
||||
private Cluster cluster;
|
||||
|
||||
public MockClusterWrapper(Cluster cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
|
||||
return new MockClusterInvoker<T>(directory,
|
||||
this.cluster.join(directory));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.Cluster;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
/**
|
||||
* mock impl
|
||||
*
|
||||
*/
|
||||
public class MockClusterWrapper implements Cluster {
|
||||
|
||||
private Cluster cluster;
|
||||
|
||||
public MockClusterWrapper(Cluster cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
|
||||
return new MockClusterInvoker<T>(directory,
|
||||
this.cluster.join(directory));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
|
||||
failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster
|
||||
failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster
|
||||
failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster
|
||||
failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster
|
||||
forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster
|
||||
available=org.apache.dubbo.rpc.cluster.support.AvailableCluster
|
||||
mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster
|
||||
broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster
|
||||
mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
|
||||
failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster
|
||||
failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster
|
||||
failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster
|
||||
failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster
|
||||
forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster
|
||||
available=org.apache.dubbo.rpc.cluster.support.AvailableCluster
|
||||
mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster
|
||||
broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster
|
||||
zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
override=org.apache.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
|
||||
override=org.apache.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
|
||||
absent=org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
random=org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
|
||||
roundrobin=org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
|
||||
leastactive=org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
|
||||
consistenthash=org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
|
||||
random=org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
|
||||
roundrobin=org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
|
||||
leastactive=org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
|
||||
consistenthash=org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
|
||||
shortestresponse=org.apache.dubbo.rpc.cluster.loadbalance.ShortestResponseLoadBalance
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
map=org.apache.dubbo.rpc.cluster.merger.MapMerger
|
||||
set=org.apache.dubbo.rpc.cluster.merger.SetMerger
|
||||
list=org.apache.dubbo.rpc.cluster.merger.ListMerger
|
||||
byte=org.apache.dubbo.rpc.cluster.merger.ByteArrayMerger
|
||||
char=org.apache.dubbo.rpc.cluster.merger.CharArrayMerger
|
||||
short=org.apache.dubbo.rpc.cluster.merger.ShortArrayMerger
|
||||
int=org.apache.dubbo.rpc.cluster.merger.IntArrayMerger
|
||||
long=org.apache.dubbo.rpc.cluster.merger.LongArrayMerger
|
||||
float=org.apache.dubbo.rpc.cluster.merger.FloatArrayMerger
|
||||
double=org.apache.dubbo.rpc.cluster.merger.DoubleArrayMerger
|
||||
boolean=org.apache.dubbo.rpc.cluster.merger.BooleanArrayMerger
|
||||
map=org.apache.dubbo.rpc.cluster.merger.MapMerger
|
||||
set=org.apache.dubbo.rpc.cluster.merger.SetMerger
|
||||
list=org.apache.dubbo.rpc.cluster.merger.ListMerger
|
||||
byte=org.apache.dubbo.rpc.cluster.merger.ByteArrayMerger
|
||||
char=org.apache.dubbo.rpc.cluster.merger.CharArrayMerger
|
||||
short=org.apache.dubbo.rpc.cluster.merger.ShortArrayMerger
|
||||
int=org.apache.dubbo.rpc.cluster.merger.IntArrayMerger
|
||||
long=org.apache.dubbo.rpc.cluster.merger.LongArrayMerger
|
||||
float=org.apache.dubbo.rpc.cluster.merger.FloatArrayMerger
|
||||
double=org.apache.dubbo.rpc.cluster.merger.DoubleArrayMerger
|
||||
boolean=org.apache.dubbo.rpc.cluster.merger.BooleanArrayMerger
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory
|
||||
condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
|
||||
service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory
|
||||
app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
|
||||
tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
|
||||
mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
|
||||
mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleRouterFactory
|
||||
file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory
|
||||
condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
|
||||
service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory
|
||||
app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
|
||||
tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
|
||||
mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
|
||||
mesh-rule=org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleRouterFactory
|
||||
|
|
|
|||
|
|
@ -1,68 +1,68 @@
|
|||
/*
|
||||
* 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.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* OverrideConfiguratorTest
|
||||
*/
|
||||
public class AbsentConfiguratorTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testOverrideApplication() {
|
||||
AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideHost() {
|
||||
AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
|
||||
AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf(UrlConstant.SERVICE_TIMEOUT_200));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* OverrideConfiguratorTest
|
||||
*/
|
||||
public class AbsentConfiguratorTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testOverrideApplication() {
|
||||
AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideHost() {
|
||||
AbsentConfigurator configurator = new AbsentConfigurator(URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
|
||||
AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf(UrlConstant.SERVICE_TIMEOUT_200));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,68 @@
|
|||
/*
|
||||
* 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.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfigurator;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* OverrideConfiguratorTest
|
||||
*/
|
||||
public class OverrideConfiguratorTest {
|
||||
|
||||
@Test
|
||||
public void testOverride_Application() {
|
||||
OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverride_Host() {
|
||||
OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf("override://10.20.153.10/com.foo.BarService?timeout=200"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.absent.AbsentConfigurator;
|
||||
import org.apache.dubbo.rpc.cluster.configurator.consts.UrlConstant;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* OverrideConfiguratorTest
|
||||
*/
|
||||
public class OverrideConfiguratorTest {
|
||||
|
||||
@Test
|
||||
public void testOverride_Application() {
|
||||
OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://foo@0.0.0.0/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_11));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_11));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverride_Host() {
|
||||
OverrideConfigurator configurator = new OverrideConfigurator(URL.valueOf("override://" + NetUtils.getLocalHost() + "/com.foo.BarService?timeout=200"));
|
||||
|
||||
URL url = configurator.configure(URL.valueOf(UrlConstant.URL_CONSUMER));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
url = configurator.configure(URL.valueOf(UrlConstant.URL_ONE));
|
||||
Assertions.assertEquals("200", url.getParameter("timeout"));
|
||||
|
||||
AbsentConfigurator configurator1 = new AbsentConfigurator(URL.valueOf("override://10.20.153.10/com.foo.BarService?timeout=200"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.APPLICATION_BAR_SIDE_CONSUMER_10));
|
||||
Assertions.assertNull(url.getParameter("timeout"));
|
||||
|
||||
url = configurator1.configure(URL.valueOf(UrlConstant.TIMEOUT_1000_SIDE_CONSUMER_10));
|
||||
Assertions.assertEquals("1000", url.getParameter("timeout"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,4 +156,4 @@ public class MockDirInvocation implements Invocation {
|
|||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public interface DemoService {
|
||||
String sayHello(String name);
|
||||
|
||||
int plus(int a, int b);
|
||||
}
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public interface DemoService {
|
||||
String sayHello(String name);
|
||||
|
||||
int plus(int a, int b);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public class DemoServiceLocal implements DemoService {
|
||||
|
||||
public DemoServiceLocal(DemoService demoService) {
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public void ondisconnect() {
|
||||
|
||||
}
|
||||
|
||||
public void onconnect() {
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public class DemoServiceLocal implements DemoService {
|
||||
|
||||
public DemoServiceLocal(DemoService demoService) {
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public void ondisconnect() {
|
||||
|
||||
}
|
||||
|
||||
public void onconnect() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,31 @@
|
|||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* MockService.java
|
||||
*
|
||||
*/
|
||||
public class DemoServiceMock implements DemoService {
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* MockService.java
|
||||
*
|
||||
*/
|
||||
public class DemoServiceMock implements DemoService {
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public class DemoServiceStub implements DemoService {
|
||||
|
||||
public DemoServiceStub(DemoService demoService) {
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* <code>TestService</code>
|
||||
*/
|
||||
|
||||
public class DemoServiceStub implements DemoService {
|
||||
|
||||
public DemoServiceStub(DemoService demoService) {
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,31 @@
|
|||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* MockService.java
|
||||
*
|
||||
*/
|
||||
public class MockService implements DemoService {
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
/**
|
||||
* MockService.java
|
||||
*
|
||||
*/
|
||||
public class MockService implements DemoService {
|
||||
public String sayHello(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int plus(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,348 +1,348 @@
|
|||
/*
|
||||
* 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.condition;
|
||||
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.router.MockInvoker;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
|
||||
public class ConditionRouterTest {
|
||||
private static final String LOCAL_HOST = "127.0.0.1";
|
||||
private URL SCRIPT_URL = URL.valueOf("condition://0.0.0.0/com.foo.BarService");
|
||||
|
||||
@BeforeAll
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
private URL getRouteUrl(String rule) {
|
||||
return SCRIPT_URL.addParameterAndEncoded(RULE_KEY, rule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_matchWhen() {
|
||||
Invocation invocation = new RpcInvocation();
|
||||
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl(" => host = 1.2.3.4"));
|
||||
boolean matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_matchFilter() {
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf(
|
||||
"dubbo://10.20.3.3:20880/com.foo.BarService?serialization=fastjson"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
|
||||
System.err.println("The localhost address: " + invoker2.getUrl().getAddress());
|
||||
System.err.println(invoker3.getUrl().getAddress());
|
||||
|
||||
Router router1 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.3").addParameter(FORCE_KEY,
|
||||
String.valueOf(true)));
|
||||
Router router2 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.* & host != 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router3 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.3 & host != 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router4 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router5 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host != 10.20.3.3").addParameter(FORCE_KEY,
|
||||
String.valueOf(true)));
|
||||
Router router6 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " serialization = fastjson").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
|
||||
|
||||
|
||||
List<Invoker<String>> filteredInvokers1 = router1.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers2 = router2.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers3 = router3.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers4 = router4.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers5 = router5.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers6 = router6.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(1, filteredInvokers1.size());
|
||||
Assertions.assertEquals(0, filteredInvokers2.size());
|
||||
Assertions.assertEquals(0, filteredInvokers3.size());
|
||||
Assertions.assertEquals(1, filteredInvokers4.size());
|
||||
Assertions.assertEquals(2, filteredInvokers5.size());
|
||||
Assertions.assertEquals(1, filteredInvokers6.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_methodRoute() {
|
||||
Invocation invocation = new RpcInvocation("getFoo", "com.foo.BarService", "", new Class<?>[0], new Object[0]);
|
||||
// More than one methods, mismatch
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("methods=getFoo => host = 1.2.3.4"));
|
||||
boolean matchWhen = ((ConditionRouter) router).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Exactly one method, match
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Method routing and Other condition routing can work together
|
||||
Router router2 = new ConditionRouterFactory()
|
||||
.getRouter(getRouteUrl("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router2).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
Router router3 = new ConditionRouterFactory()
|
||||
.getRouter(getRouteUrl("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router3).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Test filter condition
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
|
||||
Router router4 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " & methods = getFoo => " + " host = 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> filteredInvokers1 = router4.route(invokers,
|
||||
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(1, filteredInvokers1.size());
|
||||
|
||||
Router router5 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " & methods = unvalidmethod => " + " host = 10.20.3.3")
|
||||
.addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> filteredInvokers2 = router5.route(invokers,
|
||||
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, filteredInvokers2.size());
|
||||
// Request a non-exists method
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnFalse() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => false"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnEmpty() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => "));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnAll() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(invokers, filteredInvokers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Empty_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl(" => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_False_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("true => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Placeholder() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = $host"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_NoForce() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(invokers, filteredInvokers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Force() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Arguments() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0] = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> invokers = new ArrayList<>();
|
||||
Invoker<String> invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String p = "a";
|
||||
invocation.setArguments(new Object[]{null});
|
||||
List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
invocation.setArguments(new Object[]{p});
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(0, fileredInvokers.size());
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments = b " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[10].inner = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
int integer = 1;
|
||||
invocation.setArguments(new Object[]{integer});
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0].inner = 1 " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(0, fileredInvokers.size());
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.condition;
|
||||
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Router;
|
||||
import org.apache.dubbo.rpc.cluster.router.MockInvoker;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_KEY;
|
||||
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
|
||||
|
||||
public class ConditionRouterTest {
|
||||
private static final String LOCAL_HOST = "127.0.0.1";
|
||||
private URL SCRIPT_URL = URL.valueOf("condition://0.0.0.0/com.foo.BarService");
|
||||
|
||||
@BeforeAll
|
||||
public static void setUpBeforeClass() throws Exception {
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
private URL getRouteUrl(String rule) {
|
||||
return SCRIPT_URL.addParameterAndEncoded(RULE_KEY, rule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_matchWhen() {
|
||||
Invocation invocation = new RpcInvocation();
|
||||
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl(" => host = 1.2.3.4"));
|
||||
boolean matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(URL.valueOf("consumer://1.1.1.1/com.foo.BarService"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_matchFilter() {
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf(
|
||||
"dubbo://10.20.3.3:20880/com.foo.BarService?serialization=fastjson"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
|
||||
System.err.println("The localhost address: " + invoker2.getUrl().getAddress());
|
||||
System.err.println(invoker3.getUrl().getAddress());
|
||||
|
||||
Router router1 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.3").addParameter(FORCE_KEY,
|
||||
String.valueOf(true)));
|
||||
Router router2 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.* & host != 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router3 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.3 & host != 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router4 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
Router router5 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " host != 10.20.3.3").addParameter(FORCE_KEY,
|
||||
String.valueOf(true)));
|
||||
Router router6 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " => " + " serialization = fastjson").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
|
||||
|
||||
|
||||
List<Invoker<String>> filteredInvokers1 = router1.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers2 = router2.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers3 = router3.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers4 = router4.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers5 = router5.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
List<Invoker<String>> filteredInvokers6 = router6.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(1, filteredInvokers1.size());
|
||||
Assertions.assertEquals(0, filteredInvokers2.size());
|
||||
Assertions.assertEquals(0, filteredInvokers3.size());
|
||||
Assertions.assertEquals(1, filteredInvokers4.size());
|
||||
Assertions.assertEquals(2, filteredInvokers5.size());
|
||||
Assertions.assertEquals(1, filteredInvokers6.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_methodRoute() {
|
||||
Invocation invocation = new RpcInvocation("getFoo", "com.foo.BarService", "", new Class<?>[0], new Object[0]);
|
||||
// More than one methods, mismatch
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("methods=getFoo => host = 1.2.3.4"));
|
||||
boolean matchWhen = ((ConditionRouter) router).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Exactly one method, match
|
||||
matchWhen = ((ConditionRouter) router).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Method routing and Other condition routing can work together
|
||||
Router router2 = new ConditionRouterFactory()
|
||||
.getRouter(getRouteUrl("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router2).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertFalse(matchWhen);
|
||||
|
||||
Router router3 = new ConditionRouterFactory()
|
||||
.getRouter(getRouteUrl("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4"));
|
||||
matchWhen = ((ConditionRouter) router3).matchWhen(
|
||||
URL.valueOf("consumer://1.1.1.1/com.foo.BarService?methods=getFoo"), invocation);
|
||||
Assertions.assertTrue(matchWhen);
|
||||
// Test filter condition
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST
|
||||
+ ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
|
||||
Router router4 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " & methods = getFoo => " + " host = 10.20.3.3").addParameter(
|
||||
FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> filteredInvokers1 = router4.route(invokers,
|
||||
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(1, filteredInvokers1.size());
|
||||
|
||||
Router router5 = new ConditionRouterFactory().getRouter(getRouteUrl(
|
||||
"host = " + LOCAL_HOST + " & methods = unvalidmethod => " + " host = 10.20.3.3")
|
||||
.addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> filteredInvokers2 = router5.route(invokers,
|
||||
URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, filteredInvokers2.size());
|
||||
// Request a non-exists method
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnFalse() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => false"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnEmpty() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => "));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
invokers.add(new MockInvoker<String>());
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_ReturnAll() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
invokers.add(new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService")));
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(invokers, filteredInvokers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Empty_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl(" => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_False_HostFilter() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("true => " + " host = " + LOCAL_HOST));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Placeholder() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = $host"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(2, filteredInvokers.size());
|
||||
Assertions.assertEquals(invoker2, filteredInvokers.get(0));
|
||||
Assertions.assertEquals(invoker3, filteredInvokers.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_NoForce() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4"));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(invokers, filteredInvokers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Force() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("host = " + LOCAL_HOST + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
|
||||
Invoker<String> invoker1 = new MockInvoker<String>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<String>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
List<Invoker<String>> filteredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), new RpcInvocation());
|
||||
Assertions.assertEquals(0, filteredInvokers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoute_Arguments() {
|
||||
Router router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0] = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
List<Invoker<String>> invokers = new ArrayList<>();
|
||||
Invoker<String> invoker1 = new MockInvoker<>(URL.valueOf("dubbo://10.20.3.3:20880/com.foo.BarService"));
|
||||
Invoker<String> invoker2 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
Invoker<String> invoker3 = new MockInvoker<>(URL.valueOf("dubbo://" + LOCAL_HOST + ":20880/com.foo.BarService"));
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
invokers.add(invoker3);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String p = "a";
|
||||
invocation.setArguments(new Object[]{null});
|
||||
List<Invoker<String>> fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
invocation.setArguments(new Object[]{p});
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(0, fileredInvokers.size());
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments = b " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[10].inner = a " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(3, fileredInvokers.size());
|
||||
|
||||
int integer = 1;
|
||||
invocation.setArguments(new Object[]{integer});
|
||||
router = new ConditionRouterFactory().getRouter(getRouteUrl("arguments[0].inner = 1 " + " => " + " host = 1.2.3.4").addParameter(FORCE_KEY, String.valueOf(true)));
|
||||
fileredInvokers = router.route(invokers, URL.valueOf("consumer://" + LOCAL_HOST + "/com.foo.BarService"), invocation);
|
||||
Assertions.assertEquals(0, fileredInvokers.size());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,4 +177,4 @@ public class MeshAppRuleListenerTest {
|
|||
assertTrue(vsDestinationGroup1.getVirtualServiceRuleList().size() == 0);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,4 +157,4 @@ public class MeshRuleManagerTest {
|
|||
ApplicationModel.getEnvironment().setDynamicConfiguration(before.orElse(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ public class MeshRuleRouterFactoryTest {
|
|||
when(url.getServiceKey()).thenReturn("demoService");
|
||||
ruleRouterFactory.getRouter(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1404,4 +1404,4 @@ public class MeshRuleRouterTest {
|
|||
assertNotNull(meshRuleRouter.route((List) inputInvokers, inputURL, invocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,4 +99,4 @@ public class DestinationRuleTest {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ public class VirtualServiceRuleTest {
|
|||
assertNotNull(virtualServiceRule);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,4 +137,4 @@ public class DubboMatchRequestTest {
|
|||
|
||||
assertFalse(DubboMatchRequest.isMatch(dubboMatchRequest, "sayHello", new String[]{}, new Object[]{}, inputSourceLablesMap, invokeEagleEyeContextMap, invokeDubboContextMap2, new HashMap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,4 @@ public class BoolMatchTest {
|
|||
assertTrue(BoolMatch.isMatch(boolMatch,false));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,4 +98,4 @@ public class DoubleMatchTest {
|
|||
assertTrue(DoubleMatch.isMatch(doubleMatch, 3.0));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,4 +175,4 @@ public class DubboAttachmentMatchTest {
|
|||
assertFalse(DubboAttachmentMatch.isMatch(dubboAttachmentMatch, invokeEagleEyeContextMap2, invokeDubboContextMap2));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,4 +153,4 @@ public class DubboMethodMatchTest {
|
|||
assertTrue(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", true}));
|
||||
assertFalse(DubboMethodMatch.isMatch(dubboMethodMatch, "test", new String[]{"int", "java.lang.String", "boolean"}, new Object[]{10, "sayHello", false}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,4 +49,4 @@ public class ListDoubleMatchTest {
|
|||
assertTrue(ListDoubleMatch.isMatch(listDoubleMatch, 11.0));
|
||||
assertFalse(ListDoubleMatch.isMatch(listDoubleMatch, 12.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,4 +51,4 @@ public class ListStringMatchTest {
|
|||
assertFalse(ListStringMatch.isMatch(listStringMatch, "3"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,4 +78,4 @@ public class StringMatchTest {
|
|||
assertFalse(StringMatch.isMatch(stringMatch, ""));
|
||||
assertFalse(StringMatch.isMatch(stringMatch, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,4 +71,4 @@ class VsDestinationGroupRuleDispatcherTest {
|
|||
|
||||
verify(vsDestinationGroupRuleListener, times(1)).onRuleChange(anyObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,15 +96,15 @@ public class ScriptRouterTest {
|
|||
invokers.add(invoker3);
|
||||
|
||||
String script = "function route(invokers, invocation, context){ " +
|
||||
" var result = new java.util.ArrayList(invokers.size()); " +
|
||||
" var targetHost = new java.util.ArrayList(); " +
|
||||
" targetHost.add(\"10.134.108.2\"); " +
|
||||
" for (var i = 0; i < invokers.length; i++) { " +
|
||||
" if(targetHost.contains(invokers[i].getUrl().getHost())){ " +
|
||||
" result.add(invokers[i]); " +
|
||||
" } " +
|
||||
" } " +
|
||||
" return result; " +
|
||||
" var result = new java.util.ArrayList(invokers.size()); " +
|
||||
" var targetHost = new java.util.ArrayList(); " +
|
||||
" targetHost.add(\"10.134.108.2\"); " +
|
||||
" for (var i = 0; i < invokers.length; i++) { " +
|
||||
" if(targetHost.contains(invokers[i].getUrl().getHost())){ " +
|
||||
" result.add(invokers[i]); " +
|
||||
" } " +
|
||||
" } " +
|
||||
" return result; " +
|
||||
"} " +
|
||||
"route(invokers, invocation, context) ";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,122 +1,122 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.LogUtil;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.filter.DemoService;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailfastClusterInvokerTest
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FailSafeClusterInvokerTest {
|
||||
List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();
|
||||
URL url = URL.valueOf("test://test:11/test");
|
||||
Invoker<DemoService> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<DemoService> dic;
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(DemoService.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
}
|
||||
|
||||
private void resetInvokerToException() {
|
||||
given(invoker.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
}
|
||||
|
||||
private void resetInvokerToNoException() {
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
}
|
||||
|
||||
//TODO assert error log
|
||||
@Test
|
||||
public void testInvokeExceptoin() {
|
||||
resetInvokerToException();
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvokeNoExceptoin() {
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
Result ret = invoker.invoke(invocation);
|
||||
Assertions.assertSame(result, ret);
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(DemoService.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
LogUtil.start();
|
||||
invoker.invoke(invocation);
|
||||
assertTrue(LogUtil.findMessage("No provider") > 0);
|
||||
LogUtil.stop();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.LogUtil;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.filter.DemoService;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailfastClusterInvokerTest
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FailSafeClusterInvokerTest {
|
||||
List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();
|
||||
URL url = URL.valueOf("test://test:11/test");
|
||||
Invoker<DemoService> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<DemoService> dic;
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(DemoService.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
}
|
||||
|
||||
private void resetInvokerToException() {
|
||||
given(invoker.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
}
|
||||
|
||||
private void resetInvokerToNoException() {
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(DemoService.class);
|
||||
}
|
||||
|
||||
//TODO assert error log
|
||||
@Test
|
||||
public void testInvokeExceptoin() {
|
||||
resetInvokerToException();
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvokeNoExceptoin() {
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
Result ret = invoker.invoke(invocation);
|
||||
Assertions.assertSame(result, ret);
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(DemoService.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
|
||||
LogUtil.start();
|
||||
invoker.invoke(invocation);
|
||||
assertTrue(LogUtil.findMessage("No provider") > 0);
|
||||
LogUtil.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,177 +1,177 @@
|
|||
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.DubboAppender;
|
||||
import org.apache.dubbo.common.utils.LogUtil;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
import org.apache.log4j.Level;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailbackClusterInvokerTest
|
||||
* <p>
|
||||
* add annotation @TestMethodOrder, the testARetryFailed Method must to first execution
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class FailbackClusterInvokerTest {
|
||||
|
||||
List<Invoker<FailbackClusterInvokerTest>> invokers = new ArrayList<Invoker<FailbackClusterInvokerTest>>();
|
||||
URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=2");
|
||||
Invoker<FailbackClusterInvokerTest> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<FailbackClusterInvokerTest> dic;
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
|
||||
dic = null;
|
||||
invocation = new RpcInvocation();
|
||||
invokers.clear();
|
||||
}
|
||||
|
||||
|
||||
private void resetInvokerToException() {
|
||||
given(invoker.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
}
|
||||
|
||||
private void resetInvokerToNoException() {
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testInvokeException() {
|
||||
resetInvokerToException();
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
DubboAppender.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testInvokeNoException() {
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
Result ret = invoker.invoke(invocation);
|
||||
Assertions.assertSame(result, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
LogUtil.start();
|
||||
DubboAppender.clear();
|
||||
invoker.invoke(invocation);
|
||||
assertEquals(1, LogUtil.findMessage("Failback to invoke"));
|
||||
LogUtil.stop();
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testARetryFailed() throws Exception {
|
||||
//Test retries and
|
||||
|
||||
resetInvokerToException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
LogUtil.start();
|
||||
DubboAppender.clear();
|
||||
invoker.invoke(invocation);
|
||||
invoker.invoke(invocation);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
// invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked invoker,so
|
||||
//Ensure that the main thread is online
|
||||
CountDownLatch countDown = new CountDownLatch(1);
|
||||
countDown.await(15000L, TimeUnit.MILLISECONDS);
|
||||
LogUtil.stop();
|
||||
Assertions.assertEquals(4, LogUtil.findMessage(Level.ERROR, "Failed retry to invoke method"), "must have four error message ");
|
||||
Assertions.assertEquals(2, LogUtil.findMessage(Level.ERROR, "Failed retry times exceed threshold"), "must have two error message ");
|
||||
Assertions.assertEquals(1, LogUtil.findMessage(Level.ERROR, "Failback background works error"), "must have one error message ");
|
||||
// it can be invoke successfully
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.DubboAppender;
|
||||
import org.apache.dubbo.common.utils.LogUtil;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
|
||||
import org.apache.log4j.Level;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailbackClusterInvokerTest
|
||||
* <p>
|
||||
* add annotation @TestMethodOrder, the testARetryFailed Method must to first execution
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class FailbackClusterInvokerTest {
|
||||
|
||||
List<Invoker<FailbackClusterInvokerTest>> invokers = new ArrayList<Invoker<FailbackClusterInvokerTest>>();
|
||||
URL url = URL.valueOf("test://test:11/test?retries=2&failbacktasks=2");
|
||||
Invoker<FailbackClusterInvokerTest> invoker = mock(Invoker.class);
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
Directory<FailbackClusterInvokerTest> dic;
|
||||
Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
|
||||
dic = null;
|
||||
invocation = new RpcInvocation();
|
||||
invokers.clear();
|
||||
}
|
||||
|
||||
|
||||
private void resetInvokerToException() {
|
||||
given(invoker.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
}
|
||||
|
||||
private void resetInvokerToNoException() {
|
||||
given(invoker.invoke(invocation)).willReturn(result);
|
||||
given(invoker.getUrl()).willReturn(url);
|
||||
given(invoker.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testInvokeException() {
|
||||
resetInvokerToException();
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
DubboAppender.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testInvokeNoException() {
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
Result ret = invoker.invoke(invocation);
|
||||
Assertions.assertSame(result, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(FailbackClusterInvokerTest.class);
|
||||
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker);
|
||||
|
||||
resetInvokerToNoException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
LogUtil.start();
|
||||
DubboAppender.clear();
|
||||
invoker.invoke(invocation);
|
||||
assertEquals(1, LogUtil.findMessage("Failback to invoke"));
|
||||
LogUtil.stop();
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testARetryFailed() throws Exception {
|
||||
//Test retries and
|
||||
|
||||
resetInvokerToException();
|
||||
|
||||
FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
|
||||
dic);
|
||||
LogUtil.start();
|
||||
DubboAppender.clear();
|
||||
invoker.invoke(invocation);
|
||||
invoker.invoke(invocation);
|
||||
invoker.invoke(invocation);
|
||||
Assertions.assertNull(RpcContext.getServiceContext().getInvoker());
|
||||
// invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked invoker,so
|
||||
//Ensure that the main thread is online
|
||||
CountDownLatch countDown = new CountDownLatch(1);
|
||||
countDown.await(15000L, TimeUnit.MILLISECONDS);
|
||||
LogUtil.stop();
|
||||
Assertions.assertEquals(4, LogUtil.findMessage(Level.ERROR, "Failed retry to invoke method"), "must have four error message ");
|
||||
Assertions.assertEquals(2, LogUtil.findMessage(Level.ERROR, "Failed retry times exceed threshold"), "must have two error message ");
|
||||
Assertions.assertEquals(1, LogUtil.findMessage(Level.ERROR, "Failback background works error"), "must have one error message ");
|
||||
// it can be invoke successfully
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,4 +124,4 @@ public class FailfastClusterInvokerTest {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,290 +1,290 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractInvoker;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailoverClusterInvokerTest
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FailoverClusterInvokerTest {
|
||||
private List<Invoker<FailoverClusterInvokerTest>> invokers = new ArrayList<Invoker<FailoverClusterInvokerTest>>();
|
||||
private int retries = 5;
|
||||
private URL url = URL.valueOf("test://test:11/test?retries=" + retries);
|
||||
private Invoker<FailoverClusterInvokerTest> invoker1 = mock(Invoker.class);
|
||||
private Invoker<FailoverClusterInvokerTest> invoker2 = mock(Invoker.class);
|
||||
private RpcInvocation invocation = new RpcInvocation();
|
||||
private Directory<FailoverClusterInvokerTest> dic;
|
||||
private Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvokeWithRuntimeException() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker1.isAvailable()).willReturn(true);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker2.isAvailable()).willReturn(true);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
invoker.invoke(invocation);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertEquals(0, expected.getCode());
|
||||
assertFalse(expected.getCause() instanceof RpcException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvokeWithRPCException() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker1.isAvailable()).willReturn(true);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willReturn(result);
|
||||
given(invoker2.isAvailable()).willReturn(true);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvoke_retryTimes() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION));
|
||||
given(invoker1.isAvailable()).willReturn(false);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker2.isAvailable()).willReturn(false);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertTrue((expected.isTimeout() || expected.getCode() == 0));
|
||||
assertTrue(expected.getMessage().indexOf((retries + 1) + " times") > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvoke_retryTimes2() {
|
||||
int finalRetries = 1;
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION));
|
||||
given(invoker1.isAvailable()).willReturn(false);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker2.isAvailable()).willReturn(false);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
RpcContext rpcContext = RpcContext.getContext();
|
||||
rpcContext.setAttachment("retries", finalRetries);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertTrue((expected.isTimeout() || expected.getCode() == 0));
|
||||
assertTrue(expected.getMessage().indexOf((finalRetries+1) + " times") > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker1);
|
||||
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
invoker.invoke(invocation);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertFalse(expected.getCause() instanceof RpcException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When invokers in directory changes after a failed request but just before a retry effort,
|
||||
* then we should reselect from the latest invokers before retry.
|
||||
*/
|
||||
@Test
|
||||
public void testInvokerDestroyAndReList() {
|
||||
final URL url = URL.valueOf("test://localhost/" + Demo.class.getName() + "?loadbalance=roundrobin&retries=" + retries);
|
||||
RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION);
|
||||
MockInvoker<Demo> invoker1 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker1.setException(exception);
|
||||
|
||||
MockInvoker<Demo> invoker2 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker2.setException(exception);
|
||||
|
||||
final List<Invoker<Demo>> invokers = new ArrayList<Invoker<Demo>>();
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
|
||||
Callable<Object> callable = () -> {
|
||||
//Simulation: all invokers are destroyed
|
||||
for (Invoker<Demo> invoker : invokers) {
|
||||
invoker.destroy();
|
||||
}
|
||||
invokers.clear();
|
||||
MockInvoker<Demo> invoker3 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker3.setResult(AsyncRpcResult.newDefaultAsyncResult(null));
|
||||
invokers.add(invoker3);
|
||||
return null;
|
||||
};
|
||||
invoker1.setCallable(callable);
|
||||
invoker2.setCallable(callable);
|
||||
|
||||
RpcInvocation inv = new RpcInvocation();
|
||||
inv.setMethodName("test");
|
||||
|
||||
Directory<Demo> dic = new MockDirectory<Demo>(url, invokers);
|
||||
|
||||
FailoverClusterInvoker<Demo> clusterinvoker = new FailoverClusterInvoker<Demo>(dic);
|
||||
clusterinvoker.invoke(inv);
|
||||
}
|
||||
|
||||
public interface Demo {
|
||||
}
|
||||
|
||||
public static class MockInvoker<T> extends AbstractInvoker<T> {
|
||||
URL url;
|
||||
boolean available = true;
|
||||
boolean destoryed = false;
|
||||
Result result;
|
||||
RpcException exception;
|
||||
Callable<?> callable;
|
||||
|
||||
public MockInvoker(Class<T> type, URL url) {
|
||||
super(type, url);
|
||||
}
|
||||
|
||||
public void setResult(Result result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public void setException(RpcException exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public void setCallable(Callable<?> callable) {
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result doInvoke(Invocation invocation) throws Throwable {
|
||||
if (callable != null) {
|
||||
try {
|
||||
callable.call();
|
||||
} catch (Exception e) {
|
||||
throw new RpcException(e);
|
||||
}
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MockDirectory<T> extends StaticDirectory<T> {
|
||||
public MockDirectory(URL url, List<Invoker<T>> invokers) {
|
||||
super(url, invokers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
|
||||
return new ArrayList<Invoker<T>>(super.doList(invocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.AppResponse;
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.cluster.Directory;
|
||||
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractInvoker;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* FailoverClusterInvokerTest
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FailoverClusterInvokerTest {
|
||||
private List<Invoker<FailoverClusterInvokerTest>> invokers = new ArrayList<Invoker<FailoverClusterInvokerTest>>();
|
||||
private int retries = 5;
|
||||
private URL url = URL.valueOf("test://test:11/test?retries=" + retries);
|
||||
private Invoker<FailoverClusterInvokerTest> invoker1 = mock(Invoker.class);
|
||||
private Invoker<FailoverClusterInvokerTest> invoker2 = mock(Invoker.class);
|
||||
private RpcInvocation invocation = new RpcInvocation();
|
||||
private Directory<FailoverClusterInvokerTest> dic;
|
||||
private Result result = new AppResponse();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(invokers);
|
||||
given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvokeWithRuntimeException() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker1.isAvailable()).willReturn(true);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RuntimeException());
|
||||
given(invoker2.isAvailable()).willReturn(true);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
invoker.invoke(invocation);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertEquals(0, expected.getCode());
|
||||
assertFalse(expected.getCause() instanceof RpcException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvokeWithRPCException() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker1.isAvailable()).willReturn(true);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willReturn(result);
|
||||
given(invoker2.isAvailable()).willReturn(true);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvoke_retryTimes() {
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION));
|
||||
given(invoker1.isAvailable()).willReturn(false);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker2.isAvailable()).willReturn(false);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertTrue((expected.isTimeout() || expected.getCode() == 0));
|
||||
assertTrue(expected.getMessage().indexOf((retries + 1) + " times") > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testInvoke_retryTimes2() {
|
||||
int finalRetries = 1;
|
||||
given(invoker1.invoke(invocation)).willThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION));
|
||||
given(invoker1.isAvailable()).willReturn(false);
|
||||
given(invoker1.getUrl()).willReturn(url);
|
||||
given(invoker1.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
given(invoker2.invoke(invocation)).willThrow(new RpcException());
|
||||
given(invoker2.isAvailable()).willReturn(false);
|
||||
given(invoker2.getUrl()).willReturn(url);
|
||||
given(invoker2.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
|
||||
RpcContext rpcContext = RpcContext.getContext();
|
||||
rpcContext.setAttachment("retries", finalRetries);
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
Result ret = invoker.invoke(invocation);
|
||||
assertSame(result, ret);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertTrue((expected.isTimeout() || expected.getCode() == 0));
|
||||
assertTrue(expected.getMessage().indexOf((finalRetries+1) + " times") > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testNoInvoke() {
|
||||
dic = mock(Directory.class);
|
||||
|
||||
given(dic.getUrl()).willReturn(url);
|
||||
given(dic.getConsumerUrl()).willReturn(url);
|
||||
given(dic.list(invocation)).willReturn(null);
|
||||
given(dic.getInterface()).willReturn(FailoverClusterInvokerTest.class);
|
||||
invocation.setMethodName("method1");
|
||||
|
||||
invokers.add(invoker1);
|
||||
|
||||
|
||||
FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
|
||||
try {
|
||||
invoker.invoke(invocation);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
assertFalse(expected.getCause() instanceof RpcException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When invokers in directory changes after a failed request but just before a retry effort,
|
||||
* then we should reselect from the latest invokers before retry.
|
||||
*/
|
||||
@Test
|
||||
public void testInvokerDestroyAndReList() {
|
||||
final URL url = URL.valueOf("test://localhost/" + Demo.class.getName() + "?loadbalance=roundrobin&retries=" + retries);
|
||||
RpcException exception = new RpcException(RpcException.TIMEOUT_EXCEPTION);
|
||||
MockInvoker<Demo> invoker1 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker1.setException(exception);
|
||||
|
||||
MockInvoker<Demo> invoker2 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker2.setException(exception);
|
||||
|
||||
final List<Invoker<Demo>> invokers = new ArrayList<Invoker<Demo>>();
|
||||
invokers.add(invoker1);
|
||||
invokers.add(invoker2);
|
||||
|
||||
Callable<Object> callable = () -> {
|
||||
//Simulation: all invokers are destroyed
|
||||
for (Invoker<Demo> invoker : invokers) {
|
||||
invoker.destroy();
|
||||
}
|
||||
invokers.clear();
|
||||
MockInvoker<Demo> invoker3 = new MockInvoker<Demo>(Demo.class, url);
|
||||
invoker3.setResult(AsyncRpcResult.newDefaultAsyncResult(null));
|
||||
invokers.add(invoker3);
|
||||
return null;
|
||||
};
|
||||
invoker1.setCallable(callable);
|
||||
invoker2.setCallable(callable);
|
||||
|
||||
RpcInvocation inv = new RpcInvocation();
|
||||
inv.setMethodName("test");
|
||||
|
||||
Directory<Demo> dic = new MockDirectory<Demo>(url, invokers);
|
||||
|
||||
FailoverClusterInvoker<Demo> clusterinvoker = new FailoverClusterInvoker<Demo>(dic);
|
||||
clusterinvoker.invoke(inv);
|
||||
}
|
||||
|
||||
public interface Demo {
|
||||
}
|
||||
|
||||
public static class MockInvoker<T> extends AbstractInvoker<T> {
|
||||
URL url;
|
||||
boolean available = true;
|
||||
boolean destoryed = false;
|
||||
Result result;
|
||||
RpcException exception;
|
||||
Callable<?> callable;
|
||||
|
||||
public MockInvoker(Class<T> type, URL url) {
|
||||
super(type, url);
|
||||
}
|
||||
|
||||
public void setResult(Result result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public void setException(RpcException exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public void setCallable(Callable<?> callable) {
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result doInvoke(Invocation invocation) throws Throwable {
|
||||
if (callable != null) {
|
||||
try {
|
||||
callable.call();
|
||||
} catch (Exception e) {
|
||||
throw new RpcException(e);
|
||||
}
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MockDirectory<T> extends StaticDirectory<T> {
|
||||
public MockDirectory(URL url, List<Invoker<T>> invokers) {
|
||||
super(url, invokers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
|
||||
return new ArrayList<Invoker<T>>(super.doList(invocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,4 +154,4 @@ public class ForkingClusterInvokerTest {
|
|||
Assertions.assertSame(result, ret);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
|
||||
public class MyMockException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 2851692379597990457L;
|
||||
|
||||
public MyMockException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.support.wrapper;
|
||||
|
||||
|
||||
public class MyMockException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 2851692379597990457L;
|
||||
|
||||
public MyMockException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory
|
||||
script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
|
||||
<appender name="DUBBO" class="org.apache.dubbo.common.utils.DubboAppender">
|
||||
<param name="File" value="../dubbo.log"/>
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
<root>
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="DUBBO"/>
|
||||
</root>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
|
||||
<appender name="DUBBO" class="org.apache.dubbo.common.utils.DubboAppender">
|
||||
<param name="File" value="../dubbo.log"/>
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
<root>
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="DUBBO"/>
|
||||
</root>
|
||||
</log4j:configuration>
|
||||
|
|
@ -42,4 +42,4 @@ public @interface Activate {
|
|||
String[] after() default {};
|
||||
|
||||
int order() default 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,68 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marker for extension interface
|
||||
* <p/>
|
||||
* Changes on extension configuration file <br/>
|
||||
* Use <code>Protocol</code> as an example, its configuration file 'META-INF/dubbo/com.xxx.Protocol' is changes from: <br/>
|
||||
* <pre>
|
||||
* com.foo.XxxProtocol
|
||||
* com.foo.YyyProtocol
|
||||
* </pre>
|
||||
* <p>
|
||||
* to key-value pair <br/>
|
||||
* <pre>
|
||||
* xxx=com.foo.XxxProtocol
|
||||
* yyy=com.foo.YyyProtocol
|
||||
* </pre>
|
||||
* <br/>
|
||||
* The reason for this change is:
|
||||
* <p>
|
||||
* If there's third party library referenced by static field or by method in extension implementation, its class will
|
||||
* fail to initialize if the third party library doesn't exist. In this case, dubbo cannot figure out extension's id
|
||||
* therefore cannot be able to map the exception information with the extension, if the previous format is used.
|
||||
* <p/>
|
||||
* For example:
|
||||
* <p>
|
||||
* Fails to load Extension("mina"). When user configure to use mina, dubbo will complain the extension cannot be loaded,
|
||||
* instead of reporting which extract extension implementation fails and the extract reason.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated because it's too general, switch to use {@link org.apache.dubbo.common.extension.SPI}
|
||||
*/
|
||||
@Deprecated
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Extension {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marker for extension interface
|
||||
* <p/>
|
||||
* Changes on extension configuration file <br/>
|
||||
* Use <code>Protocol</code> as an example, its configuration file 'META-INF/dubbo/com.xxx.Protocol' is changes from: <br/>
|
||||
* <pre>
|
||||
* com.foo.XxxProtocol
|
||||
* com.foo.YyyProtocol
|
||||
* </pre>
|
||||
* <p>
|
||||
* to key-value pair <br/>
|
||||
* <pre>
|
||||
* xxx=com.foo.XxxProtocol
|
||||
* yyy=com.foo.YyyProtocol
|
||||
* </pre>
|
||||
* <br/>
|
||||
* The reason for this change is:
|
||||
* <p>
|
||||
* If there's third party library referenced by static field or by method in extension implementation, its class will
|
||||
* fail to initialize if the third party library doesn't exist. In this case, dubbo cannot figure out extension's id
|
||||
* therefore cannot be able to map the exception information with the extension, if the previous format is used.
|
||||
* <p/>
|
||||
* For example:
|
||||
* <p>
|
||||
* Fails to load Extension("mina"). When user configure to use mina, dubbo will complain the extension cannot be loaded,
|
||||
* instead of reporting which extract extension implementation fails and the extract reason.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated because it's too general, switch to use {@link org.apache.dubbo.common.extension.SPI}
|
||||
*/
|
||||
@Deprecated
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Extension {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* Node. (API/SPI, Prototype, ThreadSafe)
|
||||
*/
|
||||
public interface Node {
|
||||
|
||||
/**
|
||||
* get url.
|
||||
*
|
||||
* @return url.
|
||||
*/
|
||||
URL getUrl();
|
||||
|
||||
/**
|
||||
* is available.
|
||||
*
|
||||
* @return available.
|
||||
*/
|
||||
boolean isAvailable();
|
||||
|
||||
/**
|
||||
* destroy.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* Node. (API/SPI, Prototype, ThreadSafe)
|
||||
*/
|
||||
public interface Node {
|
||||
|
||||
/**
|
||||
* get url.
|
||||
*
|
||||
* @return url.
|
||||
*/
|
||||
URL getUrl();
|
||||
|
||||
/**
|
||||
* is available.
|
||||
*
|
||||
* @return available.
|
||||
*/
|
||||
boolean isAvailable();
|
||||
|
||||
/**
|
||||
* destroy.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,263 +1,263 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
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.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* Parameters for backward compatibility for version prior to 2.0.5
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class Parameters {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Parameters.class);
|
||||
private final Map<String, String> parameters;
|
||||
|
||||
public Parameters(String... pairs) {
|
||||
this(toMap(pairs));
|
||||
}
|
||||
|
||||
public Parameters(Map<String, String> parameters) {
|
||||
this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<>(parameters) : new HashMap<>(0));
|
||||
}
|
||||
|
||||
private static Map<String, String> toMap(String... pairs) {
|
||||
return CollectionUtils.toStringMap(pairs);
|
||||
}
|
||||
|
||||
public static Parameters parseParameters(String query) {
|
||||
return new Parameters(StringUtils.parseQueryString(query));
|
||||
}
|
||||
|
||||
public Map<String, String> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public <T> T getExtension(Class<T> type, String key) {
|
||||
String name = getParameter(key);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getExtension(Class<T> type, String key, String defaultValue) {
|
||||
String name = getParameter(key, defaultValue);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getMethodExtension(Class<T> type, String method, String key) {
|
||||
String name = getMethodParameter(method, key);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getMethodExtension(Class<T> type, String method, String key, String defaultValue) {
|
||||
String name = getMethodParameter(method, key, defaultValue);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public String getDecodedParameter(String key) {
|
||||
return getDecodedParameter(key, null);
|
||||
}
|
||||
|
||||
public String getDecodedParameter(String key, String defaultValue) {
|
||||
String value = getParameter(key, defaultValue);
|
||||
if (value != null && value.length() > 0) {
|
||||
try {
|
||||
value = URLDecoder.decode(value, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getParameter(String key) {
|
||||
String value = parameters.get(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(DEFAULT_KEY_PREFIX + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + DEFAULT_KEY_PREFIX + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getParameter(String key, String defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getIntParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getIntParameter(String key, int defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getPositiveIntParameter(String key, int defaultValue) {
|
||||
if (defaultValue <= 0) {
|
||||
throw new IllegalArgumentException("defaultValue <= 0");
|
||||
}
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
int i = Integer.parseInt(value);
|
||||
if (i > 0) {
|
||||
return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean getBooleanParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean getBooleanParameter(String key, boolean defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean hasParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key) {
|
||||
String value = parameters.get(method + "." + key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + method + "." + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return getParameter(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key, String defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getMethodIntParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getMethodIntParameter(String method, String key, int defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
|
||||
if (defaultValue <= 0) {
|
||||
throw new IllegalArgumentException("defaultValue <= 0");
|
||||
}
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
int i = Integer.parseInt(value);
|
||||
if (i > 0) {
|
||||
return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean getMethodBooleanParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean hasMethodParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
return parameters.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return parameters.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.toQueryString(getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
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.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* Parameters for backward compatibility for version prior to 2.0.5
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class Parameters {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Parameters.class);
|
||||
private final Map<String, String> parameters;
|
||||
|
||||
public Parameters(String... pairs) {
|
||||
this(toMap(pairs));
|
||||
}
|
||||
|
||||
public Parameters(Map<String, String> parameters) {
|
||||
this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<>(parameters) : new HashMap<>(0));
|
||||
}
|
||||
|
||||
private static Map<String, String> toMap(String... pairs) {
|
||||
return CollectionUtils.toStringMap(pairs);
|
||||
}
|
||||
|
||||
public static Parameters parseParameters(String query) {
|
||||
return new Parameters(StringUtils.parseQueryString(query));
|
||||
}
|
||||
|
||||
public Map<String, String> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public <T> T getExtension(Class<T> type, String key) {
|
||||
String name = getParameter(key);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getExtension(Class<T> type, String key, String defaultValue) {
|
||||
String name = getParameter(key, defaultValue);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getMethodExtension(Class<T> type, String method, String key) {
|
||||
String name = getMethodParameter(method, key);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public <T> T getMethodExtension(Class<T> type, String method, String key, String defaultValue) {
|
||||
String name = getMethodParameter(method, key, defaultValue);
|
||||
return ExtensionLoader.getExtensionLoader(type).getExtension(name);
|
||||
}
|
||||
|
||||
public String getDecodedParameter(String key) {
|
||||
return getDecodedParameter(key, null);
|
||||
}
|
||||
|
||||
public String getDecodedParameter(String key, String defaultValue) {
|
||||
String value = getParameter(key, defaultValue);
|
||||
if (value != null && value.length() > 0) {
|
||||
try {
|
||||
value = URLDecoder.decode(value, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getParameter(String key) {
|
||||
String value = parameters.get(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(DEFAULT_KEY_PREFIX + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + DEFAULT_KEY_PREFIX + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getParameter(String key, String defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getIntParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getIntParameter(String key, int defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getPositiveIntParameter(String key, int defaultValue) {
|
||||
if (defaultValue <= 0) {
|
||||
throw new IllegalArgumentException("defaultValue <= 0");
|
||||
}
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
int i = Integer.parseInt(value);
|
||||
if (i > 0) {
|
||||
return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean getBooleanParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean getBooleanParameter(String key, boolean defaultValue) {
|
||||
String value = getParameter(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean hasParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key) {
|
||||
String value = parameters.get(method + "." + key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = parameters.get(HIDE_KEY_PREFIX + method + "." + key);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return getParameter(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key, String defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getMethodIntParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getMethodIntParameter(String method, String key, int defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
|
||||
if (defaultValue <= 0) {
|
||||
throw new IllegalArgumentException("defaultValue <= 0");
|
||||
}
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
int i = Integer.parseInt(value);
|
||||
if (i > 0) {
|
||||
return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean getMethodBooleanParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
|
||||
String value = getMethodParameter(method, key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public boolean hasMethodParameter(String method, String key) {
|
||||
String value = getMethodParameter(method, key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
return parameters.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return parameters.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.toQueryString(getParameters());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,4 +28,4 @@ public interface Resetable {
|
|||
*/
|
||||
void reset(URL url);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,436 +1,436 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import org.apache.dubbo.common.url.component.ServiceConfigURL;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class URLBuilder extends ServiceConfigURL {
|
||||
private String protocol;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
// by default, host to registry
|
||||
private String host;
|
||||
|
||||
// by default, port to registry
|
||||
private int port;
|
||||
|
||||
private String path;
|
||||
|
||||
private Map<String, String> parameters;
|
||||
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
private Map<String, Map<String, String>> methodParameters;
|
||||
|
||||
public URLBuilder() {
|
||||
protocol = null;
|
||||
username = null;
|
||||
password = null;
|
||||
host = null;
|
||||
port = 0;
|
||||
path = null;
|
||||
parameters = new HashMap<>();
|
||||
attributes = new HashMap<>();
|
||||
methodParameters = new HashMap<>();
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port) {
|
||||
this(protocol, null, null, host, port, null, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String[] pairs) {
|
||||
this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, Map<String, String> parameters) {
|
||||
this(protocol, null, null, host, port, null, parameters);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path) {
|
||||
this(protocol, null, null, host, port, path, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path, String... pairs) {
|
||||
this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path, Map<String, String> parameters) {
|
||||
this(protocol, null, null, host, port, path, parameters);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol,
|
||||
String username,
|
||||
String password,
|
||||
String host,
|
||||
int port,
|
||||
String path,
|
||||
Map<String, String> parameters) {
|
||||
this(protocol, username, password, host, port, path, parameters, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol,
|
||||
String username,
|
||||
String password,
|
||||
String host,
|
||||
int port,
|
||||
String path,
|
||||
Map<String, String> parameters,
|
||||
Map<String, Object> attributes) {
|
||||
this.protocol = protocol;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
this.parameters = parameters != null ? parameters : new HashMap<>();
|
||||
this.attributes = attributes != null ? attributes : new HashMap<>();
|
||||
}
|
||||
|
||||
public static URLBuilder from(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
String username = url.getUsername();
|
||||
String password = url.getPassword();
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
Map<String, String> parameters = new HashMap<>(url.getParameters());
|
||||
Map<String, Object> attributes = new HashMap<>(url.getAttributes());
|
||||
return new URLBuilder(
|
||||
protocol,
|
||||
username,
|
||||
password,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
parameters,
|
||||
attributes);
|
||||
}
|
||||
|
||||
public ServiceConfigURL build() {
|
||||
if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) {
|
||||
throw new IllegalArgumentException("Invalid url, password without username!");
|
||||
}
|
||||
port = Math.max(port, 0);
|
||||
// trim the leading "/"
|
||||
int firstNonSlash = 0;
|
||||
if (path != null) {
|
||||
while (firstNonSlash < path.length() && path.charAt(firstNonSlash) == '/') {
|
||||
firstNonSlash++;
|
||||
}
|
||||
if (firstNonSlash >= path.length()) {
|
||||
path = "";
|
||||
} else if (firstNonSlash > 0) {
|
||||
path = path.substring(firstNonSlash);
|
||||
}
|
||||
}
|
||||
return new ServiceConfigURL(protocol, username, password, host, port, path, parameters, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URLBuilder putAttribute(String key, Object obj) {
|
||||
attributes.put(key, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setHost(String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPort(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setAddress(String address) {
|
||||
int i = address.lastIndexOf(':');
|
||||
String host;
|
||||
int port = this.port;
|
||||
if (i >= 0) {
|
||||
host = address.substring(0, i);
|
||||
port = Integer.parseInt(address.substring(i + 1));
|
||||
} else {
|
||||
host = address;
|
||||
}
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameterAndEncoded(String key, String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, URL.encode(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, boolean value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, char value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, byte value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, short value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, int value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, long value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, float value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, double value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, Enum<?> value) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, Number value) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, CharSequence value) {
|
||||
if (value == null || value.length() == 0) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, String value) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
parameters.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameter(String method, String key, String value) {
|
||||
if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
URL.putMethodParameter(method, key, value, methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameterIfAbsent(String key, String value) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
if (hasParameter(key)) {
|
||||
return this;
|
||||
}
|
||||
parameters.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameterIfAbsent(String method, String key, String value) {
|
||||
if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
if (hasMethodParameter(method, key)) {
|
||||
return this;
|
||||
}
|
||||
URL.putMethodParameter(method, key, value, methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameters(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean hasAndEqual = true;
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
String oldValue = this.parameters.get(entry.getKey());
|
||||
String newValue = entry.getValue();
|
||||
if (!Objects.equals(oldValue, newValue)) {
|
||||
hasAndEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// return immediately if there's no change
|
||||
if (hasAndEqual) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.parameters.putAll(parameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameters(Map<String, Map<String, String>> methodParameters) {
|
||||
if (CollectionUtils.isEmptyMap(methodParameters)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.methodParameters.putAll(methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParametersIfAbsent(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
return this;
|
||||
}
|
||||
for(Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
this.parameters.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameters(String... pairs) {
|
||||
if (pairs == null || pairs.length == 0) {
|
||||
return this;
|
||||
}
|
||||
if (pairs.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Map pairs can not be odd number.");
|
||||
}
|
||||
Map<String, String> map = new HashMap<>();
|
||||
int len = pairs.length / 2;
|
||||
for (int i = 0; i < len; i++) {
|
||||
map.put(pairs[2 * i], pairs[2 * i + 1]);
|
||||
}
|
||||
return addParameters(map);
|
||||
}
|
||||
|
||||
public URLBuilder addParameterString(String query) {
|
||||
if (StringUtils.isEmpty(query)) {
|
||||
return this;
|
||||
}
|
||||
return addParameters(StringUtils.parseQueryString(query));
|
||||
}
|
||||
|
||||
public URLBuilder removeParameter(String key) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
return this;
|
||||
}
|
||||
return removeParameters(key);
|
||||
}
|
||||
|
||||
public URLBuilder removeParameters(Collection<String> keys) {
|
||||
if (CollectionUtils.isEmpty(keys)) {
|
||||
return this;
|
||||
}
|
||||
return removeParameters(keys.toArray(new String[0]));
|
||||
}
|
||||
|
||||
public URLBuilder removeParameters(String... keys) {
|
||||
if (keys == null || keys.length == 0) {
|
||||
return this;
|
||||
}
|
||||
for (String key : keys) {
|
||||
parameters.remove(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder clearParameters() {
|
||||
parameters.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
return StringUtils.isNotEmpty(value);
|
||||
}
|
||||
|
||||
public boolean hasMethodParameter(String method, String key) {
|
||||
if (method == null) {
|
||||
String suffix = "." + key;
|
||||
for (String fullKey : parameters.keySet()) {
|
||||
if (fullKey.endsWith(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (key == null) {
|
||||
String prefix = method + ".";
|
||||
for (String fullKey : parameters.keySet()) {
|
||||
if (fullKey.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
String value = getMethodParameter(method, key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
public String getParameter(String key) {
|
||||
return parameters.get(key);
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key) {
|
||||
Map<String, String> keyMap = methodParameters.get(method);
|
||||
String value = null;
|
||||
if (keyMap != null) {
|
||||
value = keyMap.get(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.common;
|
||||
|
||||
import org.apache.dubbo.common.url.component.ServiceConfigURL;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class URLBuilder extends ServiceConfigURL {
|
||||
private String protocol;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
// by default, host to registry
|
||||
private String host;
|
||||
|
||||
// by default, port to registry
|
||||
private int port;
|
||||
|
||||
private String path;
|
||||
|
||||
private Map<String, String> parameters;
|
||||
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
private Map<String, Map<String, String>> methodParameters;
|
||||
|
||||
public URLBuilder() {
|
||||
protocol = null;
|
||||
username = null;
|
||||
password = null;
|
||||
host = null;
|
||||
port = 0;
|
||||
path = null;
|
||||
parameters = new HashMap<>();
|
||||
attributes = new HashMap<>();
|
||||
methodParameters = new HashMap<>();
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port) {
|
||||
this(protocol, null, null, host, port, null, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String[] pairs) {
|
||||
this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, Map<String, String> parameters) {
|
||||
this(protocol, null, null, host, port, null, parameters);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path) {
|
||||
this(protocol, null, null, host, port, path, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path, String... pairs) {
|
||||
this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol, String host, int port, String path, Map<String, String> parameters) {
|
||||
this(protocol, null, null, host, port, path, parameters);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol,
|
||||
String username,
|
||||
String password,
|
||||
String host,
|
||||
int port,
|
||||
String path,
|
||||
Map<String, String> parameters) {
|
||||
this(protocol, username, password, host, port, path, parameters, null);
|
||||
}
|
||||
|
||||
public URLBuilder(String protocol,
|
||||
String username,
|
||||
String password,
|
||||
String host,
|
||||
int port,
|
||||
String path,
|
||||
Map<String, String> parameters,
|
||||
Map<String, Object> attributes) {
|
||||
this.protocol = protocol;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
this.parameters = parameters != null ? parameters : new HashMap<>();
|
||||
this.attributes = attributes != null ? attributes : new HashMap<>();
|
||||
}
|
||||
|
||||
public static URLBuilder from(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
String username = url.getUsername();
|
||||
String password = url.getPassword();
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
Map<String, String> parameters = new HashMap<>(url.getParameters());
|
||||
Map<String, Object> attributes = new HashMap<>(url.getAttributes());
|
||||
return new URLBuilder(
|
||||
protocol,
|
||||
username,
|
||||
password,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
parameters,
|
||||
attributes);
|
||||
}
|
||||
|
||||
public ServiceConfigURL build() {
|
||||
if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) {
|
||||
throw new IllegalArgumentException("Invalid url, password without username!");
|
||||
}
|
||||
port = Math.max(port, 0);
|
||||
// trim the leading "/"
|
||||
int firstNonSlash = 0;
|
||||
if (path != null) {
|
||||
while (firstNonSlash < path.length() && path.charAt(firstNonSlash) == '/') {
|
||||
firstNonSlash++;
|
||||
}
|
||||
if (firstNonSlash >= path.length()) {
|
||||
path = "";
|
||||
} else if (firstNonSlash > 0) {
|
||||
path = path.substring(firstNonSlash);
|
||||
}
|
||||
}
|
||||
return new ServiceConfigURL(protocol, username, password, host, port, path, parameters, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URLBuilder putAttribute(String key, Object obj) {
|
||||
attributes.put(key, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setHost(String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPort(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setAddress(String address) {
|
||||
int i = address.lastIndexOf(':');
|
||||
String host;
|
||||
int port = this.port;
|
||||
if (i >= 0) {
|
||||
host = address.substring(0, i);
|
||||
port = Integer.parseInt(address.substring(i + 1));
|
||||
} else {
|
||||
host = address;
|
||||
}
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameterAndEncoded(String key, String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, URL.encode(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, boolean value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, char value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, byte value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, short value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, int value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, long value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, float value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, double value) {
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, Enum<?> value) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, Number value) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, CharSequence value) {
|
||||
if (value == null || value.length() == 0) {
|
||||
return this;
|
||||
}
|
||||
return addParameter(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public URLBuilder addParameter(String key, String value) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
parameters.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameter(String method, String key, String value) {
|
||||
if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
URL.putMethodParameter(method, key, value, methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameterIfAbsent(String key, String value) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
if (hasParameter(key)) {
|
||||
return this;
|
||||
}
|
||||
parameters.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameterIfAbsent(String method, String key, String value) {
|
||||
if (StringUtils.isEmpty(method) || StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
if (hasMethodParameter(method, key)) {
|
||||
return this;
|
||||
}
|
||||
URL.putMethodParameter(method, key, value, methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameters(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean hasAndEqual = true;
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
String oldValue = this.parameters.get(entry.getKey());
|
||||
String newValue = entry.getValue();
|
||||
if (!Objects.equals(oldValue, newValue)) {
|
||||
hasAndEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// return immediately if there's no change
|
||||
if (hasAndEqual) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.parameters.putAll(parameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addMethodParameters(Map<String, Map<String, String>> methodParameters) {
|
||||
if (CollectionUtils.isEmptyMap(methodParameters)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.methodParameters.putAll(methodParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParametersIfAbsent(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
return this;
|
||||
}
|
||||
for(Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
this.parameters.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder addParameters(String... pairs) {
|
||||
if (pairs == null || pairs.length == 0) {
|
||||
return this;
|
||||
}
|
||||
if (pairs.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Map pairs can not be odd number.");
|
||||
}
|
||||
Map<String, String> map = new HashMap<>();
|
||||
int len = pairs.length / 2;
|
||||
for (int i = 0; i < len; i++) {
|
||||
map.put(pairs[2 * i], pairs[2 * i + 1]);
|
||||
}
|
||||
return addParameters(map);
|
||||
}
|
||||
|
||||
public URLBuilder addParameterString(String query) {
|
||||
if (StringUtils.isEmpty(query)) {
|
||||
return this;
|
||||
}
|
||||
return addParameters(StringUtils.parseQueryString(query));
|
||||
}
|
||||
|
||||
public URLBuilder removeParameter(String key) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
return this;
|
||||
}
|
||||
return removeParameters(key);
|
||||
}
|
||||
|
||||
public URLBuilder removeParameters(Collection<String> keys) {
|
||||
if (CollectionUtils.isEmpty(keys)) {
|
||||
return this;
|
||||
}
|
||||
return removeParameters(keys.toArray(new String[0]));
|
||||
}
|
||||
|
||||
public URLBuilder removeParameters(String... keys) {
|
||||
if (keys == null || keys.length == 0) {
|
||||
return this;
|
||||
}
|
||||
for (String key : keys) {
|
||||
parameters.remove(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public URLBuilder clearParameters() {
|
||||
parameters.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
return StringUtils.isNotEmpty(value);
|
||||
}
|
||||
|
||||
public boolean hasMethodParameter(String method, String key) {
|
||||
if (method == null) {
|
||||
String suffix = "." + key;
|
||||
for (String fullKey : parameters.keySet()) {
|
||||
if (fullKey.endsWith(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (key == null) {
|
||||
String prefix = method + ".";
|
||||
for (String fullKey : parameters.keySet()) {
|
||||
if (fullKey.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
String value = getMethodParameter(method, key);
|
||||
return value != null && value.length() > 0;
|
||||
}
|
||||
|
||||
public String getParameter(String key) {
|
||||
return parameters.get(key);
|
||||
}
|
||||
|
||||
public String getMethodParameter(String method, String key) {
|
||||
Map<String, String> keyMap = methodParameters.get(method);
|
||||
String value = null;
|
||||
if (keyMap != null) {
|
||||
value = keyMap.get(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,4 +385,4 @@ public final class ClassGenerator {
|
|||
public static interface DC {
|
||||
|
||||
} // dynamic class tag interface.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,227 +1,227 @@
|
|||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Mixin
|
||||
*/
|
||||
public abstract class Mixin {
|
||||
private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();
|
||||
private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);
|
||||
|
||||
protected Mixin() {
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dc delegate class.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?> dc) {
|
||||
return mixin(ics, new Class[]{dc});
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dc delegate class.
|
||||
* @param cl class loader.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl) {
|
||||
return mixin(ics, new Class[]{dc}, cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dcs delegate class array.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs) {
|
||||
return mixin(ics, dcs, ClassUtils.getCallerClassLoader(Mixin.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dcs delegate class array.
|
||||
* @param cl class loader.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl) {
|
||||
assertInterfaceArray(ics);
|
||||
|
||||
long id = MIXIN_CLASS_COUNTER.getAndIncrement();
|
||||
String pkg = null;
|
||||
ClassGenerator ccp = null, ccm = null;
|
||||
try {
|
||||
ccp = ClassGenerator.newInstance(cl);
|
||||
|
||||
// impl constructor
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < dcs.length; i++) {
|
||||
if (!Modifier.isPublic(dcs[i].getModifiers())) {
|
||||
String npkg = dcs[i].getPackage().getName();
|
||||
if (pkg == null) {
|
||||
pkg = npkg;
|
||||
} else {
|
||||
if (!pkg.equals(npkg)) {
|
||||
throw new IllegalArgumentException("non-public interfaces class from different packages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccp.addField("private " + dcs[i].getName() + " d" + i + ";");
|
||||
|
||||
code.append("d").append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");
|
||||
if (MixinAware.class.isAssignableFrom(dcs[i])) {
|
||||
code.append("d").append(i).append(".setMixinInstance(this);\n");
|
||||
}
|
||||
}
|
||||
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{Object[].class}, code.toString());
|
||||
|
||||
// impl methods.
|
||||
Set<String> worked = new HashSet<String>();
|
||||
for (int i = 0; i < ics.length; i++) {
|
||||
if (!Modifier.isPublic(ics[i].getModifiers())) {
|
||||
String npkg = ics[i].getPackage().getName();
|
||||
if (pkg == null) {
|
||||
pkg = npkg;
|
||||
} else {
|
||||
if (!pkg.equals(npkg)) {
|
||||
throw new IllegalArgumentException("non-public delegate class from different packages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccp.addInterface(ics[i]);
|
||||
|
||||
for (Method method : ics[i].getMethods()) {
|
||||
if ("java.lang.Object".equals(method.getDeclaringClass().getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String desc = ReflectUtils.getDesc(method);
|
||||
if (worked.contains(desc)) {
|
||||
continue;
|
||||
}
|
||||
worked.add(desc);
|
||||
|
||||
int ix = findMethod(dcs, desc);
|
||||
if (ix < 0) {
|
||||
throw new RuntimeException("Missing method [" + desc + "] implement.");
|
||||
}
|
||||
|
||||
Class<?> rt = method.getReturnType();
|
||||
String mn = method.getName();
|
||||
if (Void.TYPE.equals(rt)) {
|
||||
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
|
||||
"d" + ix + "." + mn + "($$);");
|
||||
} else {
|
||||
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
|
||||
"return ($r)d" + ix + "." + mn + "($$);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg == null) {
|
||||
pkg = PACKAGE_NAME;
|
||||
}
|
||||
|
||||
// create MixinInstance class.
|
||||
String micn = pkg + ".mixin" + id;
|
||||
ccp.setClassName(micn);
|
||||
ccp.toClass();
|
||||
|
||||
// create Mixin class.
|
||||
String fcn = Mixin.class.getName() + id;
|
||||
ccm = ClassGenerator.newInstance(cl);
|
||||
ccm.setClassName(fcn);
|
||||
ccm.addDefaultConstructor();
|
||||
ccm.setSuperClass(Mixin.class.getName());
|
||||
ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");
|
||||
Class<?> mixin = ccm.toClass();
|
||||
return (Mixin) mixin.getDeclaredConstructor().newInstance();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
} finally {
|
||||
// release ClassGenerator
|
||||
if (ccp != null) {
|
||||
ccp.release();
|
||||
}
|
||||
if (ccm != null) {
|
||||
ccm.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int findMethod(Class<?>[] dcs, String desc) {
|
||||
Class<?> cl;
|
||||
Method[] methods;
|
||||
for (int i = 0; i < dcs.length; i++) {
|
||||
cl = dcs[i];
|
||||
methods = cl.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (desc.equals(ReflectUtils.getDesc(method))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void assertInterfaceArray(Class<?>[] ics) {
|
||||
for (int i = 0; i < ics.length; i++) {
|
||||
if (!ics[i].isInterface()) {
|
||||
throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* new Mixin instance.
|
||||
*
|
||||
* @param ds delegates instance.
|
||||
* @return instance.
|
||||
*/
|
||||
abstract public Object newInstance(Object[] ds);
|
||||
|
||||
public static interface MixinAware {
|
||||
void setMixinInstance(Object instance);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.common.utils.ReflectUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Mixin
|
||||
*/
|
||||
public abstract class Mixin {
|
||||
private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();
|
||||
private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);
|
||||
|
||||
protected Mixin() {
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dc delegate class.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?> dc) {
|
||||
return mixin(ics, new Class[]{dc});
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dc delegate class.
|
||||
* @param cl class loader.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl) {
|
||||
return mixin(ics, new Class[]{dc}, cl);
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dcs delegate class array.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs) {
|
||||
return mixin(ics, dcs, ClassUtils.getCallerClassLoader(Mixin.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* mixin interface and delegates.
|
||||
* all class must be public.
|
||||
*
|
||||
* @param ics interface class array.
|
||||
* @param dcs delegate class array.
|
||||
* @param cl class loader.
|
||||
* @return Mixin instance.
|
||||
*/
|
||||
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl) {
|
||||
assertInterfaceArray(ics);
|
||||
|
||||
long id = MIXIN_CLASS_COUNTER.getAndIncrement();
|
||||
String pkg = null;
|
||||
ClassGenerator ccp = null, ccm = null;
|
||||
try {
|
||||
ccp = ClassGenerator.newInstance(cl);
|
||||
|
||||
// impl constructor
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < dcs.length; i++) {
|
||||
if (!Modifier.isPublic(dcs[i].getModifiers())) {
|
||||
String npkg = dcs[i].getPackage().getName();
|
||||
if (pkg == null) {
|
||||
pkg = npkg;
|
||||
} else {
|
||||
if (!pkg.equals(npkg)) {
|
||||
throw new IllegalArgumentException("non-public interfaces class from different packages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccp.addField("private " + dcs[i].getName() + " d" + i + ";");
|
||||
|
||||
code.append("d").append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");
|
||||
if (MixinAware.class.isAssignableFrom(dcs[i])) {
|
||||
code.append("d").append(i).append(".setMixinInstance(this);\n");
|
||||
}
|
||||
}
|
||||
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{Object[].class}, code.toString());
|
||||
|
||||
// impl methods.
|
||||
Set<String> worked = new HashSet<String>();
|
||||
for (int i = 0; i < ics.length; i++) {
|
||||
if (!Modifier.isPublic(ics[i].getModifiers())) {
|
||||
String npkg = ics[i].getPackage().getName();
|
||||
if (pkg == null) {
|
||||
pkg = npkg;
|
||||
} else {
|
||||
if (!pkg.equals(npkg)) {
|
||||
throw new IllegalArgumentException("non-public delegate class from different packages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccp.addInterface(ics[i]);
|
||||
|
||||
for (Method method : ics[i].getMethods()) {
|
||||
if ("java.lang.Object".equals(method.getDeclaringClass().getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String desc = ReflectUtils.getDesc(method);
|
||||
if (worked.contains(desc)) {
|
||||
continue;
|
||||
}
|
||||
worked.add(desc);
|
||||
|
||||
int ix = findMethod(dcs, desc);
|
||||
if (ix < 0) {
|
||||
throw new RuntimeException("Missing method [" + desc + "] implement.");
|
||||
}
|
||||
|
||||
Class<?> rt = method.getReturnType();
|
||||
String mn = method.getName();
|
||||
if (Void.TYPE.equals(rt)) {
|
||||
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
|
||||
"d" + ix + "." + mn + "($$);");
|
||||
} else {
|
||||
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
|
||||
"return ($r)d" + ix + "." + mn + "($$);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg == null) {
|
||||
pkg = PACKAGE_NAME;
|
||||
}
|
||||
|
||||
// create MixinInstance class.
|
||||
String micn = pkg + ".mixin" + id;
|
||||
ccp.setClassName(micn);
|
||||
ccp.toClass();
|
||||
|
||||
// create Mixin class.
|
||||
String fcn = Mixin.class.getName() + id;
|
||||
ccm = ClassGenerator.newInstance(cl);
|
||||
ccm.setClassName(fcn);
|
||||
ccm.addDefaultConstructor();
|
||||
ccm.setSuperClass(Mixin.class.getName());
|
||||
ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");
|
||||
Class<?> mixin = ccm.toClass();
|
||||
return (Mixin) mixin.getDeclaredConstructor().newInstance();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
} finally {
|
||||
// release ClassGenerator
|
||||
if (ccp != null) {
|
||||
ccp.release();
|
||||
}
|
||||
if (ccm != null) {
|
||||
ccm.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int findMethod(Class<?>[] dcs, String desc) {
|
||||
Class<?> cl;
|
||||
Method[] methods;
|
||||
for (int i = 0; i < dcs.length; i++) {
|
||||
cl = dcs[i];
|
||||
methods = cl.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (desc.equals(ReflectUtils.getDesc(method))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void assertInterfaceArray(Class<?>[] ics) {
|
||||
for (int i = 0; i < ics.length; i++) {
|
||||
if (!ics[i].isInterface()) {
|
||||
throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* new Mixin instance.
|
||||
*
|
||||
* @param ds delegates instance.
|
||||
* @return instance.
|
||||
*/
|
||||
abstract public Object newInstance(Object[] ds);
|
||||
|
||||
public static interface MixinAware {
|
||||
void setMixinInstance(Object instance);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
/**
|
||||
* NoSuchMethodException.
|
||||
*/
|
||||
|
||||
public class NoSuchMethodException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2725364246023268766L;
|
||||
|
||||
public NoSuchMethodException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoSuchMethodException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
/**
|
||||
* NoSuchMethodException.
|
||||
*/
|
||||
|
||||
public class NoSuchMethodException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2725364246023268766L;
|
||||
|
||||
public NoSuchMethodException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoSuchMethodException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
/**
|
||||
* NoSuchPropertyException.
|
||||
*/
|
||||
|
||||
public class NoSuchPropertyException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2725364246023268766L;
|
||||
|
||||
public NoSuchPropertyException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoSuchPropertyException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.common.bytecode;
|
||||
|
||||
/**
|
||||
* NoSuchPropertyException.
|
||||
*/
|
||||
|
||||
public class NoSuchPropertyException extends RuntimeException {
|
||||
private static final long serialVersionUID = -2725364246023268766L;
|
||||
|
||||
public NoSuchPropertyException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoSuchPropertyException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue