Merge branch 'apache-3.0' into apache-3.1
# Conflicts:
# dubbo-dependencies-bom/pom.xml
# dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
# dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
# pom.xml
diff --git a/.codecov.yml b/.codecov.yml
index 4b49c86..0b498ec 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,3 +1,18 @@
+# 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.
+#
coverage:
status:
# pull-requests only
@@ -8,4 +23,4 @@
- "dubbo-demo/.*"
- "dubbo-common/src/main/java/org/apache/dubbo/common/json/*.java" # internal JSON impl is deprecate, ignore test coverage for them
- "dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/AnnotationBean.java" # Deprecated
- - "dubbo-rpc/dubbo-rpc-thrift/.*"
\ No newline at end of file
+ - "dubbo-rpc/dubbo-rpc-thrift/.*"
diff --git a/.github/workflows/build-and-test-3.yml b/.github/workflows/build-and-test-3.1.yml
similarity index 80%
rename from .github/workflows/build-and-test-3.yml
rename to .github/workflows/build-and-test-3.1.yml
index 3a9b654..f8c4370 100644
--- a/.github/workflows/build-and-test-3.yml
+++ b/.github/workflows/build-and-test-3.1.yml
@@ -1,7 +1,10 @@
-name: Build and Test For Dubbo 3
+name: Build and Test For Dubbo 3.1
on: [push, pull_request, workflow_dispatch]
+permissions:
+ contents: read
+
env:
FORK_COUNT: 2
FAIL_FAST: 0
@@ -15,8 +18,16 @@
'
jobs:
+ license:
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v2
+ - name: Check License
+ uses: apache/skywalking-eyes@main
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-source:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
outputs:
version: ${{ steps.dubbo-version.outputs.version }}
steps:
@@ -71,7 +82,7 @@
strategy:
fail-fast: false
matrix:
- os: [ ubuntu-18.04, windows-2019 ]
+ os: [ ubuntu-20.04, windows-2019 ]
jdk: [ 8, 11, 17 ]
env:
DISABLE_FILE_SYSTEM_TEST: true
@@ -123,7 +134,7 @@
strategy:
fail-fast: false
matrix:
- os: [ ubuntu-18.04, windows-2019 ]
+ os: [ ubuntu-20.04, windows-2019 ]
jdk: [ 8, 11, 17 ]
env:
DISABLE_FILE_SYSTEM_TEST: true
@@ -151,8 +162,45 @@
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v1
+ unit-test-fastjson2:
+ needs: [build-source, unit-test-prepare]
+ name: "Unit Test On ${{ matrix.os }} (JDK: ${{ matrix.jdk }}, Serialization: fastjson2)"
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ ubuntu-20.04, windows-2019 ]
+ jdk: [ 8, 11, 17 ]
+ env:
+ DISABLE_FILE_SYSTEM_TEST: true
+ DUBBO_DEFAULT_SERIALIZATION: fastjson2
+ MAVEN_SUREFIRE_ADD_OPENS: true
+ steps:
+ - uses: actions/checkout@v2
+ - name: "Set up JDK ${{ matrix.jdk }}"
+ uses: actions/setup-java@v1
+ with:
+ java-version: ${{ matrix.jdk }}
+ - uses: actions/cache@v2
+ name: "Cache local Maven repository"
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: "Test with Maven with Integration Tests"
+ timeout-minutes: 70
+ if: ${{ startsWith( matrix.os, 'ubuntu') }}
+ run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast 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 -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+ - name: "Test with Maven without Integration Tests"
+ timeout-minutes: 90
+ if: ${{ startsWith( matrix.os, 'windows') }}
+ run: ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast 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" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+ - name: "Upload coverage to Codecov"
+ uses: codecov/codecov-action@v1
+
integration-test-prepare:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
env:
JOB_COUNT: 3
steps:
@@ -171,8 +219,8 @@
integration-test-job:
needs: [build-source, integration-test-prepare]
- name: "Integration Test on ubuntu-18.04 (JobId: ${{matrix.job_id}})"
- runs-on: ubuntu-18.04
+ name: "Integration Test on ubuntu-20.04 (JobId: ${{matrix.job_id}})"
+ runs-on: ubuntu-20.04
timeout-minutes: 30
env:
JAVA_VER: 8
@@ -227,7 +275,7 @@
integration-test-result:
needs: [integration-test-job]
if: always()
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
env:
JAVA_VER: 8
steps:
diff --git a/.licenserc.yaml b/.licenserc.yaml
new file mode 100644
index 0000000..00b7286
--- /dev/null
+++ b/.licenserc.yaml
@@ -0,0 +1,82 @@
+header:
+ license:
+ spdx-id: Apache-2.0
+ content: |
+ 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.
+
+ paths-ignore:
+ - '**/*.versionsBackup'
+ - '**/.idea/'
+ - '**/*.iml'
+ - '**/.settings/*'
+ - '**/.classpath'
+ - '**/.project'
+ - '**/target/**'
+ - '**/generated/**'
+ - '**/*.log'
+ - '**/codestyle/*'
+ - '**/resources/META-INF/**'
+ - '**/resources/mockito-extensions/**'
+ - '**/*.proto'
+ - '**/*.cache'
+ - '**/*.txt'
+ - '**/*.load'
+ - '**/*.flex'
+ - '**/*.fc'
+ - '**/*.javascript'
+ - '**/*.properties'
+ - '**/*.thrift'
+ - '**/*.sh'
+ - '**/*.bat'
+ - '**/*.md'
+ - '**/*.svg'
+ - '**/*.png'
+ - '**/*.json'
+ - '**/*.conf'
+ - '**/*.ftl'
+ - '**/*.tpl'
+ - '**/*.factories'
+ - '**/*.handlers'
+ - '**/*.schemas'
+ - '**/*.nojekyll'
+ - '.git/'
+ - '.github/**'
+ - '**/.gitignore'
+ - '**/.helmignore'
+ - '.repository/'
+ - 'compiler/**'
+ - '.gitmodules'
+ - '.mvn'
+ - 'mvnw'
+ - 'mvnw.cmd'
+ - 'LICENSE'
+ - 'NOTICE'
+ - 'CNAME'
+ - 'Jenkinsfile'
+ - '**/vendor/**'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timeout.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/Timer.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/timer/TimerTask.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/utils/CIDRUtils.java'
+ - 'dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java'
+ - 'dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java'
+
+ comment: on-failure
+
+ license-location-threshold: 130
diff --git a/.travis.yml b/.travis.yml
index 6b59c76..a7b6c63 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,18 @@
+# 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.
+#
sudo: false # faster builds
os: linux
dist: focal
diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
index c20fb74..f7235cf 100644
--- a/dubbo-cluster/pom.xml
+++ b/dubbo-cluster/pom.xml
@@ -58,6 +58,12 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-test-check</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index f3b4f5d..64246b9 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
@@ -43,13 +43,15 @@
import java.util.List;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_STOP;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
/**
* Router chain
*/
public class RouterChain<T> {
- private static final Logger logger = LoggerFactory.getLogger(RouterChain.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RouterChain.class);
/**
* full list of addresses from registry, classified by method name.
@@ -241,6 +243,7 @@
RouterSnapshotNode<T> commonRouterNode = new RouterSnapshotNode<T>("CommonRouter", resultInvokers.clone());
parentNode.appendNode(commonRouterNode);
List<Invoker<T>> commonRouterResult = resultInvokers;
+
// 2. route common router
for (Router router : routers) {
// Copy resultInvokers to a arrayList. BitList not support
@@ -296,7 +299,7 @@
if (routerSnapshotSwitcher.isEnable()) {
routerSnapshotSwitcher.setSnapshot(message);
}
- logger.warn(message);
+ logger.warn(CLUSTER_NO_VALID_PROVIDER,"No provider available after route for the service","",message);
}
} else {
if (logger.isInfoEnabled()) {
@@ -344,7 +347,7 @@
try {
router.stop();
} catch (Exception e) {
- logger.error("Error trying to stop router " + router.getClass(), e);
+ logger.error(CLUSTER_FAILED_STOP,"route stop failed","","Error trying to stop router " + router.getClass(),e);
}
}
routers = Collections.emptyList();
@@ -354,7 +357,7 @@
try {
router.stop();
} catch (Exception e) {
- logger.error("Error trying to stop stateRouter " + router.getClass(), e);
+ logger.error(CLUSTER_FAILED_STOP,"StateRouter stop failed","","Error trying to stop StateRouter " + router.getClass(),e);
}
}
stateRouters = Collections.emptyList();
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index c190fa5..11e7e82 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
@@ -61,6 +61,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.RECONNECT_TASK_PERIOD;
import static org.apache.dubbo.common.constants.CommonConstants.RECONNECT_TASK_TRY_COUNT;
import static org.apache.dubbo.common.constants.CommonConstants.REGISTER_IP_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
@@ -71,7 +72,7 @@
public abstract class AbstractDirectory<T> implements Directory<T> {
// logger
- private static final Logger logger = LoggerFactory.getLogger(AbstractDirectory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractDirectory.class);
private final URL url;
@@ -193,7 +194,10 @@
List<Invoker<T>> routedResult = doList(availableInvokers, invocation);
if (routedResult.isEmpty()) {
- logger.warn("No provider available after connectivity filter for the service " + getConsumerUrl().getServiceKey()
+ // 2-2 - No provider available.
+
+ logger.warn(CLUSTER_NO_VALID_PROVIDER, "provider server or registry center crashed", "",
+ "No provider available after connectivity filter for the service " + getConsumerUrl().getServiceKey()
+ " All validInvokers' size: " + validInvokers.size()
+ " All routed invokers' size: " + routedResult.size()
+ " All invokers' size: " + invokers.size()
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
index 58a2462..106e997 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.Invocation;
@@ -28,11 +28,13 @@
import java.util.List;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION;
+
/**
* StaticDirectory
*/
public class StaticDirectory<T> extends AbstractDirectory<T> {
- private static final Logger logger = LoggerFactory.getLogger(StaticDirectory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(StaticDirectory.class);
public StaticDirectory(List<Invoker<T>> invokers) {
this(null, invokers, null);
@@ -108,7 +110,7 @@
List<Invoker<T>> finalInvokers = routerChain.route(getConsumerUrl(), invokers, invocation);
return finalInvokers == null ? BitList.emptyList() : finalInvokers;
} catch (Throwable t) {
- logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+ logger.error(CLUSTER_FAILED_SITE_SELECTION,"Failed to execute router","","Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(),t);
return BitList.emptyList();
}
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
index 15be300..22a22cd 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.extension.ExtensionDirector;
-import org.apache.dubbo.common.extension.support.MultiInstanceActivateComparator;
+import org.apache.dubbo.common.extension.support.ActivateComparator;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invoker;
@@ -113,7 +113,7 @@
}
private <T> List<T> sortingAndDeduplication(List<T> filters, List<ExtensionDirector> directors) {
- Map<Class<?>, T> filtersSet = new TreeMap<>(new MultiInstanceActivateComparator(directors));
+ Map<Class<?>, T> filtersSet = new TreeMap<>(new ActivateComparator(directors));
for (T filter : filters) {
filtersSet.putIfAbsent(filter.getClass(), filter);
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
index 74c54ce..f8c2089 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
@@ -43,8 +43,9 @@
/**
* Select one invoker between a list using a random criteria
- * @param invokers List of possible invokers
- * @param url URL
+ *
+ * @param invokers List of possible invokers
+ * @param url URL
* @param invocation Invocation
* @param <T>
* @return The selected invoker
@@ -54,7 +55,7 @@
// Number of invokers
int length = invokers.size();
- if (!needWeightLoadBalance(invokers,invocation)){
+ if (!needWeightLoadBalance(invokers, invocation)) {
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
@@ -89,8 +90,7 @@
}
private <T> boolean needWeightLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
-
- Invoker invoker = invokers.get(0);
+ Invoker<T> invoker = invokers.get(0);
URL invokerUrl = invoker.getUrl();
if (invoker instanceof ClusterInvoker) {
invokerUrl = ((ClusterInvoker<?>) invoker).getRegistryUrl();
@@ -99,22 +99,15 @@
// Multiple registry scenario, load balance among multiple registries.
if (REGISTRY_SERVICE_REFERENCE_PATH.equals(invokerUrl.getServiceInterface())) {
String weight = invokerUrl.getParameter(WEIGHT_KEY);
- if (StringUtils.isNotEmpty(weight)) {
- return true;
- }
+ return StringUtils.isNotEmpty(weight);
} else {
String weight = invokerUrl.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY);
if (StringUtils.isNotEmpty(weight)) {
return true;
- }else {
+ } else {
String timeStamp = invoker.getUrl().getParameter(TIMESTAMP_KEY);
- if (StringUtils.isNotEmpty(timeStamp)) {
- return true;
- }
+ return StringUtils.isNotEmpty(timeStamp);
}
}
- return false;
}
-
-
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MergerFactory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MergerFactory.java
index 09a9cfc..038e38f 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MergerFactory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/merger/MergerFactory.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.rpc.cluster.merger;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.TypeUtils;
@@ -31,9 +31,11 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_LOAD_MERGER;
+
public class MergerFactory implements ScopeModelAware {
- private static final Logger logger = LoggerFactory.getLogger(MergerFactory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MergerFactory.class);
private ConcurrentMap<Class<?>, Merger<?>> MERGER_CACHE = new ConcurrentHashMap<Class<?>, Merger<?>>();
private ScopeModel scopeModel;
@@ -73,7 +75,7 @@
Merger m = scopeModel.getExtensionLoader(Merger.class).getExtension(name);
Class<?> actualTypeArg = getActualTypeArgument(m.getClass());
if (actualTypeArg == null) {
- logger.warn("Failed to get actual type argument from merger " + m.getClass().getName());
+ logger.warn(CLUSTER_FAILED_LOAD_MERGER,"load merger config failed","","Failed to get actual type argument from merger " + m.getClass().getName());
continue;
}
MERGER_CACHE.putIfAbsent(actualTypeArg, m);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouter.java
index 92cee81..be4774e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionStateRouter.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
@@ -45,6 +45,8 @@
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.common.constants.LoggerCodeConstants.CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER;
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.RULE_KEY;
@@ -54,14 +56,14 @@
* 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}
+ * For 2.7.x and later, please refer to {@link org.apache.dubbo.rpc.cluster.router.condition.config.ServiceStateRouter}
* and {@link AppStateRouter}
* refer to https://dubbo.apache.org/zh/docs/v2.7/user/examples/routing-rule/ .
*/
public class ConditionStateRouter<T> extends AbstractStateRouter<T> {
public static final String NAME = "condition";
- private static final Logger logger = LoggerFactory.getLogger(ConditionStateRouter.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractStateRouter.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;
@@ -202,8 +204,7 @@
return invokers;
}
if (thenCondition == null) {
- logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
- if (needToPrintMessage) {
+ logger.warn(CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY,"condition state router thenCondition is empty","","The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey()); if (needToPrintMessage) {
messageHolder.set("Empty return. Reason: ThenCondition is empty.");
}
return BitList.emptyList();
@@ -217,15 +218,14 @@
}
return result;
} else if (this.isForce()) {
- logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
-
+ logger.warn(CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY,"execute condition state router result list is empty. and force=true","","The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
if (needToPrintMessage) {
messageHolder.set("Empty return. Reason: Empty result from condition and condition is force.");
}
return result;
}
} catch (Throwable t) {
- logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
+ logger.error(CLUSTER_FAILED_EXEC_CONDITION_ROUTER,"execute condition state router exception","","Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(),t);
}
if (needToPrintMessage) {
messageHolder.set("Directly return. Reason: Error occurred ( or result is empty ).");
@@ -325,7 +325,7 @@
return true;
}
} catch (Exception e) {
- logger.warn("Arguments match failed, matchPair[]" + matchPair + "] invocation[" + invocation + "]", e);
+ logger.warn(CLUSTER_FAILED_EXEC_CONDITION_ROUTER,"condition state router arguments match failed","","Arguments match failed, matchPair[]" + matchPair + "] invocation[" + invocation + "]",e);
}
return false;
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
index c295a66..2348a3e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableStateRouter.java
@@ -21,7 +21,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
@@ -41,6 +41,8 @@
import java.util.List;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RULE_PARSING;
+
/**
* Abstract router which listens to dynamic configuration
*/
@@ -48,7 +50,7 @@
public static final String NAME = "LISTENABLE_ROUTER";
private static final String RULE_SUFFIX = ".condition-router";
- private static final Logger logger = LoggerFactory.getLogger(ListenableStateRouter.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenableStateRouter.class);
private volatile ConditionRouterRule routerRule;
private volatile List<ConditionStateRouter<T>> conditionRouters = Collections.emptyList();
private String ruleKey;
@@ -75,8 +77,8 @@
routerRule = ConditionRuleParser.parse(event.getContent());
generateConditions(routerRule);
} catch (Exception e) {
- logger.error("Failed to parse the raw condition rule and it will not take effect, please check " +
- "if the condition rule matches with the template, the raw rule is:\n " + event.getContent(), e);
+ logger.error(CLUSTER_FAILED_RULE_PARSING,"Failed to parse the raw condition rule","","Failed to parse the raw condition rule and it will not take effect, please check " +
+ "if the condition rule matches with the template, the raw rule is:\n " + event.getContent(),e);
}
}
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
index ceb237e..b6332e4 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleDispatcher;
@@ -36,6 +36,7 @@
import java.util.List;
import java.util.Map;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.METADATA_KEY;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.NAME_KEY;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.STANDARD_ROUTER_KEY;
@@ -43,7 +44,7 @@
public class MeshAppRuleListener implements ConfigurationListener {
- public static final Logger logger = LoggerFactory.getLogger(MeshAppRuleListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshAppRuleListener.class);
private final MeshRuleDispatcher meshRuleDispatcher;
@@ -78,17 +79,17 @@
if (ruleType != null) {
groupMap.computeIfAbsent(ruleType, (k)-> new LinkedList<>()).add(resultMap);
} else {
- logger.error("Unable to get rule type from raw rule. " +
+ logger.error(CLUSTER_FAILED_RECEIVE_RULE,"receive mesh app route rule is invalid","","Unable to get rule type from raw rule. " +
"Probably the metadata.name is absent. App Name: " + appName + " RawRule: " + configInfo);
}
} else {
- logger.error("Rule format is unacceptable. App Name: " + appName + " RawRule: " + configInfo);
+ logger.error(CLUSTER_FAILED_RECEIVE_RULE,"receive mesh app route rule is invalid","","Rule format is unacceptable. App Name: " + appName + " RawRule: " + configInfo);
}
}
ruleMapHolder = groupMap;
} catch (Exception e) {
- logger.error("[MeshAppRule] parse failed: " + configInfo, e);
+ logger.error(CLUSTER_FAILED_RECEIVE_RULE,"failed to receive mesh app route rule","","[MeshAppRule] parse failed: " + configInfo,e);
}
if (ruleMapHolder != null) {
meshRuleDispatcher.post(ruleMapHolder);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
index e7d46b5..ac49475 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
@@ -18,7 +18,7 @@
package org.apache.dubbo.rpc.cluster.router.mesh.route;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
@@ -29,11 +29,12 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX;
public class MeshRuleManager {
- public static final Logger logger = LoggerFactory.getLogger(MeshRuleManager.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleManager.class);
private final ConcurrentHashMap<String, MeshAppRuleListener> APP_RULE_LISTENERS = new ConcurrentHashMap<>();
@@ -63,7 +64,7 @@
meshAppRuleListener.receiveConfigInfo(rawConfig);
}
} catch (Throwable throwable) {
- logger.error("get MeshRuleManager app rule failed.", throwable);
+ logger.error(CLUSTER_FAILED_RECEIVE_RULE,"failed to get mesh app route rule","","get MeshRuleManager app rule failed.",throwable);
}
ruleRepository.addListener(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
index 105332e..0d3d8b5 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.rpc.cluster.router.mesh.route;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
@@ -51,6 +51,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.DESTINATION_RULE_KEY;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.KIND_KEY;
@@ -58,7 +59,7 @@
public abstract class MeshRuleRouter<T> extends AbstractStateRouter<T> implements MeshRuleListener {
- public static final Logger logger = LoggerFactory.getLogger(MeshRuleRouter.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleRouter.class);
private final Map<String, String> sourcesLabels;
private volatile BitList<Invoker<T>> invokerList = BitList.emptyList();
@@ -308,7 +309,7 @@
appToVDGroup.put(appName, vsDestinationGroup);
}
} catch (Throwable t) {
- logger.error("Error occurred when parsing rule component.", t);
+ logger.error(CLUSTER_FAILED_RECEIVE_RULE,"failed to parse mesh route rule","","Error occurred when parsing rule component.",t);
}
computeSubset(appToVDGroup);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java
index 3a3c240..03f68dd 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.rpc.cluster.router.mesh.util;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
@@ -27,9 +27,11 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_RULE_LISTENER;
+
public class MeshRuleDispatcher {
- public static final Logger logger = LoggerFactory.getLogger(MeshRuleDispatcher.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleDispatcher.class);
private final String appName;
private final Map<String, Set<MeshRuleListener>> listenerMap = new ConcurrentHashMap<>();
@@ -55,7 +57,7 @@
listener.onRuleChange(appName, entry.getValue());
}
} else {
- logger.warn("Receive rule but none of listener has been registered. Maybe type not matched. Rule Type: " + ruleType);
+ logger.warn(CLUSTER_NO_RULE_LISTENER,"Receive mesh rule but none of listener has been registered","","Receive rule but none of listener has been registered. Maybe type not matched. Rule Type: " + ruleType);
}
}
// clear rule listener not being notified in this time
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouter.java
index 3e2d570..174ecb9 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptStateRouter.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Holder;
import org.apache.dubbo.common.utils.StringUtils;
@@ -49,6 +49,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_SCRIPT_EXCEPTION;
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.RULE_KEY;
@@ -61,7 +62,7 @@
public class ScriptStateRouter<T> extends AbstractStateRouter<T> {
public static final String NAME = "SCRIPT_ROUTER";
private static final int SCRIPT_ROUTER_DEFAULT_PRIORITY = 0;
- private static final Logger logger = LoggerFactory.getLogger(ScriptStateRouter.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ScriptStateRouter.class);
private static final Map<String, ScriptEngine> ENGINES = new ConcurrentHashMap<>();
@@ -92,8 +93,8 @@
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);
+ logger.error(CLUSTER_SCRIPT_EXCEPTION,"script route rule invalid","","script route error, rule has been ignored. rule: " + rule +
+ ", url: " + RpcContext.getServiceContext().getUrl(),e);
}
}
@@ -136,8 +137,8 @@
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);
+ logger.error(CLUSTER_SCRIPT_EXCEPTION,"Scriptrouter exec script error","","Script route error, rule has been ignored. rule: " + rule + ", method:" +
+ invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(),e);
return invokers;
}
}, accessControlContext));
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java
index 5b57b21..20aee26 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagStateRouter.java
@@ -21,7 +21,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Holder;
@@ -41,6 +41,8 @@
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_EMPTY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TAG_ROUTE_INVALID;
import static org.apache.dubbo.rpc.Constants.FORCE_USE_TAG;
/**
@@ -48,7 +50,7 @@
*/
public class TagStateRouter<T> extends AbstractStateRouter<T> implements ConfigurationListener {
public static final String NAME = "TAG_ROUTER";
- private static final Logger logger = LoggerFactory.getLogger(TagStateRouter.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TagStateRouter.class);
private static final String RULE_SUFFIX = ".tag-router";
private TagRouterRule tagRouterRule;
@@ -72,8 +74,8 @@
this.tagRouterRule = TagRuleParser.parse(event.getContent());
}
} catch (Exception e) {
- logger.error("Failed to parse the raw tag router rule and it will not take effect, please check if the " +
- "rule matches with the template, the raw rule is:\n ", e);
+ logger.error(CLUSTER_TAG_ROUTE_INVALID,"Failed to parse the raw tag router rule","","Failed to parse the raw tag router rule and it will not take effect, please check if the " +
+ "rule matches with the template, the raw rule is:\n ",e);
}
}
@@ -235,7 +237,7 @@
return true;
}
} catch (Exception e) {
- logger.error("The format of ip address is invalid in tag route. Address :" + address, e);
+ logger.error(CLUSTER_TAG_ROUTE_INVALID,"tag route address is invalid","","The format of ip address is invalid in tag route. Address :" + address,e);
}
}
return false;
@@ -256,7 +258,7 @@
String providerApplication = url.getRemoteApplication();
if (StringUtils.isEmpty(providerApplication)) {
- logger.error("TagRouter must getConfig from or subscribe to a specific application, but the application " +
+ logger.error(CLUSTER_TAG_ROUTE_EMPTY,"tag router get providerApplication is empty","","TagRouter must getConfig from or subscribe to a specific application, but the application " +
"in this TagRouter is not specified.");
return;
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
index 5406ac6..c4435a4 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.profiler.ProfilerSwitch;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -49,6 +49,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION;
import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.RESELECT_COUNT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RESELECT_INVOKERS;
import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_AVAILABLE_CHECK_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_STICKY_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_CLUSTER_AVAILABLE_CHECK;
@@ -59,7 +60,7 @@
*/
public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(AbstractClusterInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractClusterInvoker.class);
protected Directory<T> directory;
@@ -213,11 +214,11 @@
//Avoid collision
invoker = invokers.get((index + 1) % invokers.size());
} catch (Exception e) {
- logger.warn(e.getMessage() + " may because invokers list dynamic change, ignore.", e);
+ logger.warn(CLUSTER_FAILED_RESELECT_INVOKERS,"select invokers exception","",e.getMessage() + " may because invokers list dynamic change, ignore.",e);
}
}
} catch (Throwable t) {
- logger.error("cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url", t);
+ logger.error(CLUSTER_FAILED_RESELECT_INVOKERS,"failed to reselect invokers","","cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url",t);
}
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
index 6d03e36..02b8f6d 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
@@ -32,12 +32,14 @@
import java.util.HashMap;
import java.util.List;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_ERROR_RESPONSE;
+
/**
* BroadcastClusterInvoker
*/
public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(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;
@@ -81,7 +83,7 @@
Throwable resultException = result.getException();
if (null != resultException) {
exception = getRpcException(result.getException());
- logger.warn(exception.getMessage(), exception);
+ logger.warn(CLUSTER_ERROR_RESPONSE,"provider return error response","",exception.getMessage(),exception);
failIndex++;
if (failIndex == failThresholdIndex) {
break;
@@ -90,7 +92,7 @@
}
} catch (Throwable e) {
exception = getRpcException(e);
- logger.warn(exception.getMessage(), exception);
+ logger.warn(CLUSTER_ERROR_RESPONSE,"provider return error response","",exception.getMessage(),exception);
failIndex++;
if (failIndex == failThresholdIndex) {
break;
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
index 120f594..608b00e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.timer.HashedWheelTimer;
import org.apache.dubbo.common.timer.Timeout;
@@ -39,6 +39,8 @@
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.common.constants.LoggerCodeConstants.CLUSTER_FAILED_INVOKE_SERVICE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_TIMER_RETRY_FAILED;
import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_FAILBACK_TASKS;
import static org.apache.dubbo.rpc.cluster.Constants.FAIL_BACK_TASKS_KEY;
@@ -50,7 +52,7 @@
*/
public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailbackClusterInvoker.class);
private static final long RETRY_FAILED_PERIOD = 5;
@@ -93,7 +95,7 @@
try {
failTimer.newTimeout(retryTimerTask, RETRY_FAILED_PERIOD, TimeUnit.SECONDS);
} catch (Throwable e) {
- logger.error("Failback background works error, invocation->" + invocation + ", exception: " + e.getMessage());
+ logger.error(CLUSTER_TIMER_RETRY_FAILED,"add newTimeout exception","","Failback background works error, invocation->" + invocation + ", exception: " + e.getMessage(),e);
}
}
@@ -108,8 +110,8 @@
// Then the serviceContext will be cleared after the call is completed.
return invokeWithContextAsync(invoker, invocation, consumerUrl);
} catch (Throwable e) {
- logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
- + e.getMessage() + ", ", e);
+ logger.error(CLUSTER_FAILED_INVOKE_SERVICE,"Failback to invoke method and start to retries","","Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
+ + e.getMessage() + ", ",e);
if (retries > 0) {
addFailed(loadbalance, invocation, invokers, invoker, consumerUrl);
}
@@ -166,9 +168,9 @@
lastInvoker = retryInvoker;
invokeWithContextAsync(retryInvoker, invocation, consumerUrl);
} catch (Throwable e) {
- logger.error("Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.", e);
+ logger.error(CLUSTER_FAILED_INVOKE_SERVICE,"Failed retry to invoke method","","Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.",e);
if ((++retriedTimes) >= retries) {
- logger.error("Failed retry times exceed threshold (" + retries + "), We have to abandon, invocation->" + invocation);
+ logger.error(CLUSTER_FAILED_INVOKE_SERVICE,"Failed retry to invoke method and retry times exceed threshold","","Failed retry times exceed threshold (" + retries + "), We have to abandon, invocation->" + invocation,e);
} else {
rePut(timeout);
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
index 8781dc7..1858374 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
@@ -17,7 +17,7 @@
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.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Invocation;
@@ -36,6 +36,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES;
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_MULTIPLE_RETRIES;
/**
* When invoke fails, log the initial error and retry other invokers (retry n times, which means at most n different invokers will be invoked)
@@ -46,7 +47,7 @@
*/
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailoverClusterInvoker.class);
public FailoverClusterInvoker(Directory<T> directory) {
super(directory);
@@ -79,15 +80,15 @@
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);
+ logger.warn(CLUSTER_FAILED_MULTIPLE_RETRIES,"failed to retry do invoke","","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);
}
success = true;
return result;
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
index edc9ec5..c09205b 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
@@ -16,7 +16,7 @@
*/
package org.apache.dubbo.rpc.cluster.support;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.AsyncRpcResult;
import org.apache.dubbo.rpc.Invocation;
@@ -28,6 +28,8 @@
import java.util.List;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_ERROR_RESPONSE;
+
/**
* 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
@@ -36,7 +38,7 @@
*
*/
public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailsafeClusterInvoker.class);
public FailsafeClusterInvoker(Directory<T> directory) {
super(directory);
@@ -49,7 +51,7 @@
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
return invokeWithContext(invoker, invocation);
} catch (Throwable e) {
- logger.error("Failsafe ignore exception: " + e.getMessage(), e);
+ logger.error(CLUSTER_ERROR_RESPONSE,"Failsafe for provider exception","","Failsafe ignore exception: " + e.getMessage(),e);
return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
}
}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
index e6147da..a935fd9 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
@@ -18,7 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
@@ -39,13 +39,14 @@
import java.util.List;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_MOCK_REQUEST;
import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.FORCE_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 static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MockClusterInvoker.class);
private static final boolean setFutureWhenSync = Boolean.parseBoolean(System.getProperty(CommonConstants.SET_FUTURE_IN_SYNC_MODE, "true"));
private final Directory<T> directory;
@@ -102,7 +103,7 @@
result = this.invoker.invoke(invocation);
} else if (value.startsWith(FORCE_KEY)) {
if (logger.isWarnEnabled()) {
- logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
+ logger.warn(CLUSTER_FAILED_MOCK_REQUEST,"force mock","","force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
}
//force:direct mock
result = doMockInvoke(invocation, null);
@@ -127,7 +128,7 @@
}
if (logger.isWarnEnabled()) {
- logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(), e);
+ logger.warn(CLUSTER_FAILED_MOCK_REQUEST,"failed to mock invoke","","fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(),e);
}
result = doMockInvoke(invocation, e);
}
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveBalanceTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveBalanceTest.java
index 89dc66b..0e172e4 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveBalanceTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LeastActiveBalanceTest.java
@@ -3,7 +3,7 @@
* 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 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
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
index 0331873..c96c0fc 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
@@ -3,7 +3,7 @@
* 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 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
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalanceTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalanceTest.java
index f18a07d..7bd0a76 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalanceTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/RandomLoadBalanceTest.java
@@ -3,7 +3,7 @@
* 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 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
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalanceTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalanceTest.java
index aebaf00..9d43790 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalanceTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/ShortestResponseLoadBalanceTest.java
@@ -3,7 +3,7 @@
* 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 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
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/ProtocolServiceKey.java b/dubbo-common/src/main/java/org/apache/dubbo/common/ProtocolServiceKey.java
new file mode 100644
index 0000000..0cc8b20
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/ProtocolServiceKey.java
@@ -0,0 +1,124 @@
+/*
+ * 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.constants.CommonConstants;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.util.Objects;
+
+public class ProtocolServiceKey extends ServiceKey {
+ private final String protocol;
+
+ public ProtocolServiceKey(String interfaceName, String version, String group, String protocol) {
+ super(interfaceName, version, group);
+ this.protocol = protocol;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public String getServiceKeyString() {
+ return super.toString();
+ }
+
+ public boolean isSameWith(ProtocolServiceKey protocolServiceKey) {
+ // interface version group should be the same
+ if (!super.equals(protocolServiceKey)) {
+ return false;
+ }
+
+ // origin protocol is *, can not match any protocol
+ if (CommonConstants.ANY_VALUE.equals(protocol)) {
+ return false;
+ }
+
+ // origin protocol is null, can match any protocol
+ if (StringUtils.isEmpty(protocol) || StringUtils.isEmpty(protocolServiceKey.getProtocol())) {
+ return true;
+ }
+
+ // origin protocol is not *, match itself
+ return Objects.equals(protocol, protocolServiceKey.getProtocol());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ ProtocolServiceKey that = (ProtocolServiceKey) o;
+ return Objects.equals(protocol, that.protocol);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), protocol);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + CommonConstants.GROUP_CHAR_SEPARATOR + protocol;
+ }
+
+ public static class Matcher {
+ public static boolean isMatch(ProtocolServiceKey rule, ProtocolServiceKey target) {
+ // 1. 2. 3. match interface / version / group
+ if (!ServiceKey.Matcher.isMatch(rule, target)) {
+ return false;
+ }
+
+ // 4.match protocol
+ // 4.1. if rule group is *, match all
+ if (!CommonConstants.ANY_VALUE.equals(rule.getProtocol())) {
+ // 4.2. if rule protocol is null, match all
+ if (StringUtils.isNotEmpty(rule.getProtocol())) {
+ // 4.3. if rule protocol contains ',', split and match each
+ if (rule.getProtocol().contains(CommonConstants.COMMA_SEPARATOR)) {
+ String[] protocols = rule.getProtocol().split("\\" +CommonConstants.COMMA_SEPARATOR, -1);
+ boolean match = false;
+ for (String protocol : protocols) {
+ protocol = protocol.trim();
+ if (StringUtils.isEmpty(protocol) && StringUtils.isEmpty(target.getProtocol())) {
+ match = true;
+ break;
+ } else if (protocol.equals(target.getProtocol())) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ // 4.3. if rule protocol is not contains ',', match directly
+ else if (!Objects.equals(rule.getProtocol(), target.getProtocol())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/ServiceKey.java b/dubbo-common/src/main/java/org/apache/dubbo/common/ServiceKey.java
new file mode 100644
index 0000000..c8d8f84
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/ServiceKey.java
@@ -0,0 +1,146 @@
+/*
+ * 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.constants.CommonConstants;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.util.Objects;
+
+public class ServiceKey {
+ private final String interfaceName;
+ private final String group;
+ private final String version;
+
+ public ServiceKey(String interfaceName, String version, String group) {
+ this.interfaceName = interfaceName;
+ this.group = group;
+ this.version = version;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ServiceKey that = (ServiceKey) o;
+ return Objects.equals(interfaceName, that.interfaceName) && Objects.equals(group, that.group) && Objects.equals(version, that.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(interfaceName, group, version);
+ }
+
+ @Override
+ public String toString() {
+ return BaseServiceMetadata.buildServiceKey(interfaceName, group, version);
+ }
+
+
+ public static class Matcher {
+ public static boolean isMatch(ServiceKey rule, ServiceKey target) {
+ // 1. match interface (accurate match)
+ if (!Objects.equals(rule.getInterfaceName(), target.getInterfaceName())) {
+ return false;
+ }
+
+ // 2. match version (accurate match)
+ // 2.1. if rule version is *, match all
+ if (!CommonConstants.ANY_VALUE.equals(rule.getVersion())) {
+ // 2.2. if rule version is null, target version should be null
+ if (StringUtils.isEmpty(rule.getVersion()) && !StringUtils.isEmpty(target.getVersion())) {
+ return false;
+ }
+ if (!StringUtils.isEmpty(rule.getVersion())) {
+ // 2.3. if rule version contains ',', split and match each
+ if (rule.getVersion().contains(CommonConstants.COMMA_SEPARATOR)) {
+ String[] versions = rule.getVersion().split("\\" +CommonConstants.COMMA_SEPARATOR, -1);
+ boolean match = false;
+ for (String version : versions) {
+ version = version.trim();
+ if (StringUtils.isEmpty(version) && StringUtils.isEmpty(target.getVersion())) {
+ match = true;
+ break;
+ } else if (version.equals(target.getVersion())) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ // 2.4. if rule version is not contains ',', match directly
+ else if (!Objects.equals(rule.getVersion(), target.getVersion())) {
+ return false;
+ }
+ }
+ }
+
+ // 3. match group (wildcard match)
+ // 3.1. if rule group is *, match all
+ if (!CommonConstants.ANY_VALUE.equals(rule.getGroup())) {
+ // 3.2. if rule group is null, target group should be null
+ if (StringUtils.isEmpty(rule.getGroup()) && !StringUtils.isEmpty(target.getGroup())) {
+ return false;
+ }
+ if (!StringUtils.isEmpty(rule.getGroup())) {
+ // 3.3. if rule group contains ',', split and match each
+ if (rule.getGroup().contains(CommonConstants.COMMA_SEPARATOR)) {
+ String[] groups = rule.getGroup().split("\\" +CommonConstants.COMMA_SEPARATOR, -1);
+ boolean match = false;
+ for (String group : groups) {
+ group = group.trim();
+ if (StringUtils.isEmpty(group) && StringUtils.isEmpty(target.getGroup())) {
+ match = true;
+ break;
+ } else if (group.equals(target.getGroup())) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ // 3.4. if rule group is not contains ',', match directly
+ else if (!Objects.equals(rule.getGroup(), target.getGroup())) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index 29d9df9..cb98b66 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -146,7 +146,12 @@
public URL(URLAddress urlAddress, URLParam urlParam, Map<String, Object> attributes) {
this.urlAddress = urlAddress;
this.urlParam = null == urlParam ? URLParam.parse(new HashMap<>()) : urlParam;
- this.attributes = (attributes != null ? attributes.isEmpty() ? null : attributes : null);
+
+ if (attributes != null && !attributes.isEmpty()) {
+ this.attributes = attributes;
+ } else {
+ this.attributes = null;
+ }
}
public URL(String protocol, String host, int port) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java
index 9d66014..ba78c46 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java
@@ -79,7 +79,7 @@
* a value greater than {@code 0} if {@code version1 > version2}
*/
public static int compare(String version1, String version2) {
- return Integer.compare (getIntVersion(version1), getIntVersion(version2));
+ return Integer.compare(getIntVersion(version1), getIntVersion(version2));
}
/**
@@ -89,10 +89,8 @@
if (StringUtils.isEmpty(version)) {
return false;
}
- if (getIntVersion(version) >= 2070000) {
- return true;
- }
- return false;
+
+ return getIntVersion(version) >= 2070000;
}
/**
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java
index b4ca8b4..9d73843 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java
@@ -33,7 +33,7 @@
*/
public class InstantiationStrategy {
- private ScopeModelAccessor scopeModelAccessor;
+ private final ScopeModelAccessor scopeModelAccessor;
public InstantiationStrategy() {
this(null);
@@ -43,6 +43,7 @@
this.scopeModelAccessor = scopeModelAccessor;
}
+ @SuppressWarnings("unchecked")
public <T> T instantiate(Class<T> type) throws ReflectiveOperationException {
// should not use default constructor directly, maybe also has another constructor matched scope model arguments
@@ -55,7 +56,7 @@
}
// 2. use matched constructor if found
- List<Constructor> matchedConstructors = new ArrayList<>();
+ List<Constructor<?>> matchedConstructors = new ArrayList<>();
Constructor<?>[] declaredConstructors = type.getConstructors();
for (Constructor<?> constructor : declaredConstructors) {
if (isMatched(constructor)) {
@@ -71,7 +72,7 @@
// 1. the only matched constructor with parameters
// 2. default constructor if absent
- Constructor targetConstructor;
+ Constructor<?> targetConstructor;
if (matchedConstructors.size() > 1) {
throw new IllegalArgumentException("Expect only one but found " +
matchedConstructors.size() + " matched constructors for type: " + type.getName() +
@@ -85,7 +86,7 @@
}
// create instance with arguments
- Class[] parameterTypes = targetConstructor.getParameterTypes();
+ Class<?>[] parameterTypes = targetConstructor.getParameterTypes();
Object[] args = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
args[i] = getArgumentValueForType(parameterTypes[i]);
@@ -106,7 +107,7 @@
return ScopeModel.class.isAssignableFrom(parameterType);
}
- private Object getArgumentValueForType(Class parameterType) {
+ private Object getArgumentValueForType(Class<?> parameterType) {
// get scope mode value
if (scopeModelAccessor != null) {
if (parameterType == ScopeModel.class) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
index a8ecc80..84601ef 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
@@ -16,7 +16,7 @@
*/
package org.apache.dubbo.common.cache;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -30,18 +30,24 @@
import java.io.Writer;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_PATH_INACCESSIBLE;
+
/**
* Local file interaction class that can back different caches.
* <p>
* All items in local file are of human friendly format.
*/
public class FileCacheStore {
- private static final Logger logger = LoggerFactory.getLogger(FileCacheStore.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FileCacheStore.class);
private String cacheFilePath;
private File cacheFile;
@@ -71,10 +77,14 @@
}
if (count > entrySize) {
- logger.warn("Cache file was truncated for exceeding the maximum entry size " + entrySize);
+ logger.warn(COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED, "mis-configuration of system properties",
+ "Check Java system property 'dubbo.mapping.cache.entrySize' and 'dubbo.meta.cache.entrySize'.",
+ "Cache file was truncated for exceeding the maximum entry size: " + entrySize);
}
} catch (IOException e) {
- logger.warn("Load cache failed ", e);
+ logger.warn(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "",
+ "Load cache failed ", e);
+
throw e;
}
return properties;
@@ -88,6 +98,9 @@
directoryLock.channel().close();
deleteFile(lockFile);
} catch (IOException e) {
+ logger.error(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "",
+ "Failed to release cache path's lock file:" + lockFile, e);
+
throw new RuntimeException("Failed to release cache path's lock file:" + lockFile, e);
}
}
@@ -102,29 +115,41 @@
new LimitedLengthBufferedWriter(
new OutputStreamWriter(
new FileOutputStream(cacheFile, false), StandardCharsets.UTF_8), maxFileSize)) {
+
bw.write("#" + comment);
bw.newLine();
bw.write("#" + new Date());
bw.newLine();
+
for (Map.Entry<String, String> e : properties.entrySet()) {
String key = e.getKey();
String val = e.getValue();
bw.write(key + "=" + val);
bw.newLine();
}
+
bw.flush();
+
long remainSize = bw.getRemainSize();
if (remainSize < 0) {
- logger.info("Cache file was truncated for exceeding the maximum file size " + maxFileSize + " byte. Exceeded by " + (-remainSize) + " byte.");
+ logger.warn(COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED, "mis-configuration of system properties",
+ "Check Java system property 'dubbo.mapping.cache.maxFileSize' and 'dubbo.meta.cache.maxFileSize'.",
+ "Cache file was truncated for exceeding the maximum file size " + maxFileSize + " byte. Exceeded by " + (-remainSize) + " byte.");
}
} catch (IOException e) {
- logger.warn("Update cache error.");
+ logger.warn(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "",
+ "Update cache error.", e);
}
}
private static void deleteFile(File f) {
- if (!f.delete()) {
- logger.debug("Failed to delete file " + f.getAbsolutePath());
+
+ Path pathOfFile = f.toPath();
+
+ try {
+ Files.delete(pathOfFile);
+ } catch (IOException ioException) {
+ logger.debug("Failed to delete file " + f.getAbsolutePath(), ioException);
}
}
@@ -179,6 +204,9 @@
}
}
+ /**
+ * An empty (or fallback) implementation of FileCacheStore. Used when cache file creation failed.
+ */
protected static class Empty extends FileCacheStore {
private Empty(String cacheFilePath) {
@@ -196,9 +224,13 @@
@Override
public void refreshCache(Map<String, String> properties, String comment, long maxFileSize) {
+ // No-op.
}
}
+ /**
+ * A BufferedWriter which limits the length (in bytes). When limit exceed, this writer stops writing.
+ */
private static class LimitedLengthBufferedWriter extends BufferedWriter {
private long remainSize;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java
index f4375c7..a49f19a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java
@@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.dubbo.common.cache;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import java.io.File;
@@ -25,22 +26,34 @@
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_CACHE_PATH_INACCESSIBLE;
+
/**
* ClassLoader Level static share.
* Prevent FileCacheStore being operated in multi-application
*/
-public class FileCacheStoreFactory {
- private final static Logger logger = LoggerFactory.getLogger(FileCacheStoreFactory.class);
+public final class FileCacheStoreFactory {
+
+ /**
+ * Forbids instantiation.
+ */
+ private FileCacheStoreFactory() {
+ throw new UnsupportedOperationException("No instance of 'FileCacheStoreFactory' for you! ");
+ }
+
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FileCacheStoreFactory.class);
private static final Map<String, FileCacheStore> cacheMap = new ConcurrentHashMap<>();
private static final String SUFFIX = ".dubbo.cache";
- private static final char ESCAPE = '%';
+ private static final char ESCAPE_MARK = '%';
private static final Set<Character> LEGAL_CHARACTERS = Collections.unmodifiableSet(new HashSet<Character>(){{
// - $ . _ 0-9 a-z A-Z
add('-');
@@ -64,6 +77,7 @@
public static FileCacheStore getInstance(String basePath, String cacheName, boolean enableFileCache) {
if (basePath == null) {
+ // default case: ~/.dubbo
basePath = System.getProperty("user.home") + File.separator + ".dubbo";
}
if (basePath.endsWith(File.separator)) {
@@ -71,9 +85,20 @@
}
File candidate = new File(basePath);
+ Path path = candidate.toPath();
+
// ensure cache store path exists
- if (!candidate.isDirectory() && !candidate.mkdirs()) {
- throw new RuntimeException("Cache store path can't be created: " + candidate);
+ if (!candidate.isDirectory()) {
+ try {
+ Files.createDirectories(path);
+ } catch (IOException e) {
+ // 0-3 - cache path inaccessible
+
+ logger.error(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "",
+ "Cache store path can't be created: ", e);
+
+ throw new RuntimeException("Cache store path can't be created: " + candidate, e);
+ }
}
cacheName = safeName(cacheName);
@@ -83,7 +108,7 @@
String cacheFilePath = basePath + File.separator + cacheName;
- return cacheMap.computeIfAbsent(cacheFilePath, (k) -> getFile(k, enableFileCache));
+ return cacheMap.computeIfAbsent(cacheFilePath, k -> getFile(k, enableFileCache));
}
/**
@@ -100,7 +125,7 @@
if (LEGAL_CHARACTERS.contains(c)) {
sb.append(c);
} else {
- sb.append(ESCAPE);
+ sb.append(ESCAPE_MARK);
sb.append(String.format("%04x", (int) c));
}
}
@@ -114,22 +139,29 @@
* @return a file object
*/
private static FileCacheStore getFile(String name, boolean enableFileCache) {
- if(!enableFileCache) {
+ if (!enableFileCache) {
return FileCacheStore.Empty.getInstance(name);
}
+
try {
FileCacheStore.Builder builder = FileCacheStore.newBuilder();
tryFileLock(builder, name);
File file = new File(name);
+
if (!file.exists()) {
- file.createNewFile();
+ Path pathObjectOfFile = file.toPath();
+ Files.createFile(pathObjectOfFile);
}
builder.cacheFilePath(name)
.cacheFile(file);
+
return builder.build();
} catch (Throwable t) {
- logger.info("Failed to create file store cache. Local file cache will be disabled. Cache file name: " + name, t);
+
+ logger.warn(COMMON_CACHE_PATH_INACCESSIBLE, "inaccessible of cache path", "",
+ "Failed to create file store cache. Local file cache will be disabled. Cache file name: " + name, t);
+
return FileCacheStore.Empty.getInstance(name);
}
}
@@ -153,13 +185,13 @@
}
if (dirLock == null) {
- throw new PathNotExclusiveException(fileName + " is not exclusive.");
+ throw new PathNotExclusiveException(fileName + " is not exclusive. Maybe multiple Dubbo instances are using the same folder.");
}
builder.directoryLock(dirLock).lockFile(lockFile);
}
- protected static void removeCache(String cacheFileName) {
+ static void removeCache(String cacheFileName) {
cacheMap.remove(cacheFileName);
}
@@ -167,7 +199,7 @@
* for unit test only
*/
@Deprecated
- protected static Map<String, FileCacheStore> getCacheMap() {
+ static Map<String, FileCacheStore> getCacheMap() {
return cacheMap;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java
index 443690b..cebeaf7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java
@@ -55,7 +55,7 @@
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
+ private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
private final ClassLoaderImpl classLoader;
@@ -83,7 +83,7 @@
&& (!"sun.misc.Launcher$AppClassLoader".equals(loader.getClass().getName()))) {
try {
URLClassLoader urlClassLoader = (URLClassLoader) loader;
- List<File> files = new ArrayList<File>();
+ List<File> files = new ArrayList<>();
for (URL url : urlClassLoader.getURLs()) {
files.add(new File(url.getFile()));
}
@@ -172,7 +172,7 @@
private final ClassLoaderImpl classLoader;
- private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();
+ private final Map<URI, JavaFileObject> fileObjects = new HashMap<>();
public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {
super(fileManager);
@@ -224,7 +224,7 @@
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();
+ ArrayList<JavaFileObject> files = new ArrayList<>();
if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
for (JavaFileObject file : fileObjects.values()) {
@@ -252,7 +252,7 @@
private static final class ClassLoaderImpl extends ClassLoader {
- private final Map<String, JavaFileObject> classes = new HashMap<String, JavaFileObject>();
+ private final Map<String, JavaFileObject> classes = new HashMap<>();
ClassLoaderImpl(final ClassLoader parentClassLoader) {
super(parentClassLoader);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/RejectException.java b/dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/RejectException.java
index 1b7537b..a7c2177 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/RejectException.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/concurrent/RejectException.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.threadpool.MemorySafeLinkedBlockingQueue;
/**
- * Exception thrown by an {@link MemorySafeLinkedBlockingQueue} when a element cannot be accepted.
+ * Exception thrown by an {@link MemorySafeLinkedBlockingQueue} when an element cannot be accepted.
*/
public class RejectException extends RuntimeException {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java
index 5b8531b..5c3988a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java
@@ -16,14 +16,21 @@
*/
package org.apache.dubbo.common.config;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
import java.util.NoSuchElementException;
import static org.apache.dubbo.common.config.ConfigurationUtils.isEmptyValue;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_MISSPELLING;
/**
* Configuration interface, to fetch the value for the specified key.
*/
public interface Configuration {
+
+ ErrorTypeAwareLogger interfaceLevelLogger = LoggerFactory.getErrorTypeAwareLogger(Configuration.class);
+
/**
* Get a string associated with the given configuration key.
*
@@ -66,6 +73,11 @@
try {
return convert(Integer.class, key, defaultValue);
} catch (NumberFormatException e) {
+ // 0-2 Property type mismatch.
+ interfaceLevelLogger.error(COMMON_PROPERTY_MISSPELLING, "typo in property value",
+ "This property requires an integer value.",
+ "Actual Class: " + getClass().getName(), e);
+
throw new IllegalStateException('\'' + key + "' doesn't map to a Integer object", e);
}
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
index acbbf36..6daeb9f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.dubbo.common.config;
import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
@@ -47,7 +48,15 @@
/**
* Utilities for manipulating configurations from different sources
*/
-public class ConfigurationUtils {
+public final class ConfigurationUtils {
+
+ /**
+ * Forbids instantiation.
+ */
+ private ConfigurationUtils() {
+ throw new UnsupportedOperationException("No instance of 'ConfigurationUtils' for you! ");
+ }
+
private static final Logger logger = LoggerFactory.getLogger(ConfigurationUtils.class);
private static final List<String> securityKey;
@@ -75,13 +84,12 @@
*
* @return
*/
-
public static Configuration getEnvConfiguration(ScopeModel scopeModel) {
return getScopeModelOrDefaultApplicationModel(scopeModel).getModelEnvironment().getEnvironmentConfiguration();
}
/**
- * Used to get an composite property value.
+ * Used to get a composite property value.
* <p>
* Also see {@link Environment#getConfiguration()}
*
@@ -231,7 +239,11 @@
}
if (CollectionUtils.isNotEmptyMap(configMap)) {
- for(Map.Entry<String, V> entry : configMap.entrySet()) {
+ Map<String,V> copy ;
+ synchronized (configMap){
+ copy = new HashMap<>(configMap);
+ }
+ for(Map.Entry<String, V> entry : copy.entrySet()) {
String key = entry.getKey();
V val = entry.getValue();
if (StringUtils.startsWithIgnoreCase(key, prefix)
@@ -268,7 +280,11 @@
if (!prefix.endsWith(".")) {
prefix += ".";
}
- for (Map.Entry<String, V> entry : configMap.entrySet()) {
+ Map<String,V> copy ;
+ synchronized (configMap){
+ copy = new HashMap<>(configMap);
+ }
+ for (Map.Entry<String, V> entry : copy.entrySet()) {
String key = entry.getKey();
if (StringUtils.startsWithIgnoreCase(key, prefix)
&& key.length() > prefix.length()
@@ -303,7 +319,11 @@
}
Set<String> ids = new LinkedHashSet<>();
for (Map<String, V> configMap : configMaps) {
- for (Map.Entry<String, V> entry : configMap.entrySet()) {
+ Map<String,V> copy ;
+ synchronized (configMap){
+ copy = new HashMap<>(configMap);
+ }
+ for (Map.Entry<String, V> entry : copy.entrySet()) {
String key = entry.getKey();
V val = entry.getValue();
if (StringUtils.startsWithIgnoreCase(key, prefix)
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
index 0cc6af2..164372d 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.common.config.configcenter;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.StringUtils;
@@ -75,10 +75,10 @@
/**
* Logger
*/
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
/**
- * The thread pool for workers who executes the tasks
+ * The thread pool for workers who execute the tasks
*/
private final ThreadPoolExecutor workersThreadPool;
@@ -86,12 +86,12 @@
private final long timeout;
- public AbstractDynamicConfiguration(URL url) {
+ protected AbstractDynamicConfiguration(URL url) {
this(getThreadPoolPrefixName(url), getThreadPoolSize(url), getThreadPoolKeepAliveTime(url), getGroup(url),
getTimeout(url));
}
- public AbstractDynamicConfiguration(String threadPoolPrefixName,
+ protected AbstractDynamicConfiguration(String threadPoolPrefixName,
int threadPoolSize,
long keepAliveTime,
String group,
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfiguration.java
index fcd08eb..fc505d5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/DynamicConfiguration.java
@@ -23,7 +23,7 @@
/**
* Dynamic Configuration
* <br/>
- * From the use scenario internally inside framework, there're mainly three kinds of methods:
+ * From the use scenario internally inside framework, there are mainly three kinds of methods:
* <ol>
* <li>{@link #getProperties(String, String, long)}, get configuration file from Config Center at start up.</li>
* <li>{@link #addListener(String, String, ConfigurationListener)}/ {@link #removeListener(String, String, ConfigurationListener)}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
index cd43c54..2612c7b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
@@ -287,24 +287,32 @@
WatchKey watchKey = null;
try {
watchKey = watchService.take();
- if (watchKey.isValid()) {
- for (WatchEvent event : watchKey.pollEvents()) {
- WatchEvent.Kind kind = event.kind();
- // configChangeType's key to match WatchEvent's Kind
- ConfigChangeType configChangeType = CONFIG_CHANGE_TYPES_MAP.get(kind.name());
- if (configChangeType != null) {
- Path configDirectoryPath = (Path) watchKey.watchable();
- Path currentPath = (Path) event.context();
- Path configFilePath = configDirectoryPath.resolve(currentPath);
- File configDirectory = configDirectoryPath.toFile();
- executeMutually(configDirectory, () -> {
- fireConfigChangeEvent(configDirectory, configFilePath.toFile(), configChangeType);
- signalConfigDirectory(configDirectory);
- return null;
- });
- }
- }
+
+ if (!watchKey.isValid()) {
+ continue;
}
+
+ for (WatchEvent event : watchKey.pollEvents()) {
+ WatchEvent.Kind kind = event.kind();
+ // configChangeType's key to match WatchEvent's Kind
+ ConfigChangeType configChangeType = CONFIG_CHANGE_TYPES_MAP.get(kind.name());
+
+ if (configChangeType == null) {
+ continue;
+ }
+
+ Path configDirectoryPath = (Path) watchKey.watchable();
+ Path currentPath = (Path) event.context();
+ Path configFilePath = configDirectoryPath.resolve(currentPath);
+ File configDirectory = configDirectoryPath.toFile();
+
+ executeMutually(configDirectory, () -> {
+ fireConfigChangeEvent(configDirectory, configFilePath.toFile(), configChangeType);
+ signalConfigDirectory(configDirectory);
+ return null;
+ });
+ }
+
} catch (Exception e) {
return;
} finally {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 2939eb3..bd4e256 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -392,6 +392,8 @@
String PROTOCOL_SERVER = "server";
+ String IPV6_KEY = "ipv6";
+
/**
* The parameter key for the class path of the ServiceNameMapping {@link Properties} file
*
@@ -438,6 +440,8 @@
String DEFAULT_VERSION = "0.0.0";
+ String CLASS_DESERIALIZE_OPEN_CHECK = "dubbo.security.serialize.openCheckClass";
+
String ROUTER_KEY = "router";
String EXPORT_ASYNC_KEY = "export-async";
@@ -546,4 +550,31 @@
String PREFER_JSON_FRAMEWORK_NAME = "dubbo.json-framework.prefer";
+ /**
+ * @since 3.1.0
+ */
+ String MESH_ENABLE = "mesh-enable";
+
+ /**
+ * @since 3.1.0
+ */
+ Integer DEFAULT_MESH_PORT = 80;
+
+ /**
+ * @since 3.1.0
+ */
+ String SVC = ".svc.";
+
+ /**
+ * Domain name suffix used inside k8s.
+ *
+ * @since 3.1.0
+ */
+ String DEFAULT_CLUSTER_DOMAIN = "cluster.local";
+
+ /**
+ * @since 3.1.0
+ */
+ String UNLOAD_CLUSTER_RELATED = "unloadClusterRelated";
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java
index c0e9285..daeff6c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoadbalanceRules.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.common.constants;
/**
- * constant for Loadbalance strategy
+ * constant for Load-balance strategy
*/
public interface LoadbalanceRules {
@@ -27,7 +27,7 @@
String RANDOM = "random";
/**
- * Round robin load balance.
+ * Round-robin load balance.
**/
String ROUND_ROBIN = "roundrobin";
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
new file mode 100644
index 0000000..ae41ddf
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
@@ -0,0 +1,167 @@
+/*
+ * 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.constants;
+
+/**
+ * constants for logger
+ */
+public interface LoggerCodeConstants {
+
+ // common module 0-1 ~ 0-4
+ String COMMON_THREAD_POOL_EXHAUSTED = "0-1";
+
+ String COMMON_PROPERTY_MISSPELLING = "0-2";
+
+ String COMMON_CACHE_PATH_INACCESSIBLE = "0-3";
+
+ String COMMON_CACHE_MAX_FILE_SIZE_LIMIT_EXCEED = "0-4";
+
+
+ String COMMON_CACHE_MAX_ENTRY_COUNT_LIMIT_EXCEED = "0-5";
+
+ // registry module
+ String REGISTRY_ADDRESS_INVALID = "1-1";
+
+ String REGISTRY_ABSENCE = "1-2";
+
+ String REGISTRY_FAILED_URL_EVICTING = "1-3";
+
+ String REGISTRY_EMPTY_ADDRESS = "1-4";
+
+ String REGISTRY_NO_PARAMETERS_URL = "1-5";
+
+ String REGISTRY_FAILED_CLEAR_CACHED_URLS = "1-6";
+
+ String REGISTRY_FAILED_NOTIFY_EVENT = "1-7";
+
+ String REGISTRY_FAILED_DESTROY_UNREGISTER_URL = "1-8";
+
+ String REGISTRY_FAILED_READ_WRITE_CACHE_FILE = "1-9";
+
+ String REGISTRY_FAILED_DELETE_LOCKFILE = "1-10";
+
+ String REGISTRY_FAILED_CREATE_INSTANCE = "1-11";
+
+ String REGISTRY_FAILED_FETCH_INSTANCE = "1-12";
+
+ String REGISTRY_EXECUTE_RETRYING_TASK = "1-13";
+
+ String REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG = "1-14";
+
+ String REGISTRY_FAILED_DESTROY_SERVICE = "1-15";
+
+ String REGISTRY_UNSUPPORTED_CATEGORY = "1-16";
+
+ String REGISTRY_FAILED_REFRESH_ADDRESS = "1-17";
+
+ String REGISTRY_MISSING_METADATA_CONFIG_PORT = "1-18";
+
+ // cluster module 2-1 ~ 2-18
+ String CLUSTER_FAILED_SITE_SELECTION = "2-1";
+
+ String CLUSTER_NO_VALID_PROVIDER = "2-2";
+
+ String CLUSTER_FAILED_STOP = "2-3";
+
+ String CLUSTER_FAILED_LOAD_MERGER = "2-4";
+
+ String CLUSTER_FAILED_RESELECT_INVOKERS = "2-5";
+
+ String CLUSTER_CONDITIONAL_ROUTE_LIST_EMPTY = "2-6";
+
+ String CLUSTER_FAILED_EXEC_CONDITION_ROUTER = "2-7";
+
+ String CLUSTER_ERROR_RESPONSE = "2-8";
+
+ String CLUSTER_TIMER_RETRY_FAILED = "2-9";
+
+ String CLUSTER_FAILED_INVOKE_SERVICE = "2-10";
+
+ String CLUSTER_TAG_ROUTE_INVALID = "2-11";
+
+ String CLUSTER_TAG_ROUTE_EMPTY = "2-12";
+
+ String CLUSTER_FAILED_RECEIVE_RULE = "2-13";
+
+ String CLUSTER_SCRIPT_EXCEPTION = "2-14";
+
+ String CLUSTER_FAILED_RULE_PARSING = "2-15";
+
+ String CLUSTER_FAILED_MULTIPLE_RETRIES = "2-16";
+
+ String CLUSTER_FAILED_MOCK_REQUEST = "2-17";
+
+ String CLUSTER_NO_RULE_LISTENER = "2-18";
+
+ // proxy module 3-1
+ String PROXY_FAILED_CONVERT_URL = "3-1";
+
+ // protocol module 4-1 ~ 4-3
+ String PROTOCOL_UNSUPPORTED = "4-1";
+
+ String PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER = "4-2";
+
+ String PROTOCOL_FAILED_REFER_INVOKER = "4-3";
+
+ // config module 5-1 ~ 5-20
+ String CONFIG_FAILED_CONNECT_REGISTRY = "5-1";
+
+ String CONFIG_FAILED_SHUTDOWN_HOOK = "5-2";
+
+ String CONFIG_FAILED_DESTROY_INVOKER = "5-3";
+
+ String CONFIG_NO_METHOD_FOUND = "5-4";
+
+ String CONFIG_FAILED_LOAD_ENV_VARIABLE = "5-5";
+
+ String CONFIG_PROPERTY_CONFLICT = "5-6";
+
+ String CONFIG_UNEXPORT_ERROR = "5-7";
+
+ String CONFIG_USE_RANDOM_PORT = "5-8";
+
+ String CONFIG_FAILED_EXPORT_SERVICE = "5-9";
+
+ String CONFIG_SERVER_DISCONNECTED = "5-10";
+
+ String CONFIG_REGISTER_INSTANCE_ERROR = "5-11";
+
+ String CONFIG_REFRESH_INSTANCE_ERROR = "5-12";
+
+ String CONFIG_UNABLE_DESTROY_MODEL = "5-13";
+
+ String CONFIG_FAILED_START_MODEL = "5-14";
+
+ String CONFIG_FAILED_REFERENCE_MODEL = "5-15";
+
+ String CONFIG_FAILED_FIND_PROTOCOL = "5-16";
+
+ String CONFIG_PARAMETER_FORMAT_ERROR = "5-17";
+
+ String CONFIG_FAILED_NOTIFY_EVENT = "5-18";
+
+ String CONFIG_ZOOKEEPER_SERVER_ERROR = "5-19";
+
+ String CONFIG_STOP_DUBBO_ERROR = "5-20";
+
+ // transport module 6-1 ~ 6-2
+ String TRANSPORT_FAILED_CONNECT_PROVIDER = "6-1";
+
+ String TRANSPORT_CLIENT_CONNECT_TIMEOUT = "6-2";
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
index a8fd9a9..04bb400 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
@@ -20,9 +20,59 @@
String PROTOCOL_PROMETHEUS = "prometheus";
- String AGGREGATION_ENABLE = "aggregation.enable";
+ String TAG_IP = "ip";
- String AGGREGATION_BUCKET_NUM = "aggregation.bucket.num";
+ String TAG_HOSTNAME = "hostname";
- String AGGREGATION_TIME_WINDOW_SECONDS = "aggregation.time.window.seconds";
+ String TAG_APPLICATION_NAME = "application.name";
+
+ String TAG_INTERFACE_KEY = "interface";
+
+ String TAG_METHOD_KEY = "method";
+
+ String TAG_GROUP_KEY = "group";
+
+ String TAG_VERSION_KEY = "version";
+
+ String ENABLE_JVM_METRICS_KEY = "enable.jvm.metrics";
+
+ String AGGREGATION_COLLECTOR_KEY = "aggregation";
+
+ String AGGREGATION_ENABLED_KEY = "aggregation.enabled";
+
+ String AGGREGATION_BUCKET_NUM_KEY = "aggregation.bucket.num";
+
+ String AGGREGATION_TIME_WINDOW_SECONDS_KEY = "aggregation.time.window.seconds";
+
+ String PROMETHEUS_EXPORTER_ENABLED_KEY = "prometheus.exporter.enabled";
+
+ String PROMETHEUS_EXPORTER_ENABLE_HTTP_SERVICE_DISCOVERY_KEY = "prometheus.exporter.enable.http.service.discovery";
+
+ String PROMETHEUS_EXPORTER_HTTP_SERVICE_DISCOVERY_URL_KEY = "prometheus.exporter.http.service.discovery.url";
+
+ String PROMETHEUS_EXPORTER_METRICS_PORT_KEY = "prometheus.exporter.metrics.port";
+
+ String PROMETHEUS_EXPORTER_METRICS_PATH_KEY = "prometheus.exporter.metrics.path";
+
+ String PROMETHEUS_PUSHGATEWAY_ENABLED_KEY = "prometheus.pushgateway.enabled";
+
+ String PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY = "prometheus.pushgateway.base.url";
+
+ String PROMETHEUS_PUSHGATEWAY_USERNAME_KEY = "prometheus.pushgateway.username";
+
+ String PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY = "prometheus.pushgateway.password";
+
+ String PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY = "prometheus.pushgateway.push.interval";
+
+ String PROMETHEUS_PUSHGATEWAY_JOB_KEY = "prometheus.pushgateway.job";
+
+ int PROMETHEUS_DEFAULT_METRICS_PORT = 20888;
+
+ String PROMETHEUS_DEFAULT_METRICS_PATH = "/metrics";
+
+ int PROMETHEUS_DEFAULT_PUSH_INTERVAL = 30;
+
+ String PROMETHEUS_DEFAULT_JOB_NAME = "default_dubbo_job";
+
+ String METRIC_FILTER_START_TIME = "metric_filter_start_time";
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
index 0e4f093..622780a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
@@ -103,6 +103,13 @@
String PROVIDED_BY = "provided-by";
/**
+ * The provider tri port
+ *
+ * @since 3.1.0
+ */
+ String PROVIDER_PORT = "provider-port";
+
+ /**
* The request size of service instances
*
* @since 2.7.5
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
index 2d84934..087d792 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
@@ -524,8 +524,9 @@
}
/**
- * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
- * will be thrown.
+ * Find the extension with the given name.
+ *
+ * @throws IllegalStateException If the specified extension is not found.
*/
public T getExtension(String name) {
T extension = getExtension(name, true);
@@ -1074,30 +1075,23 @@
List<String> newContentList = getResourceContent(resourceURL);
String clazz;
for (String line : newContentList) {
- final int ci = line.indexOf('#');
- if (ci >= 0) {
- line = line.substring(0, ci);
- }
- line = line.trim();
- if (line.length() > 0) {
- try {
- String name = null;
- int i = line.indexOf('=');
- if (i > 0) {
- name = line.substring(0, i).trim();
- clazz = line.substring(i + 1).trim();
- } else {
- clazz = line;
- }
- if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages) && isIncluded(clazz, includedPackages)
- && !isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) {
- loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
- }
- } catch (Throwable t) {
- IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type +
- ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
- exceptions.put(line, e);
+ try {
+ String name = null;
+ int i = line.indexOf('=');
+ if (i > 0) {
+ name = line.substring(0, i).trim();
+ clazz = line.substring(i + 1).trim();
+ } else {
+ clazz = line;
}
+ if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages) && isIncluded(clazz, includedPackages)
+ && !isExcludedByClassLoader(clazz, classLoader, onlyExtensionClassLoaderPackages)) {
+ loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
+ }
+ } catch (Throwable t) {
+ IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type +
+ ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
+ exceptions.put(line, e);
}
}
} catch (Throwable t) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java
index 8adc441..d8386f1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/ActivateComparator.java
@@ -22,8 +22,10 @@
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.common.utils.ArrayUtils;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -32,11 +34,16 @@
*/
public class ActivateComparator implements Comparator<Class<?>> {
- private final ExtensionDirector extensionDirector;
+ private final List<ExtensionDirector> extensionDirectors;
private final Map<Class<?>, ActivateInfo> activateInfoMap = new ConcurrentHashMap<>();
public ActivateComparator(ExtensionDirector extensionDirector) {
- this.extensionDirector = extensionDirector;
+ extensionDirectors = new ArrayList<>();
+ extensionDirectors.add(extensionDirector);
+ }
+
+ public ActivateComparator(List<ExtensionDirector> extensionDirectors) {
+ this.extensionDirectors = extensionDirectors;
}
@Override
@@ -60,9 +67,16 @@
ActivateInfo a2 = parseActivate(o2);
if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) {
- ExtensionLoader<?> extensionLoader = extensionDirector.getExtensionLoader(inf);
if (a1.applicableToCompare()) {
- String n2 = extensionLoader.getExtensionName(o2);
+ String n2 = null;
+ for (ExtensionDirector director : extensionDirectors) {
+ ExtensionLoader<?> extensionLoader = director.getExtensionLoader(inf);
+ n2 = extensionLoader.getExtensionName(o2);
+ if (n2 != null) {
+ break;
+ }
+ }
+
if (a1.isLess(n2)) {
return -1;
}
@@ -73,7 +87,15 @@
}
if (a2.applicableToCompare()) {
- String n1 = extensionLoader.getExtensionName(o1);
+ String n1 = null;
+ for (ExtensionDirector director : extensionDirectors) {
+ ExtensionLoader<?> extensionLoader = director.getExtensionLoader(inf);
+ n1 = extensionLoader.getExtensionName(o1);
+ if (n1 != null) {
+ break;
+ }
+ }
+
if (a2.isLess(n1)) {
return 1;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/MultiInstanceActivateComparator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/MultiInstanceActivateComparator.java
deleted file mode 100644
index 9d78914..0000000
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/support/MultiInstanceActivateComparator.java
+++ /dev/null
@@ -1,175 +0,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.common.extension.support;
-
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.extension.ExtensionDirector;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.common.utils.ArrayUtils;
-
-import java.util.List;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Arrays;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class MultiInstanceActivateComparator implements Comparator<Class<?>> {
-
- private final List<ExtensionDirector> extensionDirectors;
- private final Map<Class<?>, ActivateInfo> activateInfoMap = new ConcurrentHashMap<>();
-
- public MultiInstanceActivateComparator(List<ExtensionDirector> extensionDirectors) {
- this.extensionDirectors = extensionDirectors;
- }
-
- @Override
- public int compare(Class o1, Class o2) {
- if (o1 == null && o2 == null) {
- return 0;
- }
- if (o1 == null) {
- return -1;
- }
- if (o2 == null) {
- return 1;
- }
- if (o1.equals(o2)) {
- return 0;
- }
-
- Class<?> inf = findSpi(o1);
-
- ActivateInfo a1 = parseActivate(o1);
- ActivateInfo a2 = parseActivate(o2);
-
- if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) {
-
-
- if (a1.applicableToCompare()) {
- String n2 = null;
- for (ExtensionDirector director : extensionDirectors) {
- ExtensionLoader<?> extensionLoader = director.getExtensionLoader(inf);
- n2 = extensionLoader.getExtensionName(o2);
- if (n2 != null) {
- break;
- }
- }
- if (a1.isLess(n2)) {
- return -1;
- }
-
- if (a1.isMore(n2)) {
- return 1;
- }
- }
-
- if (a2.applicableToCompare()) {
- String n1 = null;
- for (ExtensionDirector director : extensionDirectors) {
- ExtensionLoader<?> extensionLoader = director.getExtensionLoader(inf);
- n1 = extensionLoader.getExtensionName(o1);
- if (n1 != null) {
- break;
- }
- }
-
- if (a2.isLess(n1)) {
- return 1;
- }
-
- if (a2.isMore(n1)) {
- return -1;
- }
- }
-
- return a1.order > a2.order ? 1 : -1;
- }
-
- // In order to avoid the problem of inconsistency between the loading order of two filters
- // in different loading scenarios without specifying the order attribute of the filter,
- // when the order is the same, compare its filterName
- if (a1.order > a2.order) {
- return 1;
- } else if (a1.order == a2.order) {
- return o1.getSimpleName().compareTo(o2.getSimpleName()) > 0 ? 1 : -1;
- } else {
- return -1;
- }
- }
-
- private Class<?> findSpi(Class<?> clazz) {
- if (clazz.getInterfaces().length == 0) {
- return null;
- }
-
- for (Class<?> intf : clazz.getInterfaces()) {
- if (intf.isAnnotationPresent(SPI.class)) {
- return intf;
- }
- Class<?> result = findSpi(intf);
- if (result != null) {
- return result;
- }
- }
-
- return null;
- }
-
- private ActivateInfo parseActivate(Class<?> clazz) {
- ActivateInfo info = activateInfoMap.get(clazz);
- if (info != null) {
- return info;
- }
- info = new ActivateInfo();
- if (clazz.isAnnotationPresent(Activate.class)) {
- Activate activate = clazz.getAnnotation(Activate.class);
- info.before = activate.before();
- info.after = activate.after();
- info.order = activate.order();
- } else if (clazz.isAnnotationPresent(com.alibaba.dubbo.common.extension.Activate.class)) {
- com.alibaba.dubbo.common.extension.Activate activate = clazz.getAnnotation(
- com.alibaba.dubbo.common.extension.Activate.class);
- info.before = activate.before();
- info.after = activate.after();
- info.order = activate.order();
- } else {
- info.order = 0;
- }
- activateInfoMap.put(clazz, info);
- return info;
- }
-
- private static class ActivateInfo {
- private String[] before;
- private String[] after;
- private int order;
-
- private boolean applicableToCompare() {
- return ArrayUtils.isNotEmpty(before) || ArrayUtils.isNotEmpty(after);
- }
-
- private boolean isLess(String name) {
- return Arrays.asList(before).contains(name);
- }
-
- private boolean isMore(String name) {
- return Arrays.asList(after).contains(name);
- }
- }
-}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java
new file mode 100644
index 0000000..fe9ebcc
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/ErrorTypeAwareLogger.java
@@ -0,0 +1,66 @@
+/*
+ * 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.logger;
+
+/**
+ * Logger interface with the ability of displaying solution of different types of error.
+ */
+public interface ErrorTypeAwareLogger extends Logger {
+
+ /**
+ * Logs a message with warn log level.
+ *
+ * @param code error code
+ * @param cause error cause
+ * @param extendedInformation extended information
+ * @param msg log this message
+ */
+ void warn(String code, String cause, String extendedInformation, String msg);
+
+ /**
+ * Logs a message with warn log level.
+ *
+ * @param code error code
+ * @param cause error cause
+ * @param extendedInformation extended information
+ * @param msg log this message
+ * @param e log this cause
+ */
+ void warn(String code, String cause, String extendedInformation, String msg, Throwable e);
+
+ /**
+ * Logs a message with error log level.
+ *
+ * @param code error code
+ * @param cause error cause
+ * @param extendedInformation extended information
+ * @param msg log this message
+ */
+ void error(String code, String cause, String extendedInformation, String msg);
+
+ /**
+ * Logs a message with error log level.
+ *
+ * @param code error code
+ * @param cause error cause
+ * @param extendedInformation extended information
+ * @param msg log this message
+ * @param e log this cause
+ */
+ void error(String code, String cause, String extendedInformation, String msg, Throwable e);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
index 9f787c5..91c0e60 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/LoggerFactory.java
@@ -21,6 +21,7 @@
import org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter;
import org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter;
import org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter;
+import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.support.FailsafeLogger;
import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -39,6 +40,7 @@
public class LoggerFactory {
private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap<>();
+ private static final ConcurrentMap<String, FailsafeErrorTypeAwareLogger> ERROR_TYPE_AWARE_LOGGERS = new ConcurrentHashMap<>();
private static volatile LoggerAdapter LOGGER_ADAPTER;
// search common-used logging frameworks
@@ -128,6 +130,26 @@
}
/**
+ * Get error type aware logger by Class object.
+ *
+ * @param key the returned logger will be named after clazz
+ * @return error type aware logger
+ */
+ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(Class<?> key) {
+ return ERROR_TYPE_AWARE_LOGGERS.computeIfAbsent(key.getName(), name -> new FailsafeErrorTypeAwareLogger(LOGGER_ADAPTER.getLogger(name)));
+ }
+
+ /**
+ * Get error type aware logger by a String key.
+ *
+ * @param key the returned logger will be named after key
+ * @return error type aware logger
+ */
+ public static ErrorTypeAwareLogger getErrorTypeAwareLogger(String key) {
+ return ERROR_TYPE_AWARE_LOGGERS.computeIfAbsent(key, k -> new FailsafeErrorTypeAwareLogger(LOGGER_ADAPTER.getLogger(k)));
+ }
+
+ /**
* Get logging level
*
* @return logging level
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java
new file mode 100644
index 0000000..8438c0e
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLogger.java
@@ -0,0 +1,127 @@
+/*
+ * 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.logger.support;
+
+import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.utils.NetUtils;
+
+import java.util.regex.Pattern;
+
+/**
+ * A fail-safe (ignoring exception thrown by logger) wrapper of error type aware logger.
+ */
+public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements ErrorTypeAwareLogger {
+
+ /**
+ * Template address for formatting.
+ */
+ private static final String INSTRUCTIONS_URL = "https://dubbo.apache.org/faq/%d/%d";
+
+ private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d+-\\d+");
+
+ public FailsafeErrorTypeAwareLogger(Logger logger) {
+ super(logger);
+ }
+
+ private String appendContextMessageWithInstructions(String code, String cause, String extendedInformation, String msg) {
+ return " [DUBBO] " + msg + ", dubbo version: " + Version.getVersion() +
+ ", current host: " + NetUtils.getLocalHost() + ", error code: " + code +
+ ". This may be caused by " + cause + ", " +
+ "go to " + getErrorUrl(code) + " to find instructions. " + extendedInformation;
+ }
+
+ private String getErrorUrl(String code) {
+
+ String trimmedString = code.trim();
+
+ if (!ERROR_CODE_PATTERN.matcher(trimmedString).matches()) {
+ error("Invalid error code: " + code + ", the format of error code is: X-X (where X is a number).");
+ return "";
+ }
+
+ String[] segments = trimmedString.split("[-]");
+
+ int[] errorCodeSegments = new int[2];
+
+ try {
+ errorCodeSegments[0] = Integer.parseInt(segments[0]);
+ errorCodeSegments[1] = Integer.parseInt(segments[1]);
+ } catch (NumberFormatException numberFormatException) {
+ error("Invalid error code: " + code + ", the format of error code is: X-X (where X is a number).",
+ numberFormatException);
+
+ return "";
+ }
+
+ return String.format(INSTRUCTIONS_URL, errorCodeSegments[0], errorCodeSegments[1]);
+ }
+
+ @Override
+ public void warn(String code, String cause, String extendedInformation, String msg) {
+ if (getDisabled()) {
+ return;
+ }
+
+ try {
+ getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
+ } catch (Throwable t) {
+ // ignored.
+ }
+ }
+
+ @Override
+ public void warn(String code, String cause, String extendedInformation, String msg, Throwable e) {
+ if (getDisabled()) {
+ return;
+ }
+
+ try {
+ getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
+ } catch (Throwable t) {
+ // ignored.
+ }
+ }
+
+ @Override
+ public void error(String code, String cause, String extendedInformation, String msg) {
+ if (getDisabled()) {
+ return;
+ }
+
+ try {
+ getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
+ } catch (Throwable t) {
+ // ignored.
+ }
+ }
+
+ @Override
+ public void error(String code, String cause, String extendedInformation, String msg, Throwable e) {
+ if (getDisabled()) {
+ return;
+ }
+
+ try {
+ getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
+ } catch (Throwable t) {
+ // ignored.
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
index ff6804b..66e369f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/logger/support/FailsafeLogger.java
@@ -34,6 +34,10 @@
FailsafeLogger.disabled = disabled;
}
+ static boolean getDisabled() {
+ return disabled;
+ }
+
public Logger getLogger() {
return logger;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporter.java
new file mode 100644
index 0000000..b07af77
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporter.java
@@ -0,0 +1,30 @@
+/*
+ * 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.metrics;
+
+/**
+ * Metrics Reporter.
+ * Report metrics to specific metrics server(e.g. Prometheus).
+ */
+public interface MetricsReporter {
+
+ /**
+ * Initialize metrics reporter.
+ */
+ void init();
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporterFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporterFactory.java
new file mode 100644
index 0000000..ade70ca
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/MetricsReporterFactory.java
@@ -0,0 +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.common.metrics;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * The factory interface to create the instance of {@link MetricsReporter}.
+ */
+@SPI(value = "nop", scope = ExtensionScope.APPLICATION)
+public interface MetricsReporterFactory {
+
+ /**
+ * Create metrics reporter.
+ *
+ * @param url URL
+ * @return Metrics reporter implementation.
+ */
+ @Adaptive({"protocol"})
+ MetricsReporter createMetricsReporter(URL url);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollector.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollector.java
new file mode 100644
index 0000000..6e5ba2d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollector.java
@@ -0,0 +1,171 @@
+/*
+ * 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.metrics.collector;
+
+import static org.apache.dubbo.common.metrics.model.MetricsCategory.REQUESTS;
+import static org.apache.dubbo.common.metrics.model.MetricsCategory.RT;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.apache.dubbo.common.metrics.collector.stat.MetricsStatComposite;
+import org.apache.dubbo.common.metrics.collector.stat.MetricsStatHandler;
+import org.apache.dubbo.common.metrics.event.RequestEvent;
+import org.apache.dubbo.common.metrics.listener.MetricsListener;
+import org.apache.dubbo.common.metrics.model.MetricsKey;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+/**
+ * Default implementation of {@link MetricsCollector}
+ */
+public class DefaultMetricsCollector implements MetricsCollector {
+
+ private AtomicBoolean collectEnabled = new AtomicBoolean(false);
+ private final List<MetricsListener> listeners = new ArrayList<>();
+ private final ApplicationModel applicationModel;
+ private final MetricsStatComposite stats;
+
+ public DefaultMetricsCollector(ApplicationModel applicationModel) {
+ this.applicationModel = applicationModel;
+ this.stats = new MetricsStatComposite(applicationModel.getApplicationName(), this);
+ }
+
+ public void setCollectEnabled(Boolean collectEnabled) {
+ this.collectEnabled.compareAndSet(isCollectEnabled(), collectEnabled);
+ }
+
+ public Boolean isCollectEnabled() {
+ return collectEnabled.get();
+ }
+
+ public void addListener(MetricsListener listener) {
+ listeners.add(listener);
+ }
+
+ public List<MetricsListener> getListener() {
+ return this.listeners;
+ }
+
+ public void increaseTotalRequests(String interfaceName, String methodName, String group, String version) {
+ doExecute(RequestEvent.Type.TOTAL,statHandler-> {
+ statHandler.increase(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void increaseSucceedRequests(String interfaceName, String methodName, String group, String version) {
+ doExecute(RequestEvent.Type.SUCCEED,statHandler->{
+ statHandler.increase(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void increaseFailedRequests(String interfaceName,
+ String methodName,
+ String group,
+ String version) {
+ doExecute(RequestEvent.Type.FAILED,statHandler->{
+ statHandler.increase(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void businessFailedRequests(String interfaceName, String methodName, String group, String version) {
+ doExecute(RequestEvent.Type.BUSINESS_FAILED,statHandler->{
+ statHandler.increase(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void increaseProcessingRequests(String interfaceName, String methodName, String group, String version) {
+ doExecute(RequestEvent.Type.PROCESSING,statHandler-> {
+ statHandler.increase(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void decreaseProcessingRequests(String interfaceName, String methodName, String group, String version) {
+ doExecute(RequestEvent.Type.PROCESSING,statHandler-> {
+ statHandler.decrease(interfaceName, methodName, group, version);
+ });
+ }
+
+ public void addRT(String interfaceName, String methodName, String group, String version, Long responseTime) {
+ stats.addRT(interfaceName, methodName, group, version, responseTime);
+ }
+
+ @Override
+ public List<MetricSample> collect() {
+ List<MetricSample> list = new ArrayList<>();
+ collectRequests(list);
+ collectRT(list);
+
+ return list;
+ }
+
+ private void collectRequests(List<MetricSample> list) {
+ doExecute(RequestEvent.Type.TOTAL, MetricsStatHandler::get).filter(e->!e.isEmpty())
+ .ifPresent(map-> map.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_TOTAL, k.getTags(), REQUESTS, v::get))));
+
+ doExecute(RequestEvent.Type.SUCCEED, MetricsStatHandler::get).filter(e->!e.isEmpty())
+ .ifPresent(map-> map.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_SUCCEED, k.getTags(), REQUESTS, v::get))));
+
+ doExecute(RequestEvent.Type.FAILED, MetricsStatHandler::get).filter(e->!e.isEmpty())
+ .ifPresent(map->{
+ map.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_FAILED, k.getTags(), REQUESTS, v::get)));
+ });
+
+ doExecute(RequestEvent.Type.PROCESSING, MetricsStatHandler::get).filter(e->!e.isEmpty())
+ .ifPresent(map-> map.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_PROCESSING, k.getTags(), REQUESTS, v::get))));
+
+ doExecute(RequestEvent.Type.BUSINESS_FAILED, MetricsStatHandler::get).filter(e->!e.isEmpty())
+ .ifPresent(map-> map.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED, k.getTags(), REQUESTS, v::get))));
+ }
+
+ private void collectRT(List<MetricSample> list) {
+ this.stats.getLastRT().forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_LAST, k.getTags(), RT, v::get)));
+ this.stats.getMinRT().forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_MIN, k.getTags(), RT, v::get)));
+ this.stats.getMaxRT().forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_MAX, k.getTags(), RT, v::get)));
+
+ this.stats.getTotalRT().forEach((k, v) -> {
+ list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_TOTAL, k.getTags(), RT, v::get));
+
+ AtomicLong avg = this.stats.getAvgRT().get(k);
+ AtomicLong count = this.stats.getRtCount().get(k);
+ avg.set(v.get() / count.get());
+ list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_AVG, k.getTags(), RT, avg::get));
+ });
+ }
+ private <T> Optional<T> doExecute(RequestEvent.Type requestType, Function<MetricsStatHandler,T> statExecutor) {
+ if (isCollectEnabled()) {
+ MetricsStatHandler handler = stats.getHandler(requestType);
+ T result = statExecutor.apply(handler);
+ return Optional.ofNullable(result);
+ }
+ return Optional.empty();
+ }
+
+ private void doExecute(RequestEvent.Type requestType, Consumer<MetricsStatHandler> statExecutor) {
+ if (isCollectEnabled()) {
+ MetricsStatHandler handler = stats.getHandler(requestType);
+ statExecutor.accept(handler);
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/MetricsCollector.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/MetricsCollector.java
new file mode 100644
index 0000000..12eaa2a
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/MetricsCollector.java
@@ -0,0 +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.common.metrics.collector;
+
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+
+import java.util.List;
+
+/**
+ * Metrics Collector.
+ * An interface of collector to collect framework internal metrics.
+ */
+public interface MetricsCollector {
+
+ /**
+ * Collect metrics as {@link MetricSample}
+ *
+ * @return List of MetricSample
+ */
+ List<MetricSample> collect();
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/DefaultMetricsStatHandler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/DefaultMetricsStatHandler.java
new file mode 100644
index 0000000..1670b9d
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/DefaultMetricsStatHandler.java
@@ -0,0 +1,74 @@
+/*
+ * 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.metrics.collector.stat;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
+
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+
+
+public class DefaultMetricsStatHandler implements MetricsStatHandler {
+
+ private final String applicationName;
+ private final Map<MethodMetric, AtomicLong> counts = new ConcurrentHashMap<>();
+
+ public DefaultMetricsStatHandler(String applicationName) {
+ this.applicationName = applicationName;
+ }
+
+ @Override
+ public void increase(String interfaceName, String methodName, String group, String version) {
+ this.doIncrExecute(interfaceName,methodName,group,version);
+ }
+
+ public void decrease(String interfaceName, String methodName, String group, String version){
+ this.doDecrExecute(interfaceName,methodName,group,version);
+ }
+
+ protected void doExecute(String interfaceName, String methodName, String group, String version, BiConsumer<MethodMetric,Map<MethodMetric, AtomicLong>> execute){
+ MethodMetric metric = new MethodMetric(applicationName, interfaceName, methodName, group, version);
+ execute.accept(metric,counts);
+
+ this.doNotify(metric);
+ }
+
+ protected void doIncrExecute(String interfaceName, String methodName, String group, String version){
+ this.doExecute(interfaceName,methodName,group,version,(metric,counts)->{
+ AtomicLong count = counts.computeIfAbsent(metric, k -> new AtomicLong(0L));
+ count.incrementAndGet();
+
+ });
+ }
+
+ protected void doDecrExecute(String interfaceName, String methodName, String group, String version){
+ this.doExecute(interfaceName,methodName,group,version,(metric,counts)->{
+ AtomicLong count = counts.computeIfAbsent(metric, k -> new AtomicLong(0L));
+ count.decrementAndGet();
+ });
+ }
+
+ @Override
+ public Map<MethodMetric, AtomicLong> get() {
+ return counts;
+ }
+
+ public void doNotify(MethodMetric metric){}
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatComposite.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatComposite.java
new file mode 100644
index 0000000..d86a206
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatComposite.java
@@ -0,0 +1,138 @@
+/*
+ * 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.metrics.collector.stat;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAccumulator;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.event.MetricsEvent;
+import org.apache.dubbo.common.metrics.event.RTEvent;
+import org.apache.dubbo.common.metrics.event.RequestEvent;
+import org.apache.dubbo.common.metrics.listener.MetricsListener;
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+public class MetricsStatComposite{
+
+ public Map<RequestEvent.Type, MetricsStatHandler> stats = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, AtomicLong> lastRT = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, LongAccumulator> minRT = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, LongAccumulator> maxRT = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, AtomicLong> avgRT = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, AtomicLong> totalRT = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, AtomicLong> rtCount = new ConcurrentHashMap<>();
+ private final String applicationName;
+ private final List<MetricsListener> listeners;
+ private DefaultMetricsCollector collector;
+
+ public MetricsStatComposite(String applicationName, DefaultMetricsCollector collector){
+ this.applicationName = applicationName;
+ this.listeners = collector.getListener();
+ this.collector = collector;
+ this.init();
+ }
+
+ public MetricsStatHandler getHandler(RequestEvent.Type statType) {
+ return stats.get(statType);
+ }
+
+ public Map<MethodMetric, AtomicLong> getLastRT(){
+ return this.lastRT;
+ }
+ public Map<MethodMetric, LongAccumulator> getMinRT(){
+ return this.minRT;
+ }
+
+ public Map<MethodMetric, LongAccumulator> getMaxRT(){
+ return this.maxRT;
+ }
+ public Map<MethodMetric, AtomicLong> getAvgRT(){
+ return this.avgRT;
+ }
+ public Map<MethodMetric, AtomicLong> getTotalRT(){
+ return this.totalRT;
+ }
+ public Map<MethodMetric, AtomicLong> getRtCount(){
+ return this.rtCount;
+ }
+
+ public void addRT(String interfaceName, String methodName, String group, String version, Long responseTime) {
+ if (collector.isCollectEnabled()) {
+ MethodMetric metric = new MethodMetric(applicationName, interfaceName, methodName, group, version);
+
+ AtomicLong last = lastRT.computeIfAbsent(metric, k -> new AtomicLong());
+ last.set(responseTime);
+
+ LongAccumulator min = minRT.computeIfAbsent(metric, k -> new LongAccumulator(Long::min, Long.MAX_VALUE));
+ min.accumulate(responseTime);
+
+ LongAccumulator max = maxRT.computeIfAbsent(metric, k -> new LongAccumulator(Long::max, Long.MIN_VALUE));
+ max.accumulate(responseTime);
+
+ AtomicLong total = totalRT.computeIfAbsent(metric, k -> new AtomicLong());
+ total.addAndGet(responseTime);
+
+ AtomicLong count = rtCount.computeIfAbsent(metric, k -> new AtomicLong());
+ count.incrementAndGet();
+
+ avgRT.computeIfAbsent(metric, k -> new AtomicLong());
+
+ publishEvent(new RTEvent(metric, responseTime));
+ }
+ }
+
+ private void init() {
+ stats.put(RequestEvent.Type.TOTAL, new DefaultMetricsStatHandler(applicationName){
+ @Override
+ public void doNotify(MethodMetric metric) {
+ publishEvent(new RequestEvent(metric, RequestEvent.Type.TOTAL));
+ }
+ });
+
+ stats.put(RequestEvent.Type.SUCCEED, new DefaultMetricsStatHandler(applicationName) {
+ @Override
+ public void doNotify(MethodMetric metric) {
+ publishEvent(new RequestEvent(metric, RequestEvent.Type.SUCCEED));
+ }
+ });
+
+ stats.put(RequestEvent.Type.FAILED, new DefaultMetricsStatHandler(applicationName) {
+ @Override
+ public void doNotify(MethodMetric metric) {
+ publishEvent(new RequestEvent(metric, RequestEvent.Type.FAILED));
+ }
+ });
+
+ stats.put(RequestEvent.Type.BUSINESS_FAILED, new DefaultMetricsStatHandler(applicationName) {
+ @Override
+ public void doNotify(MethodMetric metric) {
+ publishEvent(new RequestEvent(metric, RequestEvent.Type.BUSINESS_FAILED));
+ }
+ });
+
+ stats.put(RequestEvent.Type.PROCESSING, new DefaultMetricsStatHandler(applicationName));
+ }
+
+ private void publishEvent(MetricsEvent event) {
+ for (MetricsListener listener : listeners) {
+ listener.onEvent(event);
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatHandler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatHandler.java
new file mode 100644
index 0000000..d39c5d4
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/collector/stat/MetricsStatHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.metrics.collector.stat;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+
+public interface MetricsStatHandler {
+ Map<MethodMetric, AtomicLong> get();
+ void increase(String interfaceName, String methodName, String group, String version);
+ void decrease(String interfaceName, String methodName, String group, String version);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/MetricsEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/MetricsEvent.java
new file mode 100644
index 0000000..3b009db
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/MetricsEvent.java
@@ -0,0 +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.common.metrics.event;
+
+/**
+ * BaseMetricsEvent.
+ */
+public abstract class MetricsEvent {
+
+ /**
+ * Metric object. (eg. {@link org.apache.dubbo.common.metrics.model.MethodMetric})
+ */
+ protected transient Object source;
+
+ public MetricsEvent(Object source) {
+ if (source == null) {
+ throw new IllegalArgumentException("null source");
+ }
+
+ this.source = source;
+ }
+
+ public Object getSource() {
+ return source;
+ }
+
+ public String toString() {
+ return getClass().getName() + "[source=" + source + "]";
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RTEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RTEvent.java
new file mode 100644
index 0000000..68887dc
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RTEvent.java
@@ -0,0 +1,38 @@
+/*
+ * 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.metrics.event;
+
+/**
+ * RtEvent.
+ */
+public class RTEvent extends MetricsEvent {
+ private Long rt;
+
+ public RTEvent(Object source, Long rt) {
+ super(source);
+ this.rt = rt;
+ }
+
+ public Long getRt() {
+ return rt;
+ }
+
+ public void setRt(Long rt) {
+ this.rt = rt;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RequestEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RequestEvent.java
new file mode 100644
index 0000000..f0a6677
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/event/RequestEvent.java
@@ -0,0 +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.common.metrics.event;
+
+/**
+ * RequestEvent.
+ */
+public class RequestEvent extends MetricsEvent {
+ private Type type;
+
+ public RequestEvent(Object source, Type type) {
+ super(source);
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ public enum Type {
+ TOTAL,
+ SUCCEED,
+ FAILED,
+ BUSINESS_FAILED,
+
+ PROCESSING
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/listener/MetricsListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/listener/MetricsListener.java
new file mode 100644
index 0000000..0f55e9b
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/listener/MetricsListener.java
@@ -0,0 +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.metrics.listener;
+
+import org.apache.dubbo.common.metrics.event.MetricsEvent;
+
+/**
+ * Metrics Listener.
+ */
+public interface MetricsListener {
+
+ /**
+ * notify event.
+ *
+ * @param event BaseMetricsEvent
+ */
+ void onEvent(MetricsEvent event);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MethodMetric.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MethodMetric.java
new file mode 100644
index 0000000..3e3f75a
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MethodMetric.java
@@ -0,0 +1,124 @@
+/*
+ * 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.metrics.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
+
+/**
+ * Metric class for method.
+ */
+public class MethodMetric {
+ private String applicationName;
+ private String interfaceName;
+ private String methodName;
+ private String group;
+ private String version;
+
+ public MethodMetric() {
+
+ }
+
+ public MethodMetric(String applicationName, String interfaceName, String methodName, String group, String version) {
+ this.applicationName = applicationName;
+ this.interfaceName = interfaceName;
+ this.methodName = methodName;
+ this.group = group;
+ this.version = version;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public void setInterfaceName(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Map<String, String> getTags() {
+ Map<String, String> tags = new HashMap<>();
+ tags.put(TAG_IP, getLocalHost());
+ tags.put(TAG_HOSTNAME, getLocalHostName());
+ tags.put(TAG_APPLICATION_NAME, applicationName);
+
+ tags.put(TAG_INTERFACE_KEY, interfaceName);
+ tags.put(TAG_METHOD_KEY, methodName);
+ tags.put(TAG_GROUP_KEY, group);
+ tags.put(TAG_VERSION_KEY, version);
+ return tags;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MethodMetric that = (MethodMetric) o;
+ return Objects.equals(interfaceName, that.interfaceName) && Objects.equals(methodName, that.methodName)
+ && Objects.equals(group, that.group) && Objects.equals(version, that.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(interfaceName, methodName, group, version);
+ }
+
+ @Override
+ public String toString() {
+ return "MethodMetric{" +
+ "interfaceName='" + interfaceName + '\'' +
+ ", methodName='" + methodName + '\'' +
+ ", group='" + group + '\'' +
+ ", version='" + version + '\'' +
+ '}';
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsCategory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsCategory.java
new file mode 100644
index 0000000..700a47f
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsCategory.java
@@ -0,0 +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.common.metrics.model;
+
+/**
+ * Metric category.
+ */
+public enum MetricsCategory {
+ RT,
+ QPS,
+ REQUESTS,
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsKey.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsKey.java
new file mode 100644
index 0000000..bbb1bd8
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/MetricsKey.java
@@ -0,0 +1,58 @@
+/*
+ * 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.metrics.model;
+
+public enum MetricsKey {
+
+ METRIC_REQUESTS_TOTAL("requests.total", "Total Requests"),
+ METRIC_REQUESTS_SUCCEED("requests.succeed", "Succeed Requests"),
+ METRIC_REQUESTS_FAILED("requests.failed", "Failed Requests"),
+ METRIC_REQUEST_BUSINESS_FAILED("requests.business.failed","Failed Business Requests"),
+ METRIC_REQUESTS_PROCESSING("requests.processing", "Processing Requests"),
+
+ METRIC_REQUESTS_TOTAL_AGG("requests.total.aggregate", "Aggregated Total Requests"),
+ METRIC_REQUESTS_SUCCEED_AGG("requests.succeed.aggregate", "Aggregated Succeed Requests"),
+ METRIC_REQUESTS_FAILED_AGG("requests.failed.aggregate", "Aggregated Failed Requests"),
+ METRIC_REQUESTS_BUSINESS_FAILED_AGG("requests.business.failed.aggregate", "Aggregated Business Failed Requests"),
+
+ METRIC_QPS("qps", "Query Per Seconds"),
+ METRIC_RT_LAST("rt.last", "Last Response Time"),
+ METRIC_RT_MIN("rt.min", "Min Response Time"),
+ METRIC_RT_MAX("rt.max", "Max Response Time"),
+ METRIC_RT_TOTAL("rt.total", "Total Response Time"),
+ METRIC_RT_AVG("rt.avg", "Average Response Time"),
+ METRIC_RT_P99("rt.p99", "Response Time P99"),
+ METRIC_RT_P95("rt.p95", "Response Time P95"),
+ ;
+
+ private final String name;
+ private final String description;
+
+ public final String getName() {
+ return this.name;
+ }
+
+ public final String getDescription() {
+ return this.description;
+ }
+
+ MetricsKey(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSample.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSample.java
new file mode 100644
index 0000000..1bdb2aa
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSample.java
@@ -0,0 +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.common.metrics.model.sample;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+import org.apache.dubbo.common.metrics.model.MetricsKey;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * GaugeMetricSample.
+ */
+public class GaugeMetricSample extends MetricSample {
+
+ private Supplier<Number> supplier;
+
+ public GaugeMetricSample(MetricsKey metricsKey, Map<String, String> tags, MetricsCategory category, Supplier<Number> supplier) {
+ super(metricsKey.getName(), metricsKey.getDescription(), tags, Type.GAUGE, category);
+ this.supplier = supplier;
+ }
+
+ public GaugeMetricSample(String name, String description, Map<String, String> tags, MetricsCategory category, String baseUnit, Supplier<Number> supplier) {
+ super(name, description, tags, Type.GAUGE, category, baseUnit);
+ this.supplier = supplier;
+ }
+
+ public Supplier<Number> getSupplier() {
+ return supplier;
+ }
+
+ public void setSupplier(Supplier<Number> supplier) {
+ this.supplier = supplier;
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/MetricSample.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/MetricSample.java
new file mode 100644
index 0000000..21fe5e0
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/model/sample/MetricSample.java
@@ -0,0 +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.common.metrics.model.sample;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * MetricSample.
+ */
+public class MetricSample {
+ private String name;
+ private String description;
+ private Map<String, String> tags;
+ private Type type;
+ private MetricsCategory category;
+ private String baseUnit;
+
+ public MetricSample(String name, String description, Map<String, String> tags, Type type, MetricsCategory category) {
+ this(name, description, tags, type, category, null);
+ }
+
+ public MetricSample(String name, String description, Map<String, String> tags, Type type, MetricsCategory category, String baseUnit) {
+ this.name = name;
+ this.description = description;
+ this.tags = tags;
+ this.type = type;
+ this.category = category;
+ this.baseUnit = baseUnit;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Map<String, String> getTags() {
+ return tags;
+ }
+
+ public void setTags(Map<String, String> tags) {
+ this.tags = tags;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ public MetricsCategory getCategory() {
+ return category;
+ }
+
+ public void setCategory(MetricsCategory category) {
+ this.category = category;
+ }
+
+ public String getBaseUnit() {
+ return baseUnit;
+ }
+
+ public void setBaseUnit(String baseUnit) {
+ this.baseUnit = baseUnit;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MetricSample that = (MetricSample) o;
+ return Objects.equals(name, that.name) && Objects.equals(description, that.description)
+ && Objects.equals(baseUnit, that.baseUnit) && type == that.type
+ && Objects.equals(category, that.category) && Objects.equals(tags, that.tags);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, description, baseUnit, type, category, tags);
+ }
+
+ @Override
+ public String toString() {
+ return "MetricSample{" +
+ "name='" + name + '\'' +
+ ", description='" + description + '\'' +
+ ", baseUnit='" + baseUnit + '\'' +
+ ", type=" + type +
+ ", category=" + category +
+ ", tags=" + tags +
+ '}';
+ }
+
+ public enum Type {
+ COUNTER,
+ GAUGE,
+ LONG_TASK_TIMER,
+ TIMER,
+ DISTRIBUTION_SUMMARY
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporter.java
new file mode 100644
index 0000000..07e8c6c
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporter.java
@@ -0,0 +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.common.metrics.nop;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.metrics.MetricsReporter;
+
+/**
+ * Metrics reporter without any operations.
+ */
+public class NopMetricsReporter implements MetricsReporter {
+
+ public NopMetricsReporter(URL url) {
+
+ }
+
+ @Override
+ public void init() {
+
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporterFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporterFactory.java
new file mode 100644
index 0000000..462de5e
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/nop/NopMetricsReporterFactory.java
@@ -0,0 +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.metrics.nop;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.metrics.MetricsReporter;
+import org.apache.dubbo.common.metrics.MetricsReporterFactory;
+
+/**
+ * MetricsReporterFactory to create NopMetricsReporter.
+ */
+public class NopMetricsReporterFactory implements MetricsReporterFactory {
+
+ @Override
+ public MetricsReporter createMetricsReporter(URL url) {
+ return new NopMetricsReporter(url);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsEntity.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsEntity.java
new file mode 100644
index 0000000..7c64e29
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsEntity.java
@@ -0,0 +1,91 @@
+/*
+ * 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.metrics.service;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Metrics response entity.
+ */
+public class MetricsEntity {
+
+ private String name;
+ private Map<String, String> tags;
+ private MetricsCategory category;
+ private Object value;
+
+ public MetricsEntity() {
+
+ }
+
+ public MetricsEntity(String name, Map<String, String> tags, MetricsCategory category, Object value) {
+ this.name = name;
+ this.tags = tags;
+ this.category = category;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map<String, String> getTags() {
+ return tags;
+ }
+
+ public void setTags(Map<String, String> tags) {
+ this.tags = tags;
+ }
+
+ public MetricsCategory getCategory() {
+ return category;
+ }
+
+ public void setCategory(MetricsCategory category) {
+ this.category = category;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MetricsEntity entity = (MetricsEntity) o;
+ return Objects.equals(name, entity.name) && Objects.equals(tags, entity.tags)
+ && Objects.equals(category, entity.category) && Objects.equals(value, entity.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, tags, category, value);
+ }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsService.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsService.java
new file mode 100644
index 0000000..7c8724e
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsService.java
@@ -0,0 +1,73 @@
+/*
+ * 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.metrics.service;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.metrics.service.MetricsService.DEFAULT_EXTENSION_NAME;
+
+/**
+ * Metrics Service.
+ * Provide an interface to get metrics from {@link org.apache.dubbo.common.metrics.collector.MetricsCollector}
+ */
+@SPI(value = DEFAULT_EXTENSION_NAME, scope = ExtensionScope.APPLICATION)
+public interface MetricsService {
+
+ /**
+ * Default {@link MetricsService} extension name.
+ */
+ String DEFAULT_EXTENSION_NAME = "default";
+
+ /**
+ * The contract version of {@link MetricsService}, the future update must make sure compatible.
+ */
+ String VERSION = "1.0.0";
+
+ /**
+ * Get metrics by prefixes
+ *
+ * @param categories categories
+ * @return metrics - key=MetricCategory value=MetricsEntityList
+ */
+ Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(List<MetricsCategory> categories);
+
+ /**
+ * Get metrics by interface and prefixes
+ *
+ * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version)
+ * @param categories categories
+ * @return metrics - key=MetricCategory value=MetricsEntityList
+ */
+ Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(String serviceUniqueName, List<MetricsCategory> categories);
+
+ /**
+ * Get metrics by interface、method and prefixes
+ *
+ * @param serviceUniqueName serviceUniqueName (eg.group/interfaceName:version)
+ * @param methodName methodName
+ * @param parameterTypes method parameter types
+ * @param categories categories
+ * @return metrics - key=MetricCategory value=MetricsEntityList
+ */
+ Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(String serviceUniqueName, String methodName, Class<?>[] parameterTypes, List<MetricsCategory> categories);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsServiceExporter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsServiceExporter.java
new file mode 100644
index 0000000..8a9a6a0
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/metrics/service/MetricsServiceExporter.java
@@ -0,0 +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.common.metrics.service;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * The exporter of {@link MetricsService}
+ */
+@SPI(value = "default", scope = ExtensionScope.APPLICATION)
+public interface MetricsServiceExporter {
+
+ /**
+ * Initialize exporter
+ */
+ void init();
+
+ /**
+ * Exports the {@link MetricsService} as a Dubbo service
+ *
+ * @return {@link MetricsServiceExporter itself}
+ */
+ MetricsServiceExporter export();
+
+ /**
+ * Unexports the {@link MetricsService}
+ *
+ * @return {@link MetricsServiceExporter itself}
+ */
+ MetricsServiceExporter unexport();
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java
index 4fc50d9..5d2c420 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/profiler/Profiler.java
@@ -25,7 +25,7 @@
public static final String PROFILER_KEY = "DUBBO_INVOKE_PROFILER";
public static final int MAX_ENTRY_SIZE = 1000;
- private final static InternalThreadLocal<ProfilerEntry> bizProfiler = new InternalThreadLocal<>();
+ private static final InternalThreadLocal<ProfilerEntry> bizProfiler = new InternalThreadLocal<>();
public static ProfilerEntry start(String message) {
return new ProfilerEntry(message);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
index 0942d88..223ad94 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
@@ -42,6 +42,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_EXPORT_THREAD_NUM;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_REFER_THREAD_NUM;
import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY;
@@ -83,8 +84,14 @@
Map<Integer, ExecutorService> executors = data.computeIfAbsent(getExecutorKey(url), k -> new ConcurrentHashMap<>());
// Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol.
Integer portKey = CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY)) ? Integer.MAX_VALUE : url.getPort();
+
+ String protocol = url.getProtocol();
+ if (StringUtils.isEmpty(protocol)) {
+ protocol = DEFAULT_PROTOCOL;
+ }
+
if (url.getParameter(THREAD_NAME_KEY) == null) {
- url = url.putAttribute(THREAD_NAME_KEY, "Dubbo-protocol-" + portKey);
+ url = url.putAttribute(THREAD_NAME_KEY, protocol + "-protocol-" + portKey);
}
URL finalUrl = url;
ExecutorService executor = executors.computeIfAbsent(portKey, k -> createExecutor(finalUrl));
@@ -129,6 +136,7 @@
if (executors == null) {
logger.warn("No available executors, this is not expected, framework should call createExecutorIfAbsent first " +
"before coming to here.");
+
return null;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java
index df4e10e..d630ee4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/FrameworkExecutorRepository.java
@@ -91,7 +91,7 @@
/**
* Returns a scheduler from the scheduler list, call this method whenever you need a scheduler for a cron job.
- * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicate one.
+ * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicated one.
*
* @return ScheduledExecutorService
*/
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java
index 77dff05..43520f9 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReport.java
@@ -14,10 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.dubbo.common.threadpool.support;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedEvent;
import org.apache.dubbo.common.threadpool.event.ThreadPoolExhaustedListener;
@@ -40,6 +41,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY;
import static org.apache.dubbo.common.constants.CommonConstants.OS_NAME_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.OS_WIN_PREFIX;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_THREAD_POOL_EXHAUSTED;
/**
* Abort Policy.
@@ -47,7 +49,7 @@
*/
public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {
- protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);
+ protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbortPolicyWithReport.class);
private final String threadName;
@@ -82,9 +84,13 @@
e.getLargestPoolSize(),
e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
url.getProtocol(), url.getIp(), url.getPort());
- logger.warn(msg);
+
+ // 0-1 - Thread pool is EXHAUSTED!
+ logger.warn(COMMON_THREAD_POOL_EXHAUSTED, "too much client requesting provider", "", msg);
+
dumpJStack();
dispatchThreadPoolExhaustedEvent(msg);
+
throw new RejectedExecutionException(msg);
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPool.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPool.java
index 8aa8fac..53ee25c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPool.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/eager/EagerThreadPool.java
@@ -51,7 +51,7 @@
int alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE);
// init queue and executor
- TaskQueue<Runnable> taskQueue = new TaskQueue<Runnable>(queues <= 0 ? 1 : queues);
+ TaskQueue<Runnable> taskQueue = new TaskQueue<>(queues <= 0 ? 1 : queues);
EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor(cores,
threads,
alive,
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPool.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
index de1fb7e..06acc39 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
@@ -22,6 +22,7 @@
import org.apache.dubbo.common.threadpool.ThreadPool;
import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
@@ -47,10 +48,18 @@
String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME));
int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
- return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
- queues == 0 ? new SynchronousQueue<Runnable>() :
- (queues < 0 ? new MemorySafeLinkedBlockingQueue<Runnable>()
- : new LinkedBlockingQueue<Runnable>(queues)),
+
+ BlockingQueue<Runnable> blockingQueue;
+
+ if (queues == 0) {
+ blockingQueue = new SynchronousQueue<>();
+ } else if (queues < 0) {
+ blockingQueue = new MemorySafeLinkedBlockingQueue<>();
+ } else {
+ blockingQueue = new LinkedBlockingQueue<>(queues);
+ }
+
+ return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, blockingQueue,
new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java
index 2ccf48d..e6cb3cb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java
@@ -52,14 +52,14 @@
* You can increase or decrease the accuracy of the execution timing by
* specifying smaller or larger tick duration in the constructor. In most
* network applications, I/O timeout does not need to be accurate. Therefore,
- * the default tick duration is 100 milliseconds and you will not need to try
+ * the default tick duration is 100 milliseconds, and you will not need to try
* different configurations in most cases.
*
* <h3>Ticks per Wheel (Wheel Size)</h3>
* <p>
* {@link HashedWheelTimer} maintains a data structure called 'wheel'.
* To put simply, a wheel is a hash table of {@link TimerTask}s whose hash
- * function is 'dead line of the task'. The default number of ticks per wheel
+ * function is 'deadline of the task'. The default number of ticks per wheel
* (i.e. the size of the wheel) is 512. You could specify a larger value
* if you are going to schedule a lot of timeouts.
*
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
index 2695f2a..0362af7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -86,7 +86,7 @@
}
public URLAddress setHost(String host) {
- return new URLAddress(host, port, rawAddress);
+ return new URLAddress(host, port, null);
}
public int getPort() {
@@ -94,7 +94,7 @@
}
public URLAddress setPort(int port) {
- return new URLAddress(host, port, rawAddress);
+ return new URLAddress(host, port, null);
}
public String getAddress() {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
index 05deabe..243e1d4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -978,7 +978,7 @@
*
* @param params params map added into URLParam
* @param rawParam original rawParam string, directly add to rawParam field,
- * will not effect real key-pairs store in URLParam.
+ * will not affect real key-pairs store in URLParam.
* Please make sure it can correspond with params or will
* cause unexpected result when calling {@link URLParam#getRawParam()}
* and {@link URLParam#toString()} ()}. If you not sure, you can call
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java
index dc113e1..a57f53f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CompatibleTypeUtils.java
@@ -23,6 +23,7 @@
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
@@ -34,6 +35,11 @@
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ /**
+ * the text to parse such as "2007-12-03T10:15:30"
+ */
+ private static final int ISO_LOCAL_DATE_TIME_MIN_LEN = 19;
+
private CompatibleTypeUtils() {
}
@@ -128,7 +134,12 @@
if (StringUtils.isEmpty(string)) {
return null;
}
- return LocalDateTime.parse(string).toLocalTime();
+
+ if (string.length() >= ISO_LOCAL_DATE_TIME_MIN_LEN) {
+ return LocalDateTime.parse(string).toLocalTime();
+ } else {
+ return LocalTime.parse(string);
+ }
}
if (type == Class.class) {
try {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java
index 90e905a..425e064 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java
@@ -20,6 +20,12 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+/**
+ * A 'least recently used' cache based on LinkedHashMap.
+ *
+ * @param <K> key
+ * @param <V> value
+ */
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = -5167631809472116969L;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
index c4f24df..43656f0 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
@@ -57,11 +57,25 @@
/**
* IP and Port Helper for RPC
*/
-public class NetUtils {
+public final class NetUtils {
+
+ /**
+ * Forbids instantiation.
+ */
+ private NetUtils() {
+ throw new UnsupportedOperationException("No instance of 'NetUtils' for you! ");
+ }
private static Logger logger;
static {
+ /*
+ DO NOT replace this logger to error type aware logger (or fail-safe logger), since its
+ logging method calls NetUtils.getLocalHost().
+
+ According to issue #4992, getLocalHost() method will be endless recursively invoked when network disconnected.
+ */
+
logger = LoggerFactory.getLogger(NetUtils.class);
if (logger instanceof FailsafeLogger) {
logger = ((FailsafeLogger) logger).getLogger();
@@ -82,6 +96,7 @@
private static final Map<String, String> HOST_NAME_CACHE = new LRUCache<>(1000);
private static volatile InetAddress LOCAL_ADDRESS = null;
+ private static volatile Inet6Address LOCAL_ADDRESS_V6 = null;
private static final String SPLIT_IPV4_CHARACTER = "\\.";
private static final String SPLIT_IPV6_CHARACTER = ":";
@@ -96,15 +111,16 @@
return RND_PORT_START + ThreadLocalRandom.current().nextInt(RND_PORT_RANGE);
}
- public synchronized static int getAvailablePort() {
+ public static synchronized int getAvailablePort() {
int randomPort = getRandomPort();
return getAvailablePort(randomPort);
}
- public synchronized static int getAvailablePort(int port) {
- if (port < MIN_PORT) {
+ public static synchronized int getAvailablePort(int port) {
+ if (port < MIN_PORT) {
return MIN_PORT;
}
+
for (int i = port; i < MAX_PORT; i++) {
if (USED_PORT.get(i)) {
continue;
@@ -123,8 +139,8 @@
/**
* Check the port whether is in use in os
- * @param port
- * @return
+ * @param port port to check
+ * @return true if it's occupied
*/
public static boolean isPortInUsed(int port) {
try (ServerSocket ignored = new ServerSocket(port)) {
@@ -135,10 +151,24 @@
return true;
}
+ /**
+ * Tells whether the port to test is an invalid port.
+ *
+ * @implNote Numeric comparison only.
+ * @param port port to test
+ * @return true if invalid
+ */
public static boolean isInvalidPort(int port) {
return port < MIN_PORT || port > MAX_PORT;
}
+ /**
+ * Tells whether the address to test is an invalid address.
+ *
+ * @implNote Pattern matching only.
+ * @param address address to test
+ * @return true if invalid
+ */
public static boolean isValidAddress(String address) {
return ADDRESS_PATTERN.matcher(address).matches();
}
@@ -221,6 +251,8 @@
private static volatile String HOST_ADDRESS;
+ private static volatile String HOST_ADDRESS_V6;
+
public static String getLocalHost() {
if (HOST_ADDRESS != null) {
return HOST_ADDRESS;
@@ -228,11 +260,45 @@
InetAddress address = getLocalAddress();
if (address != null) {
- return HOST_ADDRESS = address.getHostAddress();
+ if (address instanceof Inet6Address) {
+ String ipv6AddressString = address.getHostAddress();
+ if (ipv6AddressString.contains("%")) {
+ ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
+ }
+ HOST_ADDRESS = ipv6AddressString;
+ return HOST_ADDRESS;
+ }
+
+ HOST_ADDRESS = address.getHostAddress();
+ return HOST_ADDRESS;
}
+
return LOCALHOST_VALUE;
}
+ public static String getLocalHostV6() {
+ if (StringUtils.isNotEmpty(HOST_ADDRESS_V6)) {
+ return HOST_ADDRESS_V6;
+ }
+ //avoid to search network interface card many times
+ if("".equals(HOST_ADDRESS_V6)){
+ return null;
+ }
+
+ Inet6Address address = getLocalAddressV6();
+ if (address != null) {
+ String ipv6AddressString = address.getHostAddress();
+ if (ipv6AddressString.contains("%")) {
+ ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
+ }
+
+ HOST_ADDRESS_V6 = ipv6AddressString;
+ return HOST_ADDRESS_V6;
+ }
+ HOST_ADDRESS_V6 = "";
+ return null;
+ }
+
public static String filterLocalHost(String host) {
if (host == null || host.length() == 0) {
return host;
@@ -278,6 +344,15 @@
return localAddress;
}
+ public static Inet6Address getLocalAddressV6() {
+ if (LOCAL_ADDRESS_V6 != null) {
+ return LOCAL_ADDRESS_V6;
+ }
+ Inet6Address localAddress = getLocalAddress0V6();
+ LOCAL_ADDRESS_V6 = localAddress;
+ return localAddress;
+ }
+
private static Optional<InetAddress> toValidAddress(InetAddress address) {
if (address instanceof Inet6Address) {
Inet6Address v6Address = (Inet6Address) address;
@@ -324,10 +399,34 @@
logger.warn(e);
}
+ localAddress = getLocalAddressV6();
return localAddress;
}
+ private static Inet6Address getLocalAddress0V6() {
+ // @since 2.7.6, choose the {@link NetworkInterface} first
+ try {
+ NetworkInterface networkInterface = findNetworkInterface();
+ Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ InetAddress address = addresses.nextElement();
+ if (address instanceof Inet6Address) {
+ if (!address.isLoopbackAddress() //filter 127.x.x.x
+ && !address.isAnyLocalAddress() // filter 0.0.0.0
+ && !address.isLinkLocalAddress() //filter 169.254.0.0/16
+ && address.getHostAddress().contains(":")) {//filter IPv6
+ return (Inet6Address) address;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e);
+ }
+
+ return null;
+ }
+
/**
* Returns {@code true} if the specified {@link NetworkInterface} should be ignored with the given conditions.
*
@@ -349,10 +448,10 @@
for (String ignoredInterface : ignoredInterfaces.split(",")) {
String trimIgnoredInterface = ignoredInterface.trim();
boolean matched = false;
- try {
+ try {
matched = networkInterfaceDisplayName.matches(trimIgnoredInterface);
} catch (PatternSyntaxException e) {
- // if trimIgnoredInterface is a invalid regular expression, a PatternSyntaxException will be thrown out
+ // if trimIgnoredInterface is an invalid regular expression, a PatternSyntaxException will be thrown out
logger.warn("exception occurred: " + networkInterfaceDisplayName + " matches " + trimIgnoredInterface, e);
} finally {
if (matched) {
@@ -472,6 +571,14 @@
return address;
}
+ public static String getLocalHostName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ return getLocalAddress().getHostName();
+ }
+ }
+
/**
* @param hostName
* @return ip address or hostName if UnknownHostException
@@ -513,19 +620,24 @@
return sb.toString();
}
+ @SuppressWarnings("deprecation")
public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws
IOException {
setInterface(multicastSocket, multicastAddress instanceof Inet6Address);
+
+ // For the deprecation notice: the equivalent only appears in JDK 9+.
multicastSocket.setLoopbackMode(false);
multicastSocket.joinGroup(multicastAddress);
}
+ @SuppressWarnings("deprecation")
public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException {
boolean interfaceSet = false;
for (NetworkInterface networkInterface : getValidNetworkInterfaces()) {
- Enumeration addresses = networkInterface.getInetAddresses();
+ Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+
while (addresses.hasMoreElements()) {
- InetAddress address = (InetAddress) addresses.nextElement();
+ InetAddress address = addresses.nextElement();
if (preferIpv6 && address instanceof Inet6Address) {
try {
if (address.isReachable(100)) {
@@ -595,7 +707,7 @@
splitCharacter = SPLIT_IPV6_CHARACTER;
}
String[] mask = pattern.split(splitCharacter);
- //check format of pattern
+ // check format of pattern
checkHostPattern(pattern, mask, isIpv4);
host = inetAddress.getHostAddress();
@@ -610,6 +722,7 @@
}
String[] ipAddress = host.split(splitCharacter);
+
for (int i = 0; i < mask.length; i++) {
if ("*".equals(mask[i]) || mask[i].equals(ipAddress[i])) {
continue;
@@ -699,4 +812,28 @@
return Integer.parseInt(ipSegment, 16);
}
+
+ public static boolean isIPV6URLStdFormat(String ip) {
+ if ((ip.charAt(0) == '[' && ip.indexOf(']') > 2)) {
+ return true;
+ } else if (ip.indexOf(":") != ip.lastIndexOf(":")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static String getLegalIP(String ip) {
+ //ipv6 [::FFFF:129.144.52.38]:80
+ int ind;
+ if ((ip.charAt(0) == '[' && (ind = ip.indexOf(']')) > 2)) {
+ String nhost = ip;
+ ip = nhost.substring(0, ind + 1);
+ ip = ip.substring(1, ind);
+ return ip;
+ } else {
+ return ip;
+ }
+ }
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
index c6e2eae..b8abfa6 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java
@@ -33,6 +33,9 @@
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -141,6 +144,10 @@
if (ReflectUtils.isPrimitives(pojo.getClass())) {
return pojo;
}
+
+ if (pojo instanceof LocalDate || pojo instanceof LocalDateTime || pojo instanceof LocalTime) {
+ return pojo.toString();
+ }
if (pojo instanceof Class) {
return ((Class) pojo).getName();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java
index cb5c2f0..50ddfdc 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java
@@ -17,6 +17,8 @@
package org.apache.dubbo.common.utils;
import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
@@ -36,6 +38,7 @@
private static volatile SerializeClassChecker INSTANCE = null;
+ private final boolean OPEN_CHECK_CLASS;
private final boolean BLOCK_ALL_CLASS_EXCEPT_ALLOW;
private final Set<String> CLASS_DESERIALIZE_ALLOWED_SET = new ConcurrentHashSet<>();
private final Set<String> CLASS_DESERIALIZE_BLOCKED_SET = new ConcurrentHashSet<>();
@@ -47,7 +50,11 @@
private final AtomicLong counter = new AtomicLong(0);
private SerializeClassChecker() {
- String blockAllClassExceptAllow = System.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false");
+ String openCheckClass = ConfigurationUtils.getProperty(CommonConstants.CLASS_DESERIALIZE_OPEN_CHECK, "true");
+ OPEN_CHECK_CLASS = Boolean.parseBoolean(openCheckClass);
+
+ String blockAllClassExceptAllow = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false");
+
BLOCK_ALL_CLASS_EXCEPT_ALLOW = Boolean.parseBoolean(blockAllClassExceptAllow);
String[] lines;
@@ -70,8 +77,8 @@
logger.error("Failed to load blocked class list! Will ignore default blocked list.", e);
}
- String allowedClassList = System.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT);
- String blockedClassList = System.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT);
+ String allowedClassList = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT);
+ String blockedClassList = ConfigurationUtils.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT);
if (StringUtils.isNotEmpty(allowedClassList)) {
String[] classStrings = allowedClassList.trim().split(",");
@@ -111,6 +118,10 @@
* @param name class name ( all are convert to lower case )
*/
public void validateClass(String name) {
+ if(!OPEN_CHECK_CLASS){
+ return;
+ }
+
name = name.toLowerCase(Locale.ROOT);
if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) {
return;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index f0246c0..0167438 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
@@ -64,9 +64,16 @@
public class UrlUtils {
/**
+ * Forbids the instantiation.
+ */
+ private UrlUtils() {
+ throw new UnsupportedOperationException("No instance of 'UrlUtils' for you! ");
+ }
+
+ /**
* in the url string,mark the param begin
*/
- private final static String URL_PARAM_STARTING_SYMBOL = "?";
+ private static final String URL_PARAM_STARTING_SYMBOL = "?";
public static URL parseURL(String address, Map<String, String> defaults) {
if (StringUtils.isEmpty(address)) {
@@ -212,7 +219,8 @@
}
public static Map<String, String> convertSubscribe(Map<String, String> subscribe) {
- Map<String, String> newSubscribe = new HashMap<String, String>();
+ Map<String, String> newSubscribe = new HashMap<>();
+
for (Map.Entry<String, String> entry : subscribe.entrySet()) {
String serviceName = entry.getKey();
String serviceQuery = entry.getValue();
@@ -234,11 +242,13 @@
newSubscribe.put(serviceName, serviceQuery);
}
}
+
return newSubscribe;
}
public static Map<String, Map<String, String>> revertRegister(Map<String, Map<String, String>> register) {
- Map<String, Map<String, String>> newRegister = new HashMap<String, Map<String, String>>();
+ Map<String, Map<String, String>> newRegister = new HashMap<>();
+
for (Map.Entry<String, Map<String, String>> entry : register.entrySet()) {
String serviceName = entry.getKey();
Map<String, String> serviceUrls = entry.getValue();
@@ -265,11 +275,14 @@
newRegister.put(serviceName, serviceUrls);
}
}
+
return newRegister;
}
public static Map<String, String> revertSubscribe(Map<String, String> subscribe) {
- Map<String, String> newSubscribe = new HashMap<String, String>();
+
+ Map<String, String> newSubscribe = new HashMap<>();
+
for (Map.Entry<String, String> entry : subscribe.entrySet()) {
String serviceName = entry.getKey();
String serviceQuery = entry.getValue();
@@ -385,32 +398,47 @@
public static boolean isMatch(URL consumerUrl, URL providerUrl) {
String consumerInterface = consumerUrl.getServiceInterface();
String providerInterface = providerUrl.getServiceInterface();
- //FIXME accept providerUrl with '*' as interface name, after carefully thought about all possible scenarios I think it's ok to add this condition.
+
+ // FIXME accept providerUrl with '*' as interface name, after carefully thought about all possible scenarios I think it's ok to add this condition.
+
+ // Return false if the consumer interface is not equals the provider interface,
+ // except one of the interface configurations is equals '*' (i.e. any value).
if (!(ANY_VALUE.equals(consumerInterface)
|| ANY_VALUE.equals(providerInterface)
|| StringUtils.isEquals(consumerInterface, providerInterface))) {
return false;
}
- if (!isMatchCategory(providerUrl.getCategory(DEFAULT_CATEGORY),
- consumerUrl.getCategory(DEFAULT_CATEGORY))) {
+ // If the category of provider URL does not match the category of consumer URL.
+ // Usually, the provider URL's category is empty, and the default category ('providers') is present.
+ // Hence, the category of the provider URL is 'providers'.
+ // Through observing of debugging process, I found that the category of the consumer URL is 'providers,configurators,routers'.
+ if (!isMatchCategory(providerUrl.getCategory(DEFAULT_CATEGORY), consumerUrl.getCategory(DEFAULT_CATEGORY))) {
return false;
}
+
+ // If the provider is not enabled, return false.
if (!providerUrl.getParameter(ENABLED_KEY, true)
&& !ANY_VALUE.equals(consumerUrl.getParameter(ENABLED_KEY))) {
return false;
}
+ // Obtain consumer's group, version and classifier.
String consumerGroup = consumerUrl.getGroup();
String consumerVersion = consumerUrl.getVersion();
String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
+ // Obtain provider's group, version and classifier.
String providerGroup = providerUrl.getGroup();
String providerVersion = providerUrl.getVersion();
String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
- return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
- && (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
- && (consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
+
+ // If Group, Version, Classifier all matches, return true.
+ boolean groupMatches = ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup);
+ boolean versionMatches = ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion);
+ boolean classifierMatches = consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier);
+
+ return groupMatches && versionMatches && classifierMatches;
}
public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index b341bdd..9996c91 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -52,7 +52,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -364,7 +363,7 @@
*/
protected static Map<String, String> convert(Map<String, String> parameters, String prefix) {
if (parameters == null || parameters.isEmpty()) {
- return Collections.emptyMap();
+ return new HashMap<>();
}
Map<String, String> result = new HashMap<>();
@@ -1023,7 +1022,7 @@
List<Method> methods = new ArrayList<>(beanInfo.getMethodDescriptors().length);
for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) {
Method method = methodDescriptor.getMethod();
- if (MethodUtils.isGetter(method)) {
+ if (MethodUtils.isGetter(method) || isParametersGetter(method)) {
// filter non attribute
Parameter parameter = method.getAnnotation(Parameter.class);
if (parameter != null && !parameter.attribute()) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
index 0491328..315dc36 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
@@ -235,9 +235,6 @@
if (this.monitor != null && this.monitor.getScopeModel() != applicationModel) {
this.monitor.setScopeModel(applicationModel);
}
- if (this.metadataReportConfig != null && this.metadataReportConfig.getScopeModel() != applicationModel) {
- this.metadataReportConfig.setScopeModel(applicationModel);
- }
if (CollectionUtils.isNotEmpty(this.registries)) {
this.registries.forEach(registryConfig -> {
if (registryConfig.getScopeModel() != applicationModel) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java
index dcada0c..f557f93 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractReferenceConfig.java
@@ -27,6 +27,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.ROUTER_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.STUB_EVENT_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
+import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDER_PORT;
/**
* AbstractConsumerConfig
@@ -76,18 +77,26 @@
protected Boolean stubevent;//= Constants.DEFAULT_STUB_EVENT;
-
/**
* declares which app or service this interface belongs to
*/
protected String providedBy;
+ /**
+ * By VirtualService and DestinationRule, envoy will generate a new route rule,such as 'demo.default.svc.cluster.local:80',the default port is 80.
+ * When you want to specify the provider port,you can use this config.
+ *
+ * @since 3.1.0
+ */
+ protected Integer providerPort;
+
protected String router;
/**
* Weather the reference is referred asynchronously
- * @deprecated
+ *
* @see ModuleConfig#referAsync
+ * @deprecated
*/
@Deprecated
private Boolean referAsync;
@@ -248,7 +257,6 @@
}
-
@Parameter(key = PROVIDED_BY)
public String getProvidedBy() {
return providedBy;
@@ -258,6 +266,15 @@
this.providedBy = providedBy;
}
+ @Parameter(key = PROVIDER_PORT)
+ public Integer getProviderPort() {
+ return providerPort;
+ }
+
+ public void setProviderPort(Integer providerPort) {
+ this.providerPort = providerPort;
+ }
+
@Parameter(key = ROUTER_KEY, append = true)
public String getRouter() {
return router;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java
index a3c3726..10faac7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ConsumerConfig.java
@@ -20,6 +20,7 @@
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.rpc.model.ModuleModel;
+import static org.apache.dubbo.common.constants.CommonConstants.MESH_ENABLE;
import static org.apache.dubbo.common.constants.CommonConstants.REFER_BACKGROUND_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.REFER_THREAD_NUM_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.URL_MERGE_PROCESSOR_KEY;
@@ -78,6 +79,12 @@
*/
private Boolean referBackground;
+ /**
+ * enable mesh mode
+ * @since 3.1.0
+ */
+ private Boolean meshEnable;
+
public ConsumerConfig() {
}
@@ -170,4 +177,13 @@
public void setReferBackground(Boolean referBackground) {
this.referBackground = referBackground;
}
+
+ @Parameter(key = MESH_ENABLE)
+ public Boolean getMeshEnable() {
+ return meshEnable;
+ }
+
+ public void setMeshEnable(Boolean meshEnable) {
+ this.meshEnable = meshEnable;
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
index ea8ca77..a379856 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
@@ -17,7 +17,6 @@
package org.apache.dubbo.config;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.config.nested.AggregationConfig;
import org.apache.dubbo.config.nested.PrometheusConfig;
@@ -27,9 +26,6 @@
import java.util.HashMap;
import java.util.Map;
-import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
-import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS;
-
/**
* MetricsConfig
*/
@@ -40,6 +36,11 @@
private String protocol;
/**
+ * Enable jvm metrics when collecting.
+ */
+ private Boolean enableJvmMetrics;
+
+ /**
* @deprecated After metrics config is refactored.
* This parameter should no longer use and will be deleted in the future.
*/
@@ -69,14 +70,11 @@
Map<String, String> map = new HashMap<>();
appendParameters(map, this);
- // use 'prometheus' as the default metrics service.
- if (StringUtils.isEmpty(map.get(PROTOCOL_KEY))) {
- map.put(PROTOCOL_KEY, PROTOCOL_PROMETHEUS);
- }
-
// ignore address parameter, use specified url in each metrics server config
// the address "localhost" here is meaningless
- return UrlUtils.parseURL("localhost", map);
+ URL url = UrlUtils.parseURL("localhost", map);
+ url = url.setScopeModel(getScopeModel());
+ return url;
}
public String getProtocol() {
@@ -87,6 +85,14 @@
this.protocol = protocol;
}
+ public Boolean getEnableJvmMetrics() {
+ return enableJvmMetrics;
+ }
+
+ public void setEnableJvmMetrics(Boolean enableJvmMetrics) {
+ this.enableJvmMetrics = enableJvmMetrics;
+ }
+
public String getPort() {
return port;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java
index f235f44..9ef6daf 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ProtocolConfig.java
@@ -200,6 +200,11 @@
private Boolean sslEnabled;
+ /*
+ * Extra Protocol for this service, using Port Unification Server
+ */
+ private String extProtocol;
+
public ProtocolConfig() {
}
@@ -551,4 +556,11 @@
return StringUtils.isNotEmpty(name);
}
+ public String getExtProtocol() {
+ return extProtocol;
+ }
+
+ public void setExtProtocol(String extProtocol) {
+ this.extProtocol = extProtocol;
+ }
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
index 3d16ce4..0138e06 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ReferenceConfigBase.java
@@ -38,6 +38,7 @@
import java.util.Properties;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.CommonConstants.UNLOAD_CLUSTER_RELATED;
/**
* ReferenceConfig
@@ -66,6 +67,12 @@
*/
protected ConsumerConfig consumer;
+ /**
+ * In the mesh mode, uninstall the directory, router and load balance related to the cluster in the currently invoked invoker.
+ * Delegate retry, load balancing, timeout and other traffic management capabilities to Sidecar.
+ */
+ protected Boolean unloadClusterRelated;
+
public ReferenceConfigBase() {
serviceMetadata = new ServiceMetadata();
serviceMetadata.addAttribute(ORIGIN_CONFIG, this);
@@ -257,6 +264,15 @@
this.consumer = consumer;
}
+ @Parameter(key = UNLOAD_CLUSTER_RELATED)
+ public Boolean getUnloadClusterRelated() {
+ return unloadClusterRelated;
+ }
+
+ public void setUnloadClusterRelated(Boolean unloadClusterRelated) {
+ this.unloadClusterRelated = unloadClusterRelated;
+ }
+
public ServiceMetadata getServiceMetadata() {
return serviceMetadata;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java
index 6b5c215..10afb4f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java
@@ -19,6 +19,7 @@
import org.apache.dubbo.common.constants.ClusterRules;
import org.apache.dubbo.common.constants.LoadbalanceRules;
import org.apache.dubbo.common.constants.RegistryConstants;
+import org.apache.dubbo.config.AbstractReferenceConfig;
import org.apache.dubbo.config.ReferenceConfigBase;
import java.lang.annotation.Documented;
@@ -32,7 +33,7 @@
* <p>
* <b>It is recommended to use @DubboReference on the @Bean method in the Java-config class, but not on the fields or setter methods to be injected.</b>
* </p>
- *
+ * <p>
* Step 1: Register ReferenceBean in Java-config class:
* <pre class="code">
* @Configuration
@@ -50,7 +51,7 @@
* }
* }
* </pre>
- *
+ * <p>
* Step 2: Inject ReferenceBean by @Autowired
* <pre class="code">
* public class FooController {
@@ -62,9 +63,9 @@
* }
* </pre>
*
- * @since 2.7.7
* @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder
* @see org.apache.dubbo.config.spring.ReferenceBean
+ * @since 2.7.7
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@@ -103,6 +104,7 @@
/**
* Whether to enable generic invocation, default value is false
+ *
* @deprecated Do not need specify generic value, judge by injection type and interface class
*/
@Deprecated
@@ -110,6 +112,7 @@
/**
* When enable, prefer to call local service in the same JVM if it's present, default value is true
+ *
* @deprecated using scope="local" or scope="remote" instead
*/
@Deprecated
@@ -122,6 +125,7 @@
/**
* Whether eager initialize the reference bean when all properties are set, default value is true ( null as true)
+ *
* @see ReferenceConfigBase#shouldInit()
*/
boolean init() default true;
@@ -270,6 +274,7 @@
/**
* Application name
+ *
* @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext
*/
@Deprecated
@@ -321,6 +326,7 @@
/**
* The id
* NOTE: The id attribute is ignored when using @DubboReference on @Bean method
+ *
* @return default value is empty
* @since 2.7.3
*/
@@ -337,12 +343,22 @@
/**
* declares which app or service this interface belongs to
+ *
* @see RegistryConstants#PROVIDED_BY
*/
String[] providedBy() default {};
/**
+ * The service port of the provider
+ *
+ * @see AbstractReferenceConfig#providerPort
+ * @since 3.1.0
+ */
+ int providerPort() default -1;
+
+ /**
* the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
+ *
* @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
* @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
*/
@@ -352,4 +368,12 @@
* Weather the reference is refer asynchronously
*/
boolean referAsync() default false;
+
+ /**
+ * unload Cluster related in mesh mode
+ *
+ * @see ReferenceConfigBase#unloadClusterRelated
+ * @since 3.1.0
+ */
+ boolean unloadClusterRelated() default false;
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeClassLoaderListener.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeClassLoaderListener.java
new file mode 100644
index 0000000..a2b7529
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeClassLoaderListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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.model;
+
+public interface ScopeClassLoaderListener<T extends ScopeModel> {
+
+ void onAddClassLoader(T scopeModel, ClassLoader classLoader);
+
+ void onRemoveClassLoader(T scopeModel, ClassLoader classLoader);
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
index b3f016c..514d3ca 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
@@ -70,6 +70,8 @@
private ScopeBeanFactory beanFactory;
private List<ScopeModelDestroyListener> destroyListeners;
+ private List<ScopeClassLoaderListener> classLoaderListeners;
+
private Map<String, Object> attributes;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private final boolean internalScope;
@@ -94,6 +96,7 @@
this.extensionDirector.addExtensionPostProcessor(new ScopeModelAwareExtensionProcessor(this));
this.beanFactory = new ScopeBeanFactory(parent != null ? parent.getBeanFactory() : null, extensionDirector);
this.destroyListeners = new LinkedList<>();
+ this.classLoaderListeners = new LinkedList<>();
this.attributes = new ConcurrentHashMap<>();
this.classLoaders = new ConcurrentHashSet<>();
@@ -142,12 +145,28 @@
}
}
+ protected void notifyClassLoaderAdd(ClassLoader classLoader) {
+ for (ScopeClassLoaderListener classLoaderListener : classLoaderListeners) {
+ classLoaderListener.onAddClassLoader(this, classLoader);
+ }
+ }
+
+ protected void notifyClassLoaderDestroy(ClassLoader classLoader) {
+ for (ScopeClassLoaderListener classLoaderListener : classLoaderListeners) {
+ classLoaderListener.onRemoveClassLoader(this, classLoader);
+ }
+ }
+
protected abstract void onDestroy();
public final void addDestroyListener(ScopeModelDestroyListener listener) {
destroyListeners.add(listener);
}
+ public final void addClassLoaderListener(ScopeClassLoaderListener listener) {
+ classLoaderListeners.add(listener);
+ }
+
public Map<String, Object> getAttributes() {
return attributes;
}
@@ -187,6 +206,7 @@
parent.addClassLoader(classLoader);
}
extensionDirector.removeAllCachedLoader();
+ notifyClassLoaderAdd(classLoader);
}
public void removeClassLoader(ClassLoader classLoader) {
@@ -196,6 +216,7 @@
parent.removeClassLoader(classLoader);
}
extensionDirector.removeAllCachedLoader();
+ notifyClassLoaderDestroy(classLoader);
}
}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyMatcherTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyMatcherTest.java
new file mode 100644
index 0000000..635045c
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyMatcherTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ProtocolServiceKeyMatcherTest {
+
+ @Test
+ public void testProtocol() {
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo"),
+ new ProtocolServiceKey(null, null, null, null)
+ ));
+
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo"),
+ new ProtocolServiceKey("DemoService", null, null, "dubbo")
+ ));
+
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, null),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, ""),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "*"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "dubbo1")
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "dubbo2")
+ ));
+
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"),
+ new ProtocolServiceKey(null, null, null, null)
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "")
+ ));
+
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, null)
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "")
+ ));
+
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"),
+ new ProtocolServiceKey(null, null, null, null)
+ ));
+ Assertions.assertTrue(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"),
+ new ProtocolServiceKey(null, null, null, "")
+ ));
+
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, ",dubbo1,dubbo2"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ Assertions.assertFalse(ProtocolServiceKey.Matcher.isMatch(
+ new ProtocolServiceKey(null, null, null, "dubbo1,dubbo2,"),
+ new ProtocolServiceKey(null, null, null, "dubbo")
+ ));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyTest.java
new file mode 100644
index 0000000..09b2711
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/ProtocolServiceKeyTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ProtocolServiceKeyTest {
+ @Test
+ public void test() {
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1");
+ Assertions.assertEquals("DemoService", protocolServiceKey.getInterfaceName());
+ Assertions.assertEquals("1.0.0", protocolServiceKey.getVersion());
+ Assertions.assertEquals("group1", protocolServiceKey.getGroup());
+ Assertions.assertEquals("protocol1", protocolServiceKey.getProtocol());
+
+ Assertions.assertEquals("group1/DemoService:1.0.0:protocol1", protocolServiceKey.toString());
+ Assertions.assertEquals("group1/DemoService:1.0.0", protocolServiceKey.getServiceKeyString());
+
+ Assertions.assertEquals(protocolServiceKey, protocolServiceKey);
+
+ ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1");
+ Assertions.assertEquals(protocolServiceKey, protocolServiceKey1);
+ Assertions.assertEquals(protocolServiceKey.hashCode(), protocolServiceKey1.hashCode());
+
+ ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2");
+ Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey2);
+
+ ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey("DemoService", "1.0.0", "group2", "protocol1");
+ Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey3);
+
+ ProtocolServiceKey protocolServiceKey4 = new ProtocolServiceKey("DemoService", "1.0.1", "group1", "protocol1");
+ Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey4);
+
+ ProtocolServiceKey protocolServiceKey5 = new ProtocolServiceKey("DemoInterface", "1.0.0", "group1", "protocol1");
+ Assertions.assertNotEquals(protocolServiceKey, protocolServiceKey5);
+
+ ServiceKey serviceKey = new ServiceKey("DemoService", "1.0.0", "group1");
+ Assertions.assertNotEquals(protocolServiceKey, serviceKey);
+
+ Assertions.assertTrue(protocolServiceKey.isSameWith(protocolServiceKey));
+ Assertions.assertTrue(protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "")));
+ Assertions.assertTrue(protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", null)));
+
+ Assertions.assertFalse(protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", "protocol1")));
+ Assertions.assertFalse(protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", "")));
+ Assertions.assertFalse(protocolServiceKey.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group2", null)));
+
+
+ ProtocolServiceKey protocolServiceKey6 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", null);
+ Assertions.assertTrue(protocolServiceKey6.isSameWith(protocolServiceKey6));
+ Assertions.assertTrue(protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "")));
+ Assertions.assertTrue(protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1")));
+ Assertions.assertTrue(protocolServiceKey6.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2")));
+
+ ProtocolServiceKey protocolServiceKey7 = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "*");
+ Assertions.assertFalse(protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", null)));
+ Assertions.assertFalse(protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "")));
+ Assertions.assertFalse(protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1")));
+ Assertions.assertFalse(protocolServiceKey7.isSameWith(new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol2")));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyMatcherTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyMatcherTest.java
new file mode 100644
index 0000000..6d85c46
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyMatcherTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ServiceKeyMatcherTest {
+
+ @Test
+ public void testInterface() {
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey("DemoService", null, null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, null),
+ new ServiceKey("DemoService", null, null)
+ ));
+
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey("*", null, null),
+ new ServiceKey("DemoService", null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey("*", null, null),
+ new ServiceKey(null, null, null)
+ ));
+ }
+
+ @Test
+ public void testVersion() {
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0", null),
+ new ServiceKey(null, "1.0.0", null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, null),
+ new ServiceKey(null, "1.0.0", null)
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1", null),
+ new ServiceKey(null, "1.0.0", null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1", null),
+ new ServiceKey(null, "1.0.2", null)
+ ));
+
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1", null),
+ new ServiceKey(null, null, null)
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, ",1.0.0,1.0.1", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, ",1.0.0,1.0.1", null),
+ new ServiceKey(null, "", null)
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,,1.0.1", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,,1.0.1", null),
+ new ServiceKey(null, "", null)
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1,", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1,", null),
+ new ServiceKey(null, "", null)
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "1.0.0,1.0.1", null),
+ new ServiceKey(null, "", null)
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, ",1.0.0,1.0.1", null),
+ new ServiceKey(null, "1.0.2", null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, ",1.0.0,1.0.1", null),
+ new ServiceKey(null, "1.0.2", null)
+ ));
+
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "*", null),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "*", null),
+ new ServiceKey(null, "", null)
+ ));
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, "*", null),
+ new ServiceKey(null, "1.0.0", null)
+ ));
+ }
+
+ @Test
+ public void testGroup() {
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group1"),
+ new ServiceKey(null, null, "group1")
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group1"),
+ new ServiceKey(null, null, null)
+ ));
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, null),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group1, group2"),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2, group3"),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2, group3"),
+ new ServiceKey(null, null, null)
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2, group3"),
+ new ServiceKey(null, null, "")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, ",group2"),
+ new ServiceKey(null, null, "")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2,"),
+ new ServiceKey(null, null, "")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2, ,group3"),
+ new ServiceKey(null, null, "")
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, ",group2"),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2,"),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertFalse(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "group2, ,group3"),
+ new ServiceKey(null, null, "group1")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "*"),
+ new ServiceKey(null, null, "")
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "*"),
+ new ServiceKey(null, null, null)
+ ));
+
+ Assertions.assertTrue(ServiceKey.Matcher.isMatch(
+ new ServiceKey(null, null, "*"),
+ new ServiceKey(null, null, "group1")
+ ));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyTest.java
new file mode 100644
index 0000000..5643166
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/ServiceKeyTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ServiceKeyTest {
+ @Test
+ public void test() {
+ ServiceKey serviceKey = new ServiceKey("DemoService", "1.0.0", "group1");
+
+ Assertions.assertEquals("DemoService", serviceKey.getInterfaceName());
+ Assertions.assertEquals("1.0.0", serviceKey.getVersion());
+ Assertions.assertEquals("group1", serviceKey.getGroup());
+
+ Assertions.assertEquals("group1/DemoService:1.0.0", serviceKey.toString());
+ Assertions.assertEquals("DemoService", new ServiceKey("DemoService", null, null).toString());
+ Assertions.assertEquals("DemoService:1.0.0", new ServiceKey("DemoService", "1.0.0", null).toString());
+ Assertions.assertEquals("group1/DemoService", new ServiceKey("DemoService", null, "group1").toString());
+
+ Assertions.assertEquals(serviceKey, serviceKey);
+
+ ServiceKey serviceKey1 = new ServiceKey("DemoService", "1.0.0", "group1");
+ Assertions.assertEquals(serviceKey, serviceKey1);
+ Assertions.assertEquals(serviceKey.hashCode(), serviceKey1.hashCode());
+
+ ServiceKey serviceKey2 = new ServiceKey("DemoService", "1.0.0", "group2");
+ Assertions.assertNotEquals(serviceKey, serviceKey2);
+
+ ServiceKey serviceKey3 = new ServiceKey("DemoService", "1.0.1", "group1");
+ Assertions.assertNotEquals(serviceKey, serviceKey3);
+
+ ServiceKey serviceKey4 = new ServiceKey("DemoInterface", "1.0.0", "group1");
+ Assertions.assertNotEquals(serviceKey, serviceKey4);
+
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey("DemoService", "1.0.0", "group1", "protocol1");
+ Assertions.assertNotEquals(serviceKey, protocolServiceKey);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
index f295de9..fdd3533 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLStrParserTest.java
@@ -43,6 +43,7 @@
testCases.add("dubbo://192.168.1.1/" + RandomString.make(10240));
testCases.add("file:/path/to/file.txt");
testCases.add("dubbo://fe80:0:0:0:894:aeec:f37d:23e1%en0/path?abc=abc");
+ testCases.add("dubbo://[fe80:0:0:0:894:aeec:f37d:23e1]:20880/path?abc=abc");
errorDecodedCases.add("dubbo:192.168.1.1");
errorDecodedCases.add("://192.168.1.1");
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
index e78ab5d..033901b 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
@@ -18,7 +18,6 @@
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.CollectionUtils;
-
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -1082,4 +1081,13 @@
URL urlWithoutUserInformation = URL.valueOf("10.20.130.230:20880/context/path?version=1.0.0&application=app1");
assertEquals("10.20.130.230:20880", urlWithoutUserInformation.getAuthority());
}
+
+
+ @Test
+ public void testIPV6() {
+ URL url = URL.valueOf("dubbo://[2408:4004:194:8896:3e8a:82ae:814a:398]:20881?name=apache");
+ assertEquals("[2408:4004:194:8896:3e8a:82ae:814a:398]", url.getHost());
+ assertEquals(20881, url.getPort());
+ assertEquals("apache", url.getParameter("name"));
+ }
}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/Bean.java b/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/Bean.java
index e17a538..4ea53cb 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/Bean.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/Bean.java
@@ -1,12 +1,3 @@
-package org.apache.dubbo.common.beanutil;
-
-import org.apache.dubbo.rpc.model.person.FullAddress;
-import org.apache.dubbo.rpc.model.person.PersonStatus;
-import org.apache.dubbo.rpc.model.person.Phone;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -23,6 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.apache.dubbo.common.beanutil;
+
+import org.apache.dubbo.rpc.model.person.FullAddress;
+import org.apache.dubbo.rpc.model.person.PersonStatus;
+import org.apache.dubbo.rpc.model.person.Phone;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
public class Bean {
private Class<?> type;
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java
index b2c119c..927501f 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java
@@ -25,10 +25,10 @@
import java.net.URL;
import java.nio.file.Paths;
-public class FileCacheStoreFactoryTest {
+class FileCacheStoreFactoryTest {
@Test
- public void testSafeName() throws URISyntaxException {
+ void testSafeName() throws URISyntaxException {
FileCacheStore store1 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "../../../dubbo");
Assertions.assertEquals(getDirectoryOfClassPath() + "..%002f..%002f..%002fdubbo.dubbo.cache", store1.getCacheFilePath());
store1.destroy();
@@ -39,7 +39,7 @@
}
@Test
- public void testPathIsFile() throws URISyntaxException, IOException {
+ void testPathIsFile() throws URISyntaxException, IOException {
String basePath = getDirectoryOfClassPath();
String filePath = basePath + File.separator + "isFile";
new File(filePath).createNewFile();
@@ -48,7 +48,7 @@
}
@Test
- public void testCacheContains() throws URISyntaxException {
+ void testCacheContains() throws URISyntaxException {
FileCacheStore store1 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "testCacheContains");
Assertions.assertNotNull(store1.getCacheFilePath());
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
index 6d8bf11..1c2a878 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
@@ -26,11 +26,11 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
-public class FileCacheStoreTest {
- FileCacheStore cacheStore;
+class FileCacheStoreTest {
+ private FileCacheStore cacheStore;
@Test
- public void testCache() throws Exception {
+ void testCache() throws Exception {
String directoryPath = getDirectoryOfClassPath();
String filePath = "test-cache.dubbo.cache";
cacheStore = FileCacheStoreFactory.getInstance(directoryPath, filePath);
@@ -54,7 +54,7 @@
}
@Test
- public void testFileSizeExceed() throws Exception {
+ void testFileSizeExceed() throws Exception {
String directoryPath = getDirectoryOfClassPath();
Map<String, String> newProperties = new HashMap<>();
newProperties.put("newKey1", "newValue1");
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
index b507188..3ac7b90 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.common.config;
+import org.apache.dubbo.common.beanutil.JavaBeanAccessor;
+
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -23,9 +25,10 @@
import java.util.HashMap;
import java.util.Map;
+import java.util.NoSuchElementException;
/**
- * The type Inmemory configuration test.
+ * Unit test of class InmemoryConfiguration, and interface Configuration.
*/
class InmemoryConfigurationTest {
@@ -41,7 +44,6 @@
*/
@BeforeEach
public void init() {
-
memConfig = new InmemoryConfiguration();
}
@@ -49,7 +51,7 @@
* Test get mem property.
*/
@Test
- public void testGetMemProperty() {
+ void testGetMemProperty() {
Assertions.assertNull(memConfig.getInternalProperty(MOCK_KEY));
Assertions.assertFalse(memConfig.containsKey(MOCK_KEY));
Assertions.assertNull(memConfig.getString(MOCK_KEY));
@@ -65,7 +67,7 @@
* Test get properties.
*/
@Test
- public void testGetProperties() {
+ void testGetProperties() {
Assertions.assertNull(memConfig.getInternalProperty(MOCK_ONE_KEY));
Assertions.assertNull(memConfig.getInternalProperty(MOCK_TWO_KEY));
Map<String, String> proMap = new HashMap<>();
@@ -84,7 +86,7 @@
}
@Test
- public void testGetInt() {
+ void testGetInt() {
memConfig.addProperty("a", "1");
Assertions.assertEquals(1, memConfig.getInt("a"));
Assertions.assertEquals(Integer.valueOf(1), memConfig.getInteger("a", 2));
@@ -92,13 +94,63 @@
}
@Test
- public void getBoolean() {
+ void getBoolean() {
memConfig.addProperty("a", Boolean.TRUE.toString());
Assertions.assertTrue(memConfig.getBoolean("a"));
Assertions.assertFalse(memConfig.getBoolean("b", false));
Assertions.assertTrue(memConfig.getBoolean("b", Boolean.TRUE));
}
+ @Test
+ void testIllegalType() {
+ memConfig.addProperty("it", "aaa");
+
+ Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInteger("it", 1));
+ Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInt("it", 1));
+ Assertions.assertThrows(IllegalStateException.class, () -> memConfig.getInt("it"));
+ }
+
+ @Test
+ void testDoesNotExist() {
+ Assertions.assertThrows(NoSuchElementException.class, () -> memConfig.getInt("ne"));
+ Assertions.assertThrows(NoSuchElementException.class, () -> memConfig.getBoolean("ne"));
+ }
+
+ @Test
+ void testConversions() {
+ memConfig.addProperty("long", "2147483648");
+ memConfig.addProperty("byte", "127");
+ memConfig.addProperty("short", "32767");
+ memConfig.addProperty("float", "3.14");
+ memConfig.addProperty("double", "3.14159265358979323846264338327950");
+ memConfig.addProperty("enum", "FIELD");
+
+ Object longObject = memConfig.convert(Long.class, "long", 1L);
+ Object byteObject = memConfig.convert(Byte.class, "byte", (byte) 1);
+ Object shortObject = memConfig.convert(Short.class, "short", (short) 1);
+ Object floatObject = memConfig.convert(Float.class, "float", 3.14F);
+ Object doubleObject = memConfig.convert(Double.class, "double", 3.14159265358979323846264338327950);
+ JavaBeanAccessor javaBeanAccessor = memConfig.convert(JavaBeanAccessor.class, "enum", JavaBeanAccessor.ALL);
+
+ Assertions.assertEquals(Long.class, longObject.getClass());
+ Assertions.assertEquals(2147483648L, longObject);
+
+ Assertions.assertEquals(Byte.class, byteObject.getClass());
+ Assertions.assertEquals((byte) 127, byteObject);
+
+ Assertions.assertEquals(Short.class, shortObject.getClass());
+ Assertions.assertEquals((short) 32767, shortObject);
+
+ Assertions.assertEquals(Float.class, floatObject.getClass());
+ Assertions.assertEquals(3.14F, floatObject);
+
+ Assertions.assertEquals(Double.class, doubleObject.getClass());
+ Assertions.assertEquals(3.14159265358979323846264338327950, doubleObject);
+
+ Assertions.assertEquals(JavaBeanAccessor.class, javaBeanAccessor.getClass());
+ Assertions.assertEquals(JavaBeanAccessor.FIELD, javaBeanAccessor);
+ }
+
/**
* Clean.
*/
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
index 2fd6516..cb34f71 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
@@ -42,8 +42,8 @@
/**
* {@link FileSystemDynamicConfiguration} Test
*/
-// Test often failed on Github Actions Platform because of file system on Azure
-// Change to Disabled because DisabledIfEnvironmentVariable does not work on Github.
+// Test often failed on GitHub Actions Platform because of file system on Azure
+// Change to Disabled because DisabledIfEnvironmentVariable does not work on GitHub.
@Disabled
public class FileSystemDynamicConfigurationTest {
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
index c83765f..70a7ce8 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/LoggerFactoryTest.java
@@ -68,4 +68,12 @@
assertThat(logger1, is(logger2));
}
+
+ @Test
+ public void shouldReturnSameErrorTypeAwareLogger() {
+ ErrorTypeAwareLogger logger1 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName());
+ ErrorTypeAwareLogger logger2 = LoggerFactory.getErrorTypeAwareLogger(this.getClass().getName());
+
+ assertThat(logger1, is(logger2));
+ }
}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java
new file mode 100644
index 0000000..6eb3605
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/logger/support/FailsafeErrorTypeAwareLoggerTest.java
@@ -0,0 +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.common.logger.support;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for FailsafeErrorTypeAwareLogger to test whether it 'ignores' exceptions thrown by logger or not.
+ */
+public class FailsafeErrorTypeAwareLoggerTest {
+ @Test
+ public void testFailsafeErrorTypeAwareForLoggingMethod() {
+ Logger failLogger = mock(Logger.class);
+ FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger);
+
+ doThrow(new RuntimeException()).when(failLogger).error(anyString());
+ doThrow(new RuntimeException()).when(failLogger).warn(anyString());
+ doThrow(new RuntimeException()).when(failLogger).info(anyString());
+ doThrow(new RuntimeException()).when(failLogger).debug(anyString());
+ doThrow(new RuntimeException()).when(failLogger).trace(anyString());
+
+ failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error");
+ failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn");
+
+ doThrow(new RuntimeException()).when(failLogger).error(any(Throwable.class));
+ doThrow(new RuntimeException()).when(failLogger).warn(any(Throwable.class));
+ doThrow(new RuntimeException()).when(failLogger).info(any(Throwable.class));
+ doThrow(new RuntimeException()).when(failLogger).debug(any(Throwable.class));
+ doThrow(new RuntimeException()).when(failLogger).trace(any(Throwable.class));
+
+ failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error", new Exception("error"));
+ failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn", new Exception("warn"));
+ }
+
+ @Test
+ public void testSuccessLogger() {
+ Logger successLogger = mock(Logger.class);
+ FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(successLogger);
+
+ failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error");
+ failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn");
+
+ verify(successLogger).error(anyString());
+ verify(successLogger).warn(anyString());
+
+ failsafeLogger.error("1-1", "Registry center", "May be it's offline.", "error", new Exception("error"));
+ failsafeLogger.warn("1-1", "Registry center", "May be it's offline.", "warn", new Exception("warn"));
+ }
+
+ @Test
+ public void testGetLogger() {
+ Assertions.assertThrows(RuntimeException.class, () -> {
+ Logger failLogger = mock(Logger.class);
+ FailsafeErrorTypeAwareLogger failsafeLogger = new FailsafeErrorTypeAwareLogger(failLogger);
+
+ doThrow(new RuntimeException()).when(failLogger).error(anyString());
+ failsafeLogger.getLogger().error("should get error");
+ });
+ }
+
+ @Test
+ public void testInstructionShownOrNot() {
+ LoggerFactory.setLoggerAdapter(FrameworkModel.defaultModel(), "jdk");
+
+ ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(FailsafeErrorTypeAwareLoggerTest.class);
+
+ logger.error("1-1", "Registry center", "May be it's offline.",
+ "error message", new Exception("error"));
+
+ logger.error("-1", "Registry center", "May be it's offline.",
+ "error message", new Exception("error"));
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollectorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollectorTest.java
new file mode 100644
index 0000000..1dcd227
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/collector/DefaultMetricsCollectorTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.metrics.collector;
+
+import org.apache.dubbo.common.metrics.event.MetricsEvent;
+import org.apache.dubbo.common.metrics.event.RTEvent;
+import org.apache.dubbo.common.metrics.event.RequestEvent;
+import org.apache.dubbo.common.metrics.listener.MetricsListener;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
+
+public class DefaultMetricsCollectorTest {
+
+ private ApplicationModel applicationModel;
+ private String interfaceName;
+ private String methodName;
+ private String group;
+ private String version;
+
+ @BeforeEach
+ public void setup() {
+ ApplicationConfig config = new ApplicationConfig();
+ config.setName("MockMetrics");
+
+ applicationModel = ApplicationModel.defaultModel();
+ applicationModel.getApplicationConfigManager().setApplication(config);
+
+ interfaceName = "org.apache.dubbo.MockInterface";
+ methodName = "mockMethod";
+ group = "mockGroup";
+ version = "1.0.0";
+ }
+
+ @AfterEach
+ public void teardown() {
+ applicationModel.destroy();
+ }
+
+ @Test
+ public void testRequestsMetrics() {
+ DefaultMetricsCollector collector = new DefaultMetricsCollector(applicationModel);
+ collector.setCollectEnabled(true);
+ collector.increaseTotalRequests(interfaceName, methodName, group, version);
+ collector.increaseProcessingRequests(interfaceName, methodName, group, version);
+ collector.increaseSucceedRequests(interfaceName, methodName, group, version);
+ collector.increaseFailedRequests(interfaceName, methodName, group, version);
+
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ Assertions.assertTrue(sample instanceof GaugeMetricSample);
+ GaugeMetricSample gaugeSample = (GaugeMetricSample) sample;
+ Map<String, String> tags = gaugeSample.getTags();
+ Supplier<Number> supplier = gaugeSample.getSupplier();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+ Assertions.assertEquals(supplier.get().longValue(), 1);
+ }
+
+ collector.decreaseProcessingRequests(interfaceName, methodName, group, version);
+ samples = collector.collect();
+ Map<String, Long> sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+ Number number = ((GaugeMetricSample) k).getSupplier().get();
+ return number.longValue();
+ }));
+
+ Assertions.assertEquals(sampleMap.get("requests.processing"), 0L);
+ }
+
+ @Test
+ public void testRTMetrics() {
+ DefaultMetricsCollector collector = new DefaultMetricsCollector(applicationModel);
+ collector.setCollectEnabled(true);
+ collector.addRT(interfaceName, methodName, group, version, 10L);
+ collector.addRT(interfaceName, methodName, group, version, 0L);
+
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+ }
+
+ Map<String, Long> sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+ Number number = ((GaugeMetricSample) k).getSupplier().get();
+ return number.longValue();
+ }));
+
+ Assertions.assertEquals(sampleMap.get("rt.last"), 0L);
+ Assertions.assertEquals(sampleMap.get("rt.min"), 0L);
+ Assertions.assertEquals(sampleMap.get("rt.max"), 10L);
+ Assertions.assertEquals(sampleMap.get("rt.avg"), 5L);
+ Assertions.assertEquals(sampleMap.get("rt.total"), 10L);
+ }
+
+ @Test
+ public void testListener() {
+ DefaultMetricsCollector collector = new DefaultMetricsCollector(applicationModel);
+ collector.setCollectEnabled(true);
+
+ MockListener mockListener = new MockListener();
+ collector.addListener(mockListener);
+
+ collector.increaseTotalRequests(interfaceName, methodName, group, version);
+ Assertions.assertNotNull(mockListener.getCurEvent());
+ Assertions.assertTrue(mockListener.getCurEvent() instanceof RequestEvent);
+ Assertions.assertEquals(((RequestEvent) mockListener.getCurEvent()).getType(), RequestEvent.Type.TOTAL);
+
+ collector.addRT(interfaceName, methodName, group, version, 5L);
+ Assertions.assertTrue(mockListener.getCurEvent() instanceof RTEvent);
+ Assertions.assertEquals(((RTEvent) mockListener.getCurEvent()).getRt(), 5L);
+ }
+
+ static class MockListener implements MetricsListener {
+
+ private MetricsEvent curEvent;
+
+ @Override
+ public void onEvent(MetricsEvent event) {
+ curEvent = event;
+ }
+
+ public MetricsEvent getCurEvent() {
+ return curEvent;
+ }
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RTEventTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RTEventTest.java
new file mode 100644
index 0000000..fea3e0e
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RTEventTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common.metrics.event;
+
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RTEventTest {
+
+ @Test
+ public void testNewEvent() {
+ MethodMetric metric = new MethodMetric();
+ Long rt = 5L;
+ RTEvent event = new RTEvent(metric, rt);
+
+ Assertions.assertEquals(event.getSource(), metric);
+ Assertions.assertEquals(event.getRt(), rt);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RequestEventTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RequestEventTest.java
new file mode 100644
index 0000000..3da2f3a
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/event/RequestEventTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common.metrics.event;
+
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RequestEventTest {
+
+ @Test
+ public void testNewEvent() {
+ MethodMetric metric = new MethodMetric();
+ RequestEvent.Type type = RequestEvent.Type.TOTAL;
+ RequestEvent event = new RequestEvent(metric, type);
+
+ Assertions.assertEquals(event.getSource(), metric);
+ Assertions.assertEquals(event.getType(), type);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/MethodMetricTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/MethodMetricTest.java
new file mode 100644
index 0000000..f4dbcf2
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/MethodMetricTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.metrics.model;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.*;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
+
+public class MethodMetricTest {
+
+ private static final String applicationName = null;
+ private static String interfaceName;
+ private static String methodName;
+ private static String group;
+ private static String version;
+
+ @BeforeAll
+ public static void setup() {
+ interfaceName = "org.apache.dubbo.MockInterface";
+ methodName = "mockMethod";
+ group = "mockGroup";
+ version = "1.0.0";
+ }
+
+ @Test
+ public void test() {
+ MethodMetric metric = new MethodMetric(applicationName, interfaceName, methodName, group, version);
+ Assertions.assertEquals(metric.getInterfaceName(), interfaceName);
+ Assertions.assertEquals(metric.getMethodName(), methodName);
+ Assertions.assertEquals(metric.getGroup(), group);
+ Assertions.assertEquals(metric.getVersion(), version);
+
+ Map<String, String> tags = metric.getTags();
+ Assertions.assertEquals(tags.get(TAG_IP), getLocalHost());
+ Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName());
+ Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), applicationName);
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSampleTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSampleTest.java
new file mode 100644
index 0000000..d45ee03
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/GaugeMetricSampleTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.metrics.model.sample;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class GaugeMetricSampleTest {
+
+ private static String name;
+ private static String description;
+ private static Map<String, String> tags;
+ private static MetricsCategory category;
+ private static String baseUnit;
+ private static Supplier<Number> supplier;
+
+ @BeforeAll
+ public static void setup() {
+ name = "test";
+ description = "test";
+ tags = new HashMap<>();
+ category = MetricsCategory.REQUESTS;
+ baseUnit = "byte";
+ supplier = () -> 1;
+ }
+
+ @Test
+ public void test() {
+ GaugeMetricSample sample = new GaugeMetricSample(name, description, tags, category, baseUnit, supplier);
+ Assertions.assertEquals(sample.getName(), name);
+ Assertions.assertEquals(sample.getDescription(), description);
+ Assertions.assertEquals(sample.getTags(), tags);
+ Assertions.assertEquals(sample.getType(), MetricSample.Type.GAUGE);
+ Assertions.assertEquals(sample.getCategory(), category);
+ Assertions.assertEquals(sample.getBaseUnit(), baseUnit);
+ Assertions.assertEquals(sample.getSupplier().get(), 1);
+ sample.setSupplier(() -> 2);
+ Assertions.assertEquals(sample.getSupplier().get(), 2);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/MetricSampleTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/MetricSampleTest.java
new file mode 100644
index 0000000..78b5753
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/model/sample/MetricSampleTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.metrics.model.sample;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MetricSampleTest {
+
+ private static String name;
+ private static String description;
+ private static Map<String, String> tags;
+ private static MetricSample.Type type;
+ private static MetricsCategory category;
+ private static String baseUnit;
+
+ @BeforeAll
+ public static void setup() {
+ name = "test";
+ description = "test";
+ tags = new HashMap<>();
+ type = MetricSample.Type.GAUGE;
+ category = MetricsCategory.REQUESTS;
+ baseUnit = "byte";
+ }
+
+ @Test
+ public void test() {
+ MetricSample sample = new MetricSample(name, description, tags, type, category, baseUnit);
+ Assertions.assertEquals(sample.getName(), name);
+ Assertions.assertEquals(sample.getDescription(), description);
+ Assertions.assertEquals(sample.getTags(), tags);
+ Assertions.assertEquals(sample.getType(), type);
+ Assertions.assertEquals(sample.getCategory(), category);
+ Assertions.assertEquals(sample.getBaseUnit(), baseUnit);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/service/MetricsEntityTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/service/MetricsEntityTest.java
new file mode 100644
index 0000000..c2688ce
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/metrics/service/MetricsEntityTest.java
@@ -0,0 +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.common.metrics.service;
+
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MetricsEntityTest {
+
+ private static String name;
+ private static Map<String, String> tags;
+ private static MetricsCategory category;
+ private static Object value;
+
+ @BeforeAll
+ public static void setup() {
+ name = "test";
+ tags = new HashMap<>();
+ category = MetricsCategory.REQUESTS;
+ value = 1;
+ }
+
+ @Test
+ public void test() {
+ MetricsEntity entity = new MetricsEntity(name, tags, category, value);
+ Assertions.assertEquals(entity.getName(), name);
+ Assertions.assertEquals(entity.getTags(), tags);
+ Assertions.assertEquals(entity.getCategory(), category);
+ Assertions.assertEquals(entity.getValue(), value);
+ }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
index 9719191..79f01df 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/NetUtilsTest.java
@@ -42,51 +42,51 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class NetUtilsTest {
+class NetUtilsTest {
@Test
- public void testGetRandomPort() throws Exception {
+ void testGetRandomPort() throws Exception {
assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000));
assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000));
assertThat(NetUtils.getRandomPort(), greaterThanOrEqualTo(30000));
}
@Test
- public void testGetAvailablePort() throws Exception {
+ void testGetAvailablePort() throws Exception {
assertThat(NetUtils.getAvailablePort(), greaterThan(0));
assertThat(NetUtils.getAvailablePort(12345), greaterThanOrEqualTo(12345));
assertThat(NetUtils.getAvailablePort(-1), greaterThanOrEqualTo(0));
}
@Test
- public void testValidAddress() throws Exception {
+ void testValidAddress() throws Exception {
assertTrue(NetUtils.isValidAddress("10.20.130.230:20880"));
assertFalse(NetUtils.isValidAddress("10.20.130.230"));
assertFalse(NetUtils.isValidAddress("10.20.130.230:666666"));
}
@Test
- public void testIsInvalidPort() throws Exception {
+ void testIsInvalidPort() throws Exception {
assertTrue(NetUtils.isInvalidPort(0));
assertTrue(NetUtils.isInvalidPort(65536));
assertFalse(NetUtils.isInvalidPort(1024));
}
@Test
- public void testIsLocalHost() throws Exception {
+ void testIsLocalHost() throws Exception {
assertTrue(NetUtils.isLocalHost("localhost"));
assertTrue(NetUtils.isLocalHost("127.1.2.3"));
assertFalse(NetUtils.isLocalHost("128.1.2.3"));
}
@Test
- public void testIsAnyHost() throws Exception {
+ void testIsAnyHost() throws Exception {
assertTrue(NetUtils.isAnyHost("0.0.0.0"));
assertFalse(NetUtils.isAnyHost("1.1.1.1"));
}
@Test
- public void testIsInvalidLocalHost() throws Exception {
+ void testIsInvalidLocalHost() throws Exception {
assertTrue(NetUtils.isInvalidLocalHost(null));
assertTrue(NetUtils.isInvalidLocalHost(""));
assertTrue(NetUtils.isInvalidLocalHost("localhost"));
@@ -97,13 +97,13 @@
}
@Test
- public void testIsValidLocalHost() throws Exception {
+ void testIsValidLocalHost() throws Exception {
assertTrue(NetUtils.isValidLocalHost("1.2.3.4"));
assertTrue(NetUtils.isValidLocalHost("128.0.0.1"));
}
@Test
- public void testGetLocalSocketAddress() throws Exception {
+ void testGetLocalSocketAddress() throws Exception {
InetSocketAddress address = NetUtils.getLocalSocketAddress("localhost", 12345);
assertTrue(address.getAddress().isAnyLocalAddress());
assertEquals(address.getPort(), 12345);
@@ -113,7 +113,7 @@
}
@Test
- public void testIsValidAddress() throws Exception {
+ void testIsValidAddress() throws Exception {
assertFalse(NetUtils.isValidV4Address((InetAddress) null));
InetAddress address = mock(InetAddress.class);
when(address.isLoopbackAddress()).thenReturn(true);
@@ -133,19 +133,19 @@
}
@Test
- public void testGetLocalHost() throws Exception {
+ void testGetLocalHost() throws Exception {
assertNotNull(NetUtils.getLocalHost());
}
@Test
- public void testGetLocalAddress() throws Exception {
+ void testGetLocalAddress() throws Exception {
InetAddress address = NetUtils.getLocalAddress();
assertNotNull(address);
assertTrue(NetUtils.isValidLocalHost(address.getHostAddress()));
}
@Test
- public void testFilterLocalHost() throws Exception {
+ void testFilterLocalHost() throws Exception {
assertNull(NetUtils.filterLocalHost(null));
assertEquals(NetUtils.filterLocalHost(""), "");
String host = NetUtils.filterLocalHost("dubbo://127.0.0.1:8080/foo");
@@ -159,18 +159,18 @@
}
@Test
- public void testGetHostName() throws Exception {
+ void testGetHostName() throws Exception {
assertNotNull(NetUtils.getHostName("127.0.0.1"));
}
@Test
- public void testGetIpByHost() throws Exception {
+ void testGetIpByHost() throws Exception {
assertThat(NetUtils.getIpByHost("localhost"), equalTo("127.0.0.1"));
assertThat(NetUtils.getIpByHost("dubbo.local"), equalTo("dubbo.local"));
}
@Test
- public void testToAddressString() throws Exception {
+ void testToAddressString() throws Exception {
InetAddress address = mock(InetAddress.class);
when(address.getHostAddress()).thenReturn("dubbo");
InetSocketAddress socketAddress = new InetSocketAddress(address, 1234);
@@ -178,7 +178,7 @@
}
@Test
- public void testToAddress() throws Exception {
+ void testToAddress() throws Exception {
InetSocketAddress address = NetUtils.toAddress("localhost:1234");
assertThat(address.getHostName(), equalTo("localhost"));
assertThat(address.getPort(), equalTo(1234));
@@ -188,13 +188,13 @@
}
@Test
- public void testToURL() throws Exception {
+ void testToURL() throws Exception {
String url = NetUtils.toURL("dubbo", "host", 1234, "foo");
assertThat(url, equalTo("dubbo://host:1234/foo"));
}
@Test
- public void testIsValidV6Address() {
+ void testIsValidV6Address() {
String saved = System.getProperty("java.net.preferIPv6Addresses", "false");
System.setProperty("java.net.preferIPv6Addresses", "true");
@@ -212,11 +212,11 @@
* Mockito starts to support mocking final classes since 2.1.0
* see https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#unmockable
* But enable it will cause other UT to fail.
- * Therefore currently disabling this UT.
+ * Therefore, currently disabling this UT.
*/
@Disabled
@Test
- public void testNormalizeV6Address() {
+ void testNormalizeV6Address() {
Inet6Address address = mock(Inet6Address.class);
when(address.getHostAddress()).thenReturn("fe80:0:0:0:894:aeec:f37d:23e1%en0");
when(address.getScopeId()).thenReturn(5);
@@ -225,7 +225,7 @@
}
@Test
- public void testMatchIpRangeMatchWhenIpv4() throws UnknownHostException {
+ void testMatchIpRangeMatchWhenIpv4() throws UnknownHostException {
assertTrue(NetUtils.matchIpRange("*.*.*.*", "192.168.1.63", 90));
assertTrue(NetUtils.matchIpRange("192.168.1.*", "192.168.1.63", 90));
assertTrue(NetUtils.matchIpRange("192.168.1.63", "192.168.1.63", 90));
@@ -235,7 +235,7 @@
}
@Test
- public void testMatchIpRangeMatchWhenIpv6() throws UnknownHostException {
+ void testMatchIpRangeMatchWhenIpv6() throws UnknownHostException {
assertTrue(NetUtils.matchIpRange("*.*.*.*", "192.168.1.63", 90));
assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:*", "234e:0:4567::3d:ff", 90));
assertTrue(NetUtils.matchIpRange("234e:0:4567:0:0:0:3d:ee", "234e:0:4567::3d:ee", 90));
@@ -248,7 +248,7 @@
}
@Test
- public void testMatchIpRangeMatchWhenIpv6Exception() throws UnknownHostException {
+ void testMatchIpRangeMatchWhenIpv6Exception() throws UnknownHostException {
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () ->
NetUtils.matchIpRange("234e:0:4567::3d:*", "234e:0:4567::3d:ff", 90));
@@ -265,7 +265,7 @@
}
@Test
- public void testMatchIpRangeMatchWhenIpWrongException() throws UnknownHostException {
+ void testMatchIpRangeMatchWhenIpWrongException() throws UnknownHostException {
UnknownHostException thrown =
assertThrows(UnknownHostException.class, () ->
NetUtils.matchIpRange("192.168.1.63", "192.168.1.ff", 90));
@@ -273,13 +273,13 @@
}
@Test
- public void testMatchIpMatch() throws UnknownHostException {
+ void testMatchIpMatch() throws UnknownHostException {
assertTrue(NetUtils.matchIpExpression("192.168.1.*", "192.168.1.63", 90));
assertTrue(NetUtils.matchIpExpression("192.168.1.192/26", "192.168.1.199", 90));
}
@Test
- public void testMatchIpv6WithIpPort() throws UnknownHostException {
+ void testMatchIpv6WithIpPort() throws UnknownHostException {
assertTrue(NetUtils.matchIpRange("[234e:0:4567::3d:ee]", "234e:0:4567::3d:ee", 8090));
assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee]", "234e:0:4567::3d:ee", 8090));
assertTrue(NetUtils.matchIpRange("[234e:0:4567:0:0:0:3d:ee]:8090", "234e:0:4567::3d:ee", 8090));
@@ -292,7 +292,7 @@
}
@Test
- public void testMatchIpv4WithIpPort() throws UnknownHostException {
+ void testMatchIpv4WithIpPort() throws UnknownHostException {
NumberFormatException thrown =
assertThrows(NumberFormatException.class, () -> NetUtils.matchIpExpression("192.168.1.192/26:90", "192.168.1.199", 90));
assertTrue(thrown instanceof NumberFormatException);
@@ -314,25 +314,25 @@
}
@Test
- public void testLocalHost() {
+ void testLocalHost() {
assertEquals(NetUtils.getLocalHost(), NetUtils.getLocalAddress().getHostAddress());
assertTrue(NetUtils.isValidLocalHost(NetUtils.getLocalHost()));
assertFalse(NetUtils.isInvalidLocalHost(NetUtils.getLocalHost()));
}
@Test
- public void testIsMulticastAddress() {
+ void testIsMulticastAddress() {
assertTrue(NetUtils.isMulticastAddress("224.0.0.1"));
assertFalse(NetUtils.isMulticastAddress("127.0.0.1"));
}
@Test
- public void testFindNetworkInterface() {
+ void testFindNetworkInterface() {
assertNotNull(NetUtils.findNetworkInterface());
}
@Test
- public void testIgnoreAllInterfaces() {
+ void testIgnoreAllInterfaces() {
// store the origin ignored interfaces
String originIgnoredInterfaces = this.getIgnoredInterfaces();
try {
@@ -346,7 +346,7 @@
}
@Test
- public void testIgnoreGivenInterface() {
+ void testIgnoreGivenInterface() {
// store the origin ignored interfaces
String originIgnoredInterfaces = this.getIgnoredInterfaces();
try {
@@ -365,7 +365,7 @@
}
@Test
- public void testIgnoreGivenPrefixInterfaceName() {
+ void testIgnoreGivenPrefixInterfaceName() {
// store the origin ignored interfaces
String originIgnoredInterfaces = this.getIgnoredInterfaces();
try {
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
index e615dd9..6a0f1ef 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java
@@ -32,6 +32,9 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -760,6 +763,22 @@
assertEquals(setResult, setStr);
}
+ @Test
+ public void testJava8Time() {
+
+ Object localDateTimeGen = PojoUtils.generalize(LocalDateTime.now());
+ Object localDateTime = PojoUtils.realize(localDateTimeGen, LocalDateTime.class);
+ assertEquals(localDateTimeGen, localDateTime.toString());
+
+ Object localDateGen = PojoUtils.generalize(LocalDate.now());
+ Object localDate = PojoUtils.realize(localDateGen, LocalDate.class);
+ assertEquals(localDateGen, localDate.toString());
+
+ Object localTimeGen = PojoUtils.generalize(LocalTime.now());
+ Object localTime = PojoUtils.realize(localTimeGen, LocalTime.class);
+ assertEquals(localTimeGen, localTime.toString());
+ }
+
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
diff --git a/dubbo-compatible/pom.xml b/dubbo-compatible/pom.xml
index d520448..bbadadb 100644
--- a/dubbo-compatible/pom.xml
+++ b/dubbo-compatible/pom.xml
@@ -66,6 +66,12 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-multicast</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java
new file mode 100644
index 0000000..dfc8d86
--- /dev/null
+++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java
@@ -0,0 +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.gen.tri.reactive;
+
+import com.salesforce.jprotoc.ProtocPlugin;
+import org.apache.dubbo.gen.AbstractGenerator;
+
+public class ReactorDubbo3TripleGenerator extends AbstractGenerator {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ ProtocPlugin.generate(new ReactorDubbo3TripleGenerator());
+ } else {
+ ProtocPlugin.debug(new ReactorDubbo3TripleGenerator(), args[0]);
+ }
+ }
+
+ @Override
+ protected String getClassPrefix() {
+ return "Dubbo";
+ }
+
+ @Override
+ protected String getClassSuffix() {
+ return "Triple";
+ }
+
+ @Override
+ protected String getTemplateFileName() {
+ return "ReactorDubbo3TripleStub.mustache";
+ }
+
+ @Override
+ protected String getInterfaceTemplateFileName() {
+ return "ReactorDubbo3TripleInterfaceStub.mustache";
+ }
+
+ @Override
+ protected String getSingleTemplateFileName() {
+ throw new IllegalStateException("Do not support single template!");
+ }
+
+ @Override
+ protected boolean enableMultipleTemplateFiles() {
+ return true;
+ }
+}
diff --git a/dubbo-compiler/src/main/resources/ReactorDubbo3TripleInterfaceStub.mustache b/dubbo-compiler/src/main/resources/ReactorDubbo3TripleInterfaceStub.mustache
new file mode 100644
index 0000000..b3b9008
--- /dev/null
+++ b/dubbo-compiler/src/main/resources/ReactorDubbo3TripleInterfaceStub.mustache
@@ -0,0 +1,41 @@
+/*
+* 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.
+*/
+
+{{#packageName}}
+package {{packageName}};
+{{/packageName}}
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface {{interfaceClassName}} {
+
+ String JAVA_SERVICE_NAME = "{{packageName}}.{{serviceName}}";
+
+ String SERVICE_NAME = "{{commonPackageName}}.{{serviceName}}";
+
+{{#methods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ {{#deprecated}}
+ @java.lang.Deprecated
+ {{/deprecated}}
+ {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> reactorRequest) ;
+
+{{/methods}}
+}
diff --git a/dubbo-compiler/src/main/resources/ReactorDubbo3TripleStub.mustache b/dubbo-compiler/src/main/resources/ReactorDubbo3TripleStub.mustache
new file mode 100644
index 0000000..afc799b
--- /dev/null
+++ b/dubbo-compiler/src/main/resources/ReactorDubbo3TripleStub.mustache
@@ -0,0 +1,180 @@
+/*
+* 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.
+*/
+
+{{#packageName}}
+package {{packageName}};
+{{/packageName}}
+
+import com.google.protobuf.Message;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.PathResolver;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.ServerService;
+import org.apache.dubbo.rpc.TriRpcStatus;
+import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.StubMethodDescriptor;
+import org.apache.dubbo.rpc.model.StubServiceDescriptor;
+import org.apache.dubbo.reactive.handler.ManyToManyMethodHandler;
+import org.apache.dubbo.reactive.handler.ManyToOneMethodHandler;
+import org.apache.dubbo.reactive.handler.OneToManyMethodHandler;
+import org.apache.dubbo.reactive.calls.ReactorClientCalls;
+import org.apache.dubbo.reactive.handler.OneToOneMethodHandler;
+
+import org.apache.dubbo.rpc.stub.StubInvoker;
+import org.apache.dubbo.rpc.stub.StubMethodHandler;
+import org.apache.dubbo.rpc.stub.StubSuppliers;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class {{className}} {
+
+ private {{className}}() {}
+
+ public static final String SERVICE_NAME = {{interfaceClassName}}.SERVICE_NAME;
+
+ private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME,{{interfaceClassName}}.class);
+
+ static {
+ StubSuppliers.addSupplier(SERVICE_NAME, {{className}}::newStub);
+ StubSuppliers.addSupplier({{interfaceClassName}}.JAVA_SERVICE_NAME, {{className}}::newStub);
+ StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor);
+ StubSuppliers.addDescriptor({{interfaceClassName}}.JAVA_SERVICE_NAME, serviceDescriptor);
+ }
+
+ @SuppressWarnings("all")
+ public static {{interfaceClassName}} newStub(Invoker<?> invoker) {
+ return new {{interfaceClassName}}Stub((Invoker<{{interfaceClassName}}>)invoker);
+ }
+
+{{#unaryMethods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}",
+ {{inputType}}.class, {{outputType}}.class, serviceDescriptor, MethodDescriptor.RpcType.UNARY,
+ obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom,
+ {{outputType}}::parseFrom);
+{{/unaryMethods}}
+
+{{#serverStreamingMethods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}",
+ {{inputType}}.class, {{outputType}}.class, serviceDescriptor, MethodDescriptor.RpcType.SERVER_STREAM,
+ obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom,
+ {{outputType}}::parseFrom);
+{{/serverStreamingMethods}}
+
+{{#clientStreamingMethods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}",
+ {{inputType}}.class, {{outputType}}.class, serviceDescriptor, MethodDescriptor.RpcType.CLIENT_STREAM,
+ obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom,
+ {{outputType}}::parseFrom);
+{{/clientStreamingMethods}}
+
+{{#biStreamingWithoutClientStreamMethods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ private static final StubMethodDescriptor {{methodName}}Method = new StubMethodDescriptor("{{originMethodName}}",
+ {{inputType}}.class, {{outputType}}.class, serviceDescriptor, MethodDescriptor.RpcType.BI_STREAM,
+ obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom,
+ {{outputType}}::parseFrom);
+{{/biStreamingWithoutClientStreamMethods}}
+
+ public static class {{interfaceClassName}}Stub implements {{interfaceClassName}}{
+
+ private final Invoker<{{interfaceClassName}}> invoker;
+
+ public {{interfaceClassName}}Stub(Invoker<{{interfaceClassName}}> invoker) {
+ this.invoker = invoker;
+ }
+
+ {{#methods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ {{#deprecated}}
+ @java.lang.Deprecated
+ {{/deprecated}}
+ public {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> request) {
+ return ReactorClientCalls.{{reactiveCallsMethodName}}(invoker, request, {{methodNameCamelCase}}Method);
+ }
+ {{/methods}}
+ }
+
+ public static abstract class {{interfaceClassName}}ImplBase implements {{interfaceClassName}}, ServerService<{{interfaceClassName}}> {
+
+ @Override
+ public final Invoker<{{interfaceClassName}}> getInvoker(URL url) {
+ PathResolver pathResolver = url.getOrDefaultFrameworkModel()
+ .getExtensionLoader(PathResolver.class)
+ .getDefaultExtension();
+ Map<String,StubMethodHandler<?, ?>> handlers = new HashMap<>();
+
+ {{#methods}}
+ pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}" );
+ {{/methods}}
+
+ {{#unaryMethods}}
+ handlers.put({{methodName}}Method.getMethodName(), new OneToOneMethodHandler<>(this::{{methodName}}));
+ {{/unaryMethods}}
+ {{#serverStreamingMethods}}
+ handlers.put({{methodName}}Method.getMethodName(), new OneToManyMethodHandler<>(this::{{methodName}}));
+ {{/serverStreamingMethods}}
+ {{#clientStreamingMethods}}
+ handlers.put({{methodName}}Method.getMethodName(), new ManyToOneMethodHandler<>(this::{{methodName}}));
+ {{/clientStreamingMethods}}
+ {{#biStreamingWithoutClientStreamMethods}}
+ handlers.put({{methodName}}Method.getMethodName(), new ManyToManyMethodHandler<>(this::{{methodName}}));
+ {{/biStreamingWithoutClientStreamMethods}}
+
+ return new StubInvoker<>(this, url, {{interfaceClassName}}.class, handlers);
+ }
+
+ {{#methods}}
+ {{#javaDoc}}
+ {{{javaDoc}}}
+ {{/javaDoc}}
+ {{#deprecated}}
+ @java.lang.Deprecated
+ {{/deprecated}}
+ public {{#isManyOutput}}Flux{{/isManyOutput}}{{^isManyOutput}}Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}Flux{{/isManyInput}}{{^isManyInput}}Mono{{/isManyInput}}<{{inputType}}> request) {
+ throw unimplementedMethodException({{methodName}}Method);
+ }
+ {{/methods}}
+
+ @Override
+ public final ServiceDescriptor getServiceDescriptor() {
+ return serviceDescriptor;
+ }
+
+ private RpcException unimplementedMethodException(StubMethodDescriptor methodDescriptor) {
+ return TriRpcStatus.UNIMPLEMENTED.withDescription(String.format("Method %s is unimplemented",
+ "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())).asException();
+ }
+ }
+}
diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml
index 45de7cb..2baac24 100644
--- a/dubbo-config/dubbo-config-api/pom.xml
+++ b/dubbo-config/dubbo-config-api/pom.xml
@@ -98,6 +98,13 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-multicast</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
index ffdd3fa..6e023d3 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
@@ -18,13 +18,15 @@
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.concurrent.atomic.AtomicBoolean;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_SHUTDOWN_HOOK;
+
/**
* The shutdown hook thread to do the cleanup stuff.
* This is a singleton in order to ensure there is only one shutdown hook registered.
@@ -33,7 +35,7 @@
*/
public class DubboShutdownHook extends Thread {
- private static final Logger logger = LoggerFactory.getLogger(DubboShutdownHook.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboShutdownHook.class);
private final ApplicationModel applicationModel;
@@ -86,9 +88,9 @@
try {
Runtime.getRuntime().addShutdownHook(this);
} catch (IllegalStateException e) {
- logger.warn("register shutdown hook failed: " + e.getMessage());
+ logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "register shutdown hook failed: " + e.getMessage(), e);
} catch (Exception e) {
- logger.warn("register shutdown hook failed: " + e.getMessage(), e);
+ logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "register shutdown hook failed: " + e.getMessage(), e);
}
}
}
@@ -105,9 +107,9 @@
try {
Runtime.getRuntime().removeShutdownHook(this);
} catch (IllegalStateException e) {
- logger.warn("unregister shutdown hook failed: " + e.getMessage());
+ logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "unregister shutdown hook failed: " + e.getMessage(), e);
} catch (Exception e) {
- logger.warn("unregister shutdown hook failed: " + e.getMessage(), e);
+ logger.warn(CONFIG_FAILED_SHUTDOWN_HOOK, "", "", "unregister shutdown hook failed: " + e.getMessage(), e);
}
}
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 931adce..4f81b5d 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@ -21,7 +21,7 @@
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.constants.RegistryConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.ArrayUtils;
@@ -59,6 +59,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
@@ -66,14 +67,26 @@
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLUSTER_DOMAIN;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_MESH_PORT;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.MESH_ENABLE;
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROXY_CLASS_REF;
import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SEMICOLON_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.SVC;
+import static org.apache.dubbo.common.constants.CommonConstants.TRIPLE;
+import static org.apache.dubbo.common.constants.CommonConstants.UNLOAD_CLUSTER_RELATED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_VALID_PROVIDER;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_DESTROY_INVOKER;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_METHOD_FOUND;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_LOAD_ENV_VARIABLE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_PROPERTY_CONFLICT;
+import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
import static org.apache.dubbo.common.utils.NetUtils.isInvalidLocalHost;
import static org.apache.dubbo.common.utils.StringUtils.splitToSet;
@@ -91,7 +104,7 @@
*/
public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
- public static final Logger logger = LoggerFactory.getLogger(ReferenceConfig.class);
+ public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ReferenceConfig.class);
/**
* The {@link Protocol} implementation with adaptive functionality,it will be different in different scenarios.
@@ -236,7 +249,7 @@
invoker.destroy();
}
} catch (Throwable t) {
- logger.warn("Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);
+ logger.warn(CONFIG_FAILED_DESTROY_INVOKER, "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);
}
invoker = null;
ref = null;
@@ -247,7 +260,7 @@
}
protected synchronized void init() {
- if (initialized && ref !=null ) {
+ if (initialized && ref != null) {
return;
}
try {
@@ -275,7 +288,7 @@
serviceDescriptor = repository.registerService(interfaceClass);
}
consumerModel = new ConsumerModel(serviceMetadata.getServiceKey(), proxy, serviceDescriptor,
- getScopeModel(), serviceMetadata, createAsyncMethodInfo(), interfaceClassLoader);
+ getScopeModel(), serviceMetadata, createAsyncMethodInfo(), interfaceClassLoader);
// Compatible with dependencies on ServiceModel#getReferenceConfig() , and will be removed in a future version.
consumerModel.setConfig(this);
@@ -300,7 +313,7 @@
invoker.destroy();
}
} catch (Throwable destroy) {
- logger.warn("Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", destroy);
+ logger.warn(CONFIG_FAILED_DESTROY_INVOKER, "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);
}
if (consumerModel != null) {
ModuleServiceRepository repository = getScopeModel().getServiceRepository();
@@ -313,6 +326,14 @@
serviceMetadata.setTarget(null);
serviceMetadata.getAttributeMap().remove(PROXY_CLASS_REF);
+ // Thrown by checkInvokerAvailable().
+ if (t.getClass() == IllegalStateException.class &&
+ t.getMessage().contains("No provider available for the service")) {
+
+ // 2-2 - No provider available.
+ logger.error(CLUSTER_NO_VALID_PROVIDER, "server crashed", "", "No provider available.", t);
+ }
+
throw t;
}
initialized = true;
@@ -365,7 +386,7 @@
String[] methods = methods(interfaceClass);
if (methods.length == 0) {
- logger.warn("No method found in service interface " + interfaceClass.getName());
+ logger.warn(CONFIG_NO_METHOD_FOUND, "", "", "No method found in service interface: " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<>(Arrays.asList(methods)), COMMA_SEPARATOR));
@@ -383,7 +404,7 @@
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException(
- "Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
+ "Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
@@ -410,6 +431,9 @@
createInvokerForLocal(referenceParameters);
} else {
urls.clear();
+
+ meshModeHandleUrl(referenceParameters);
+
if (StringUtils.isNotEmpty(url)) {
// user specified URL, could be peer-to-peer address, or register center's address.
parseUrl(referenceParameters);
@@ -424,12 +448,12 @@
if (logger.isInfoEnabled()) {
logger.info("Referred dubbo service: [" + referenceParameters.get(INTERFACE_KEY) + "]." +
- (Boolean.parseBoolean(referenceParameters.get(GENERIC_KEY)) ?
- " it's GenericService reference" : " it's not GenericService reference"));
+ (Boolean.parseBoolean(referenceParameters.get(GENERIC_KEY)) ?
+ " it's GenericService reference" : " it's not GenericService reference"));
}
URL consumerUrl = new ServiceConfigURL(CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0,
- referenceParameters.get(INTERFACE_KEY), referenceParameters);
+ referenceParameters.get(INTERFACE_KEY), referenceParameters);
consumerUrl = consumerUrl.setScopeModel(getScopeModel());
consumerUrl = consumerUrl.setServiceModel(consumerModel);
MetadataUtils.publishServiceDefinition(consumerUrl, consumerModel.getServiceModel(), getApplicationModel());
@@ -439,6 +463,70 @@
}
/**
+ * if enable mesh mode, handle url.
+ *
+ * @param referenceParameters referenceParameters
+ */
+ private void meshModeHandleUrl(Map<String, String> referenceParameters) {
+ if (!checkMeshConfig(referenceParameters)) {
+ return;
+ }
+ if (StringUtils.isNotEmpty(url)) {
+ // user specified URL, could be peer-to-peer address, or register center's address.
+ if (logger.isInfoEnabled()) {
+ logger.info("The url already exists, mesh no longer processes url: " + url);
+ }
+ return;
+ }
+ // get pod namespace
+ String podNamespace;
+ if (StringUtils.isEmpty(System.getenv("POD_NAMESPACE"))) {
+ if (logger.isWarnEnabled()) {
+ logger.warn(CONFIG_FAILED_LOAD_ENV_VARIABLE, "", "", "Can not get env variable: POD_NAMESPACE, it may not be running in the K8S environment , " +
+ "finally use 'default' replace.");
+ }
+ podNamespace = "default";
+ } else {
+ podNamespace = System.getenv("POD_NAMESPACE");
+ }
+
+ // In mesh mode, providedBy equals K8S Service name.
+ String providedBy = referenceParameters.get(PROVIDED_BY);
+ // cluster_domain default is 'cluster.local',generally unchanged.
+ String clusterDomain = Optional.ofNullable(System.getenv("CLUSTER_DOMAIN")).orElse(DEFAULT_CLUSTER_DOMAIN);
+ // By VirtualService and DestinationRule, envoy will generate a new route rule,such as 'demo.default.svc.cluster.local:80',the default port is 80.
+ Integer meshPort = Optional.ofNullable(getProviderPort()).orElse(DEFAULT_MESH_PORT);
+ // DubboReference default is -1, process it.
+ meshPort = meshPort > -1 ? meshPort : DEFAULT_MESH_PORT;
+ // get mesh url.
+ url = TRIPLE + "://" + providedBy + "." + podNamespace + SVC + clusterDomain + ":" + meshPort;
+ }
+
+ /**
+ * check if mesh config is correct
+ *
+ * @param referenceParameters referenceParameters
+ * @return mesh config is correct
+ */
+ private boolean checkMeshConfig(Map<String, String> referenceParameters) {
+ if (!"true".equals(referenceParameters.getOrDefault(MESH_ENABLE, "false"))) {
+ // In mesh mode, unloadClusterRelated can only be false.
+ referenceParameters.put(UNLOAD_CLUSTER_RELATED, "false");
+ return false;
+ }
+
+ getScopeModel().getConfigManager().getProtocol(TRIPLE)
+ .orElseThrow(() -> new IllegalStateException("In mesh mode, a triple protocol must be specified"));
+
+ String providedBy = referenceParameters.get(PROVIDED_BY);
+ if (StringUtils.isEmpty(providedBy)) {
+ throw new IllegalStateException("In mesh mode, the providedBy of ReferenceConfig is must be set");
+ }
+
+ return true;
+ }
+
+ /**
* Make a local reference, create a local invoker.
*
* @param referenceParameters
@@ -498,9 +586,9 @@
}
if (urls.isEmpty()) {
throw new IllegalStateException(
- "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
- " use dubbo version " + Version.getVersion() +
- ", please config <dubbo:registry address=\"...\" /> to your spring config.");
+ "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
+ " use dubbo version " + Version.getVersion() +
+ ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
@@ -513,7 +601,9 @@
if (urls.size() == 1) {
URL curUrl = urls.get(0);
invoker = protocolSPI.refer(interfaceClass, curUrl);
- if (!UrlUtils.isRegistry(curUrl)) {
+ // registry url, mesh-enable and unloadClusterRelated is true, not need Cluster.
+ if (!UrlUtils.isRegistry(curUrl) &&
+ !curUrl.getParameter(UNLOAD_CLUSTER_RELATED, false)) {
List<Invoker<?>> invokers = new ArrayList<>();
invokers.add(invoker);
invoker = Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(new StaticDirectory(curUrl, invokers), true);
@@ -553,7 +643,9 @@
private void checkInvokerAvailable() throws IllegalStateException {
if (shouldCheck() && !invoker.isAvailable()) {
- throw new IllegalStateException("Failed to check the status of the service "
+ // 2-2 - No provider available.
+
+ IllegalStateException illegalStateException = new IllegalStateException("Failed to check the status of the service "
+ interfaceName
+ ". No provider available for the service "
+ (group == null ? "" : group + "/")
@@ -563,6 +655,10 @@
+ invoker.getUrl()
+ " to the consumer "
+ NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
+
+ logger.error(CLUSTER_NO_VALID_PROVIDER, "provider not started", "", "No provider available.", illegalStateException);
+
+ throw illegalStateException;
}
}
@@ -580,7 +676,7 @@
// init some null configuration.
List<ConfigInitializer> configInitializers = this.getExtensionLoader(ConfigInitializer.class)
- .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
+ .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
configInitializers.forEach(e -> e.initReferConfig(this));
if (getGeneric() == null && getConsumer() != null) {
@@ -588,7 +684,7 @@
}
if (ProtocolUtils.isGeneric(generic)) {
if (interfaceClass != null && !interfaceClass.equals(GenericService.class)) {
- logger.warn(String.format("Found conflicting attributes for interface type: [interfaceClass=%s] and [generic=%s], " +
+ logger.warn(CONFIG_PROPERTY_CONFLICT, "", "", String.format("Found conflicting attributes for interface type: [interfaceClass=%s] and [generic=%s], " +
"because the 'generic' attribute has higher priority than 'interfaceClass', so change 'interfaceClass' to '%s'. " +
"Note: it will make this reference bean as a candidate bean of type '%s' instead of '%s' when resolving dependency in Spring.",
interfaceClass.getName(), generic, GenericService.class.getName(), GenericService.class.getName(), interfaceClass.getName()));
@@ -600,7 +696,7 @@
interfaceClass = Class.forName(interfaceName, true, getInterfaceClassLoader());
} else if (interfaceClass == null) {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
- .getContextClassLoader());
+ .getContextClassLoader());
}
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
@@ -657,7 +753,7 @@
private void postProcessConfig() {
List<ConfigPostProcessor> configPostProcessors = this.getExtensionLoader(ConfigPostProcessor.class)
- .getActivateExtension(URL.valueOf("configPostProcessor://"), (String[]) null);
+ .getActivateExtension(URL.valueOf("configPostProcessor://"), (String[]) null);
configPostProcessors.forEach(component -> component.postProcessReferConfig(this));
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index af2f4cc..054f679 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -21,13 +21,14 @@
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker;
@@ -70,6 +71,11 @@
import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_NAME_MAPPING_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_NO_METHOD_FOUND;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNEXPORT_ERROR;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_USE_RANDOM_PORT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXPORT_SERVICE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_SERVER_DISCONNECTED;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort;
@@ -83,6 +89,7 @@
import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
+import static org.apache.dubbo.remoting.Constants.IS_PU_SERVER_KEY;
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
import static org.apache.dubbo.rpc.Constants.LOCAL_PROTOCOL;
import static org.apache.dubbo.rpc.Constants.PROXY_KEY;
@@ -97,7 +104,7 @@
private static final long serialVersionUID = 7868244018230856253L;
- private static final Logger logger = LoggerFactory.getLogger(ServiceConfig.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceConfig.class);
/**
* A random port cache, the different protocols who have no port specified have different random port
@@ -181,7 +188,7 @@
try {
exporter.unexport();
} catch (Throwable t) {
- logger.warn("Unexpected error occured when unexport " + exporter, t);
+ logger.warn(CONFIG_UNEXPORT_ERROR, "", "", "Unexpected error occurred when unexport " + exporter, t);
}
}
exporters.clear();
@@ -242,7 +249,7 @@
try {
doExport();
} catch (Exception e) {
- logger.error("Failed to export service config: " + interfaceName, e);
+ logger.error(CONFIG_FAILED_EXPORT_SERVICE, "configuration server disconnected", "", "Failed to (async)export service config: " + interfaceName, e);
}
}, getDelay(), TimeUnit.MILLISECONDS);
}
@@ -258,10 +265,10 @@
if (succeeded) {
logger.info("Successfully registered interface application mapping for service " + url.getServiceKey());
} else {
- logger.error("Failed register interface application mapping for service " + url.getServiceKey());
+ logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey());
}
} catch (Exception e) {
- logger.error("Failed register interface application mapping for service " + url.getServiceKey(), e);
+ logger.error(CONFIG_SERVER_DISCONNECTED, "configuration server disconnected", "", "Failed register interface application mapping for service " + url.getServiceKey(), e);
}
}
});
@@ -447,7 +454,7 @@
String[] methods = methods(interfaceClass);
if (methods.length == 0) {
- logger.warn("No method found in service interface " + interfaceClass.getName());
+ logger.warn(CONFIG_NO_METHOD_FOUND, "", "", "No method found in service interface: " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<>(Arrays.asList(methods)), ","));
@@ -554,6 +561,15 @@
// export service
String host = findConfiguredHosts(protocolConfig, provider, params);
+ if (NetUtils.isIPV6URLStdFormat(host)) {
+ if (!host.contains("[")) {
+ host = "[" + host + "]";
+ }
+ } else if (NetUtils.getLocalHostV6() != null) {
+ String ipv6Host = NetUtils.getLocalHostV6();
+ params.put(CommonConstants.IPV6_KEY, ipv6Host);
+ }
+
Integer port = findConfiguredPort(protocolConfig, provider, this.getExtensionLoader(Protocol.class), name, params);
URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params);
@@ -580,10 +596,36 @@
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
+ // export to extra protocol is used in remote export
+ String extProtocol = url.getParameter("ext.protocol", "");
+ List<String> protocols = new ArrayList<>();
+ // export original url
+ url = URLBuilder.from(url).
+ addParameter(IS_PU_SERVER_KEY, Boolean.TRUE.toString()).
+ removeParameter("ext.protocol").
+ build();
url = exportRemote(url, registryURLs);
if (!isGeneric(generic) && !getScopeModel().isInternal()) {
MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
}
+
+ if (!extProtocol.equals("")) {
+ String[] extProtocols = extProtocol.split(",", -1);
+ protocols.addAll(Arrays.asList(extProtocols));
+ }
+ // export extra protocols
+ for(String protocol : protocols) {
+ if(!protocol.equals("")){
+ URL localUrl = URLBuilder.from(url).
+ setProtocol(protocol).
+ build();
+ localUrl = exportRemote(localUrl, registryURLs);
+ if (!isGeneric(generic) && !getScopeModel().isInternal()) {
+ MetadataUtils.publishServiceDefinition(localUrl, providerModel.getServiceModel(), getApplicationModel());
+ }
+ this.urls.add(localUrl);
+ }
+ }
}
}
this.urls.add(url);
@@ -831,7 +873,7 @@
protocol = protocol.toLowerCase();
if (!RANDOM_PORT_MAP.containsKey(protocol)) {
RANDOM_PORT_MAP.put(protocol, port);
- logger.warn("Use random available port(" + port + ") for protocol " + protocol);
+ logger.warn(CONFIG_USE_RANDOM_PORT, "", "", "Use random available port(" + port + ") for protocol " + protocol);
}
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
index 2d13f58..441598e 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
@@ -31,7 +31,7 @@
import org.apache.dubbo.common.deploy.ModuleDeployer;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
@@ -73,6 +73,8 @@
import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties;
import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REFRESH_INSTANCE_ERROR;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_REGISTER_INSTANCE_ERROR;
import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_PUBLISH_DELAY;
@@ -84,7 +86,7 @@
*/
public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationModel> implements ApplicationDeployer {
- private static final Logger logger = LoggerFactory.getLogger(DefaultApplicationDeployer.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultApplicationDeployer.class);
private final ApplicationModel applicationModel;
@@ -722,7 +724,7 @@
registered = true;
ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel);
} catch (Exception e) {
- logger.error("Register instance error", e);
+ logger.error(CONFIG_REGISTER_INSTANCE_ERROR, "configuration server disconnected", "", "Register instance error.", e);
}
if (registered) {
// scheduled task for updating Metadata and ServiceInstance
@@ -738,7 +740,7 @@
}
} catch (Exception e) {
if (!applicationModel.isDestroyed()) {
- logger.error("Refresh instance and metadata error", e);
+ logger.error(CONFIG_REFRESH_INSTANCE_ERROR, "", "", "Refresh instance and metadata error.", e);
}
}
}, 0, ConfigurationUtils.get(applicationModel, METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS);
@@ -764,15 +766,14 @@
}
onStopping();
+ unregisterServiceInstance();
destroyRegistries();
- destroyServiceDiscoveries();
destroyMetadataReports();
unRegisterShutdownHook();
if (asyncMetadataFuture != null) {
asyncMetadataFuture.cancel(true);
}
- unregisterServiceInstance();
}
}
@@ -957,7 +958,7 @@
ServiceInstanceMetadataUtils.refreshMetadataAndInstance(applicationModel);
}
} catch (Exception e) {
- logger.error("refresh meta and instance failed: " + e.getMessage(), e);
+ logger.error(CONFIG_REFRESH_INSTANCE_ERROR, "", "", "Refresh instance and metadata error.", e);
}
} finally {
// complete future
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
index 5b73adb..f692dcd 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
@@ -22,7 +22,7 @@
import org.apache.dubbo.common.deploy.DeployState;
import org.apache.dubbo.common.deploy.ModuleDeployListener;
import org.apache.dubbo.common.deploy.ModuleDeployer;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
@@ -46,12 +46,17 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_START_MODEL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_UNABLE_DESTROY_MODEL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_REFERENCE_MODEL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_EXPORT_SERVICE;
+
/**
* Export/refer services of module
*/
public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
- private static final Logger logger = LoggerFactory.getLogger(DefaultModuleDeployer.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultModuleDeployer.class);
private final List<CompletableFuture<?>> asyncExportingFutures = new ArrayList<>();
@@ -223,7 +228,7 @@
consumerModel.getDestroyRunner().run();
}
} catch (Throwable t) {
- logger.error("Unable to destroy consumerModel.", t);
+ logger.error(CONFIG_UNABLE_DESTROY_MODEL, "there are problems with the custom implementation.", "", "Unable to destroy model: consumerModel.", t);
}
}
@@ -234,7 +239,7 @@
providerModel.getDestroyRunner().run();
}
} catch (Throwable t) {
- logger.error("Unable to destroy providerModel.", t);
+ logger.error(CONFIG_UNABLE_DESTROY_MODEL, "there are problems with the custom implementation.", "", "Unable to destroy model: providerModel.", t);
}
}
serviceRepository.destroy();
@@ -265,7 +270,7 @@
private void onModuleFailed(String msg, Throwable ex) {
try {
setFailed(ex);
- logger.error(msg, ex);
+ logger.error(CONFIG_FAILED_START_MODEL, "", "", "Model start failed: " + msg, ex);
applicationDeployer.notifyModuleChanged(moduleModel, DeployState.STARTED);
} finally {
completeStartFuture(false);
@@ -333,7 +338,7 @@
exportedServices.add(sc);
}
} catch (Throwable t) {
- logger.error(getIdentifier() + " export async catch error : " + t.getMessage(), t);
+ logger.error(CONFIG_FAILED_EXPORT_SERVICE, "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t);
}
}, executor);
@@ -380,7 +385,7 @@
try {
referenceCache.get(rc);
} catch (Throwable t) {
- logger.error(getIdentifier() + " refer async catch error : " + t.getMessage(), t);
+ logger.error(CONFIG_FAILED_EXPORT_SERVICE, "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t);
}
}, executor);
@@ -390,7 +395,7 @@
}
}
} catch (Throwable t) {
- logger.error(getIdentifier() + " refer catch error.");
+ logger.error(CONFIG_FAILED_REFERENCE_MODEL, "", "", "Model reference failed: " + getIdentifier() + " , catch error : " + t.getMessage(), t);
referenceCache.destroy(rc);
throw t;
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index 778b7c3..d663e8a 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.config.metadata;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
@@ -28,6 +28,7 @@
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProtocolServer;
import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -45,6 +46,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_FIND_PROTOCOL;
import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
/**
@@ -52,7 +54,7 @@
*/
public class ConfigurableMetadataServiceExporter {
- private final Logger logger = LoggerFactory.getLogger(getClass());
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
private MetadataServiceDelegation metadataService;
@@ -138,7 +140,7 @@
}
}
} catch (Exception e) {
- logger.error("Failed to find any valid " + specifiedProtocol + " protocol, will use random port to export metadata service.");
+ logger.error(CONFIG_FAILED_FIND_PROTOCOL, "invalid specified " + specifiedProtocol + " protocol", "", "Failed to find any valid protocol, will use random port to export metadata service.", e);
}
} else {
protocolConfig.setPort(port);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java
index 1a0bafc..bff2e29 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ExporterDeployListener.java
@@ -19,6 +19,7 @@
import org.apache.dubbo.common.deploy.ApplicationDeployListener;
import org.apache.dubbo.common.lang.Prioritized;
import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
import org.apache.dubbo.rpc.model.ApplicationModel;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index 5e81a63..17c7363 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.config.PropertiesConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.serialize.Serialization;
import org.apache.dubbo.common.status.StatusChecker;
@@ -99,6 +99,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_PARAMETER_FORMAT_ERROR;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_ALL;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_INSTANCE;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_REGISTER_MODE_INTERFACE;
@@ -143,7 +144,7 @@
import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
public class ConfigValidationUtils {
- private static final Logger logger = LoggerFactory.getLogger(ConfigValidationUtils.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ConfigValidationUtils.class);
/**
* The maximum length of a <b>parameter's value</b>
*/
@@ -726,12 +727,14 @@
return;
}
if (value.length() > maxlength) {
- logger.error("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength);
+ logger.error(CONFIG_PARAMETER_FORMAT_ERROR, "the value content is too long", "", "Parameter value format error. Invalid " +
+ property + "=\"" + value + "\" is longer than " + maxlength);
}
if (pattern != null) {
Matcher matcher = pattern.matcher(value);
if (!matcher.matches()) {
- logger.error("Invalid " + property + "=\"" + value + "\" contains illegal " +
+ logger.error(CONFIG_PARAMETER_FORMAT_ERROR, "the value content is illegal character", "", "Parameter value format error. Invalid " +
+ property + "=\"" + value + "\" contains illegal " +
"character, only digit, letter, '-', '_' or '.' is legal.");
}
}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
index 6fc328f..c6eb027 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java
@@ -654,6 +654,24 @@
Assertions.assertNotEquals(protocol1, protocol2);
}
+ @Test
+ void testRegistryConfigEquals() {
+ RegistryConfig hangzhou = new RegistryConfig();
+ hangzhou.setAddress("nacos://localhost:8848");
+ HashMap<String, String> parameters = new HashMap<>();
+ parameters.put("namespace", "hangzhou");
+ hangzhou.setParameters(parameters);
+
+ RegistryConfig shanghai = new RegistryConfig();
+ shanghai.setAddress("nacos://localhost:8848");
+ parameters = new HashMap<>();
+ parameters.put("namespace", "shanghai");
+
+ shanghai.setParameters(parameters);
+
+ Assertions.assertNotEquals(hangzhou, shanghai);
+ }
+
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface ConfigField {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java
index 1c2b154..f27fcd7 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java
@@ -25,8 +25,8 @@
import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.integration.IntegrationTest;
-import org.apache.dubbo.config.metadata.MetadataServiceDelegation;
import org.apache.dubbo.registry.RegistryServiceListener;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
import org.apache.dubbo.test.check.registrycenter.config.ZookeeperConfig;
import org.apache.dubbo.test.check.registrycenter.config.ZookeeperRegistryCenterConfig;
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java
index be60e4e..94b7200 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java
@@ -16,8 +16,8 @@
*/
package org.apache.dubbo.config.integration.multiple.servicediscoveryregistry;
-import org.apache.dubbo.config.metadata.MetadataServiceDelegation;
import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
/**
* The instance to wrap {@link org.apache.dubbo.registry.client.ServiceDiscoveryRegistry}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
index 3b5ec69..c4f1e86 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
@@ -27,13 +27,13 @@
import org.apache.dubbo.config.ServiceListener;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.integration.IntegrationTest;
-import org.apache.dubbo.config.metadata.MetadataServiceDelegation;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.registry.ListenerRegistryWrapper;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
import org.apache.dubbo.registry.client.migration.MigrationInvoker;
import org.apache.dubbo.registry.support.RegistryManager;
import org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery;
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
index 6e60131..dbaf02f 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
@@ -27,7 +27,7 @@
//import org.apache.dubbo.config.bootstrap.DubboBootstrap;
//import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter;
//import org.apache.dubbo.config.metadata.ExporterDeployListener;
-//import org.apache.dubbo.config.metadata.MetadataServiceDelegation;
+//import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
//import org.apache.dubbo.config.provider.impl.DemoServiceImpl;
//import org.apache.dubbo.rpc.model.ApplicationModel;
//import org.apache.dubbo.rpc.model.FrameworkModel;
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index 7df9344..93aad74 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -111,6 +111,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<scope>test</scope>
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ConfigCenterBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ConfigCenterBean.java
index 49640ee..3126a88 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ConfigCenterBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ConfigCenterBean.java
@@ -33,7 +33,10 @@
import java.util.Map;
/**
- * Start from 2.7.0+, export and refer will only be executed when Spring is fully initialized, and each Config bean will get refreshed on the start of the export and refer process.
+ * Starting from 2.7.0+, export and refer will only be executed when Spring is fully initialized.
+ * <p>
+ * Each Config bean will get refreshed on the start of the exporting and referring process.
+ * <p>
* So it's ok for this bean not to be the first Dubbo Config bean being initialized.
* <p>
*/
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index fd7e111..37fe2a9 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -16,12 +16,13 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.config.spring.Constants;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent;
@@ -30,9 +31,6 @@
import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
import org.apache.dubbo.config.spring.util.SpringCompatUtils;
import org.apache.dubbo.rpc.service.GenericService;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
@@ -509,7 +507,7 @@
/**
* Gets all beans of {@link ReferenceBean}
- * @deprecated use {@link ConfigManager#getReferences()} instead
+ * @deprecated use {@link ReferenceBeanManager.getReferences()} instead
*/
@Deprecated
public Collection<ReferenceBean<?>> getReferenceBeans() {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
index c96236f..05851fc 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/config/DubboConfigDefaultPropertyValueBeanPostProcessor.java
@@ -52,6 +52,7 @@
*/
public static final String BEAN_NAME = "dubboConfigDefaultPropertyValueBeanPostProcessor";
+ @Override
protected void processBeforeInitialization(AbstractConfig dubboConfigBean, String beanName) throws BeansException {
// ignore auto generate bean name
if (!beanName.contains("#")) {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
index 60188a4..7aa7dd2 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
@@ -16,13 +16,14 @@
*/
package org.apache.dubbo.config.spring.context;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_STOP_DUBBO_ERROR;
import static org.springframework.util.ObjectUtils.nullSafeEquals;
import java.util.concurrent.Future;
import org.apache.dubbo.common.deploy.DeployListenerAdapter;
import org.apache.dubbo.common.deploy.DeployState;
import org.apache.dubbo.common.deploy.ModuleDeployer;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent;
@@ -44,7 +45,7 @@
*/
public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
- private static final Logger logger = LoggerFactory.getLogger(DubboDeployApplicationListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DubboDeployApplicationListener.class);
private ApplicationContext applicationContext;
@@ -130,7 +131,7 @@
moduleModel.destroy();
}
} catch (Exception e) {
- logger.error("An error occurred when stop dubbo module: " + e.getMessage(), e);
+ logger.error(CONFIG_STOP_DUBBO_ERROR, "", "", "Unexpected error occurred when stop dubbo module: " + e.getMessage(), e);
}
// remove context bind cache
DubboSpringInitializer.remove(event.getApplicationContext());
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
index 2f70012..d9f4f2b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
@@ -89,6 +89,11 @@
// register ConfigManager singleton
beanFactory.registerSingleton(ConfigManager.BEAN_NAME, applicationModel.getApplicationConfigManager());
+
+ // fix https://github.com/apache/dubbo/issues/10278
+ if (registry != null){
+ registry.removeBeanDefinition(BEAN_NAME);
+ }
}
@Override
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java
index 7a2d0be..efc13f9 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java
@@ -52,6 +52,8 @@
String PROVIDED_BY = "providedBy";
+ String PROVIDER_PORT = "providerPort";
+
String URL = "url";
String CLIENT = "client";
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java
index 775d879..36c7418 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java
@@ -198,6 +198,11 @@
return this;
}
+ public ReferenceBeanBuilder setProviderPort(Integer providerPort) {
+ attributes.put(ReferenceAttributes.PROVIDER_PORT, providerPort);
+ return this;
+ }
+
// public ReferenceBeanBuilder setRouter(String router) {
// attributes.put(ReferenceAttributes.ROUTER, router);
// return this;
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceCreator.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceCreator.java
index bbab458..68ebdba 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceCreator.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceCreator.java
@@ -95,7 +95,9 @@
configureBean(configBean);
if (logger.isInfoEnabled()) {
- logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
+ logger.info("The configBean[type:" +
+ configBean.getClass().getSimpleName() + "<" + defaultInterfaceClass.getTypeName() + ">" +
+ "] has been built.");
}
return configBean;
@@ -122,14 +124,6 @@
}
}
-// private void configureApplicationConfig(ReferenceConfig configBean) {
-// String applicationConfigId = getAttribute(attributes, "application");
-// if (StringUtils.hasText(applicationConfigId)) {
-// ApplicationConfig applicationConfig = getConfig(applicationConfigId, ApplicationConfig.class);
-// configBean.setApplication(applicationConfig);
-// }
-// }
-
private void configureModuleConfig(ReferenceConfig configBean) {
String moduleConfigId = getAttribute(attributes, "module");
if (StringUtils.hasText(moduleConfigId)) {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
index d83398b..ebb8075 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
@@ -51,20 +51,6 @@
@Override
public Status check() {
- // TODO It seems to be ok with GenericWebApplicationContext, need further confirmation
-// ApplicationContext context = null;
-// for (ApplicationContext c : SpringExtensionInjector.getContexts()) {
-// // [Issue] SpringStatusChecker execute errors on non-XML Spring configuration
-// // issue : https://github.com/apache/dubbo/issues/3615
-// if(c instanceof GenericWebApplicationContext) { // ignore GenericXmlApplicationContext
-// continue;
-// }
-//
-// if (c != null) {
-// context = c;
-// break;
-// }
-// }
if (applicationContext == null && applicationModel != null) {
SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(applicationModel);
@@ -110,10 +96,12 @@
}
}
}
- } catch (UnsupportedOperationException t) {
- logger.debug(t.getMessage(), t);
} catch (Throwable t) {
- logger.warn(t.getMessage(), t);
+ if (t.getCause() instanceof UnsupportedOperationException){
+ logger.debug(t.getMessage(), t);
+ }else {
+ logger.warn(t.getMessage(), t);
+ }
}
return new Status(level, buf.toString());
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
index b22bd91..99b34ee 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
@@ -239,6 +239,12 @@
<![CDATA[ declares which app or service this interface belongs to. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="provider-port" type="xsd:integer">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ declares provider service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="router" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1017,6 +1023,11 @@
<xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="mesh-enable" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Enable mesh mode. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:complexContent>
@@ -1059,6 +1070,14 @@
<xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="unloadClusterRelated" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ In the mesh mode, uninstall the directory, router and load balance related to the cluster in the currently invoked invoker.
+ Delegate retry, load balancing, timeout and other traffic management capabilities to Sidecar. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:complexContent>
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
index 9a9a6a7..10bc5c6 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
@@ -239,6 +239,12 @@
<![CDATA[ declares which app or service this interface belongs to. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="provider-port" type="xsd:integer">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ declares provider service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="router" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1180,6 +1186,11 @@
<![CDATA[ Whether refer should run in background or not, default false. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="mesh-enable" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Enable mesh mode. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:complexContent>
@@ -1230,6 +1241,14 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="unloadClusterRelated" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ In the mesh mode, uninstall the directory, router and load balance related to the cluster in the currently invoked invoker.
+ Delegate retry, load balancing, timeout and other traffic management capabilities to Sidecar. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:complexContent>
@@ -1259,6 +1278,11 @@
<xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="ext-protocol" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ extra protocol.]]]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="threadpool" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/AbstractRegistryService.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/AbstractRegistryService.java
index c664601..9ac517c 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/AbstractRegistryService.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/AbstractRegistryService.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.config.spring;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
@@ -32,13 +32,15 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_NOTIFY_EVENT;
+
/**
* AbstractRegistryService
*/
public abstract class AbstractRegistryService implements RegistryService {
// logger
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
// registered services
// Map<serviceName, Map<url, queryString>>
@@ -184,7 +186,7 @@
try {
notify(service, urls, listener);
} catch (Throwable t) {
- logger.error("Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
+ logger.error(CONFIG_FAILED_NOTIFY_EVENT, "", "", "Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
}
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java
index bb29825..009f371 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/EmbeddedZooKeeper.java
@@ -15,11 +15,11 @@
*/
package org.apache.dubbo.config.spring;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.zookeeper.server.ServerConfig;
import org.apache.zookeeper.server.ZooKeeperServerMain;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.util.ErrorHandler;
import org.springframework.util.SocketUtils;
@@ -29,6 +29,8 @@
import java.util.Properties;
import java.util.UUID;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_ZOOKEEPER_SERVER_ERROR;
+
/**
* from: https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java
* <p>
@@ -43,7 +45,7 @@
/**
* Logger.
*/
- private static final Logger logger = LoggerFactory.getLogger(EmbeddedZooKeeper.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EmbeddedZooKeeper.class);
/**
* ZooKeeper client port. This will be determined dynamically upon startup.
@@ -238,7 +240,7 @@
if (errorHandler != null) {
errorHandler.handleError(e);
} else {
- logger.error("Exception running embedded ZooKeeper", e);
+ logger.error(CONFIG_ZOOKEEPER_SERVER_ERROR, "ZooKeeper server error", "", "Exception running embedded ZooKeeper.", e);
}
}
}
diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
index f0c77b5..b9c6d57 100644
--- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
@@ -21,7 +21,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
@@ -47,6 +47,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_NAMESPACE_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY;
/**
* Apollo implementation, https://github.com/ctripcorp/apollo
@@ -61,7 +62,7 @@
* Please see http://dubbo.apache.org/zh-cn/docs/user/configuration/config-center.html for details.
*/
public class ApolloDynamicConfiguration implements DynamicConfiguration {
- private static final Logger logger = LoggerFactory.getLogger(ApolloDynamicConfiguration.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ApolloDynamicConfiguration.class);
private static final String APOLLO_ENV_KEY = "env";
private static final String APOLLO_ADDR_KEY = "apollo.meta";
private static final String APOLLO_CLUSTER_KEY = "apollo.cluster";
@@ -106,7 +107,10 @@
throw new IllegalStateException("Failed to connect to config center, the config center is Apollo, " +
"the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv));
} else {
- logger.warn("Failed to connect to config center, the config center is Apollo, " +
+ // 5-1 Failed to connect to configuration center.
+
+ logger.warn(CONFIG_FAILED_CONNECT_REGISTRY, "configuration server offline", "",
+ "Failed to connect to config center, the config center is Apollo, " +
"the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv) +
", will use the local cache value instead before eventually the connection is established.");
}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
index 17daabb..cd20375 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
@@ -35,6 +35,8 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY;
+
public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration {
private Executor executor;
@@ -60,7 +62,16 @@
zkClient = zookeeperTransporter.connect(url);
boolean isConnected = zkClient.isConnected();
if (!isConnected) {
- throw new IllegalStateException("Failed to connect with zookeeper, pls check if url " + url + " is correct.");
+
+ IllegalStateException illegalStateException =
+ new IllegalStateException("Failed to connect with zookeeper, pls check if url " + url + " is correct.");
+
+ if (logger != null) {
+ logger.error(CONFIG_FAILED_CONNECT_REGISTRY, "configuration server offline", "",
+ "Failed to connect with zookeeper", illegalStateException);
+ }
+
+ throw illegalStateException;
}
}
diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
index e286141..c7f6fe3 100644
--- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
@@ -87,6 +87,10 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
index 6319d64..526f501 100644
--- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
@@ -89,6 +89,10 @@
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml
index 997510b..32de339 100644
--- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-consumer/pom.xml
@@ -87,6 +87,10 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml
index 67ae69b..e4b2d26 100644
--- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/pom.xml
@@ -89,6 +89,10 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
diff --git a/dubbo-demo/dubbo-demo-generic-call/pom.xml b/dubbo-demo/dubbo-demo-generic-call/pom.xml
index 1cb5222..1fd332f 100644
--- a/dubbo-demo/dubbo-demo-generic-call/pom.xml
+++ b/dubbo-demo/dubbo-demo-generic-call/pom.xml
@@ -82,6 +82,10 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
index f70c8dd..d23b171 100644
--- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
@@ -67,6 +67,10 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
index 07fa629..5319a35 100644
--- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
@@ -64,6 +64,10 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-filter-cache</artifactId>
</dependency>
<dependency>
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml
index 570ab67..9eb6068 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-consumer/pom.xml
@@ -87,6 +87,11 @@
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
index c106da6..c31ff38 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
@@ -87,6 +87,11 @@
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
diff --git a/dubbo-demo/dubbo-demo-triple/pom.xml b/dubbo-demo/dubbo-demo-triple/pom.xml
index 9a0139b..6597e0c 100644
--- a/dubbo-demo/dubbo-demo-triple/pom.xml
+++ b/dubbo-demo/dubbo-demo-triple/pom.xml
@@ -112,6 +112,10 @@
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
index 663c76f..6aa8986 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
@@ -85,6 +85,10 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
</dependency>
<dependency>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
index e252cf8..4237066 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
@@ -92,6 +92,10 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
</dependency>
<dependency>
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index c8ec8c9..4c34e8b 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -99,6 +99,7 @@
<httpclient_version>4.5.13</httpclient_version>
<httpcore_version>4.4.6</httpcore_version>
<fastjson_version>1.2.83</fastjson_version>
+ <fastjson2_version>2.0.14</fastjson2_version>
<zookeeper_version>3.4.14</zookeeper_version>
<curator_version>4.2.0</curator_version>
<curator_test_version>2.12.0</curator_test_version>
@@ -125,17 +126,22 @@
<fst_version>2.48-jdk-6</fst_version>
<avro_version>1.8.2</avro_version>
<apollo_client_version>1.8.0</apollo_client_version>
- <snakeyaml_version>1.29</snakeyaml_version>
+ <snakeyaml_version>1.32</snakeyaml_version>
<commons_lang3_version>3.8.1</commons_lang3_version>
<protostuff_version>1.5.9</protostuff_version>
<envoy_api_version>0.1.23</envoy_api_version>
+ <micrometer.version>1.7.4</micrometer.version>
+ <t_digest.version>3.3</t_digest.version>
+ <prometheus_client.version>0.10.0</prometheus_client.version>
+ <reactive.version>1.0.4</reactive.version>
+ <reactor.version>3.4.19</reactor.version>
<rs_api_version>2.0</rs_api_version>
<resteasy_version>3.0.19.Final</resteasy_version>
<tomcat_embed_version>8.5.69</tomcat_embed_version>
<jetcd_version>0.5.3</jetcd_version>
<nacos_version>2.1.0</nacos_version>
- <grpc.version>1.44.0</grpc.version>
+ <grpc.version>1.47.0</grpc.version>
<grpc_contrib_verdion>0.8.1</grpc_contrib_verdion>
<jprotoc_version>1.2.1</jprotoc_version>
<!-- Log libs -->
@@ -153,7 +159,7 @@
<eureka.version>1.9.12</eureka.version>
<!-- Fabric8 for Kubernetes -->
- <fabric8_kubernetes_version>5.3.2</fabric8_kubernetes_version>
+ <fabric8_kubernetes_version>6.1.1</fabric8_kubernetes_version>
<!-- Alibaba -->
<alibaba_spring_context_support_version>1.0.8</alibaba_spring_context_support_version>
@@ -175,7 +181,7 @@
<portlet_version>2.0</portlet_version>
<maven_flatten_version>1.1.0</maven_flatten_version>
<commons_compress_version>1.21</commons_compress_version>
- <revision>3.0.13-SNAPSHOT</revision>
+ <revision>3.1.2-SNAPSHOT</revision>
</properties>
<dependencyManagement>
@@ -229,6 +235,11 @@
<version>${fastjson_version}</version>
</dependency>
<dependency>
+ <groupId>com.alibaba.fastjson2</groupId>
+ <artifactId>fastjson2</artifactId>
+ <version>${fastjson2_version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper_version}</version>
@@ -767,6 +778,43 @@
<version>${snappy_java_version}</version>
<optional>true</optional>
</dependency>
+ <!-- metrics related dependencies-->
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ <version>${micrometer.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-registry-prometheus</artifactId>
+ <version>${micrometer.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.tdunning</groupId>
+ <artifactId>t-digest</artifactId>
+ <version>${t_digest.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient</artifactId>
+ <version>${prometheus_client.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient_pushgateway</artifactId>
+ <version>${prometheus_client.version}</version>
+ </dependency>
+ <!-- reactive related dependencies -->
+ <dependency>
+ <groupId>org.reactivestreams</groupId>
+ <artifactId>reactive-streams</artifactId>
+ <version>${reactive.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-core</artifactId>
+ <version>${reactor.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
index 80ebf73..7f9410f 100644
--- a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
+++ b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
@@ -32,7 +32,7 @@
<packaging>pom</packaging>
<properties>
- <revision>3.0.13-SNAPSHOT</revision>
+ <revision>3.1.2-SNAPSHOT</revision>
<maven_flatten_version>1.1.0</maven_flatten_version>
<curator_version>5.1.0</curator_version>
<zookeeper_version>3.7.0</zookeeper_version>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
index fed04af..3f883d6 100644
--- a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
+++ b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
@@ -32,7 +32,7 @@
<packaging>pom</packaging>
<properties>
- <revision>3.0.13-SNAPSHOT</revision>
+ <revision>3.1.2-SNAPSHOT</revision>
<maven_flatten_version>1.1.0</maven_flatten_version>
<curator_version>4.2.0</curator_version>
<zookeeper_version>3.4.14</zookeeper_version>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index bd5f9ea..09d4ef8 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -215,6 +215,13 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
@@ -304,6 +311,27 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-kubernetes</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-xds</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-reactive</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
<!-- Transitive dependencies -->
@@ -335,6 +363,10 @@
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.alibaba.fastjson2</groupId>
+ <artifactId>fastjson2</artifactId>
+ </dependency>
<!-- Temporarily add this part to exclude transitive dependency -->
<dependency>
@@ -407,6 +439,7 @@
<include>org.apache.dubbo:dubbo-native</include>
<include>org.apache.dubbo:dubbo-plugin</include>
<include>org.apache.dubbo:dubbo-qos</include>
+ <include>org.apache.dubbo:dubbo-reactive</include>
<include>org.apache.dubbo:dubbo-registry-api</include>
<include>org.apache.dubbo:dubbo-registry-multicast</include>
<include>org.apache.dubbo:dubbo-registry-multiple</include>
@@ -429,9 +462,12 @@
<include>org.apache.dubbo:dubbo-rpc</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
<include>org.apache.dubbo:dubbo-serialization-hessian2</include>
+ <include>org.apache.dubbo:dubbo-serialization-fastjson2</include>
<include>org.apache.dubbo:dubbo-serialization-jdk</include>
<include>org.apache.dubbo:dubbo-serialization</include>
<include>org.apache.dubbo:dubbo-compiler</include>
+ <include>org.apache.dubbo:dubbo-kubernetes</include>
+ <include>org.apache.dubbo:dubbo-xds</include>
</includes>
</artifactSet>
<transformers>
diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml
index a477c4dc..013d842 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -173,6 +173,11 @@
<artifactId>dubbo-container-spring</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-reactive</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- dubbo plugin -->
<dependency>
@@ -198,6 +203,11 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
<version>${project.version}</version>
</dependency>
@@ -260,6 +270,16 @@
<artifactId>dubbo-configcenter-nacos</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-kubernetes</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-xds</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
diff --git a/dubbo-kubernetes/pom.xml b/dubbo-kubernetes/pom.xml
new file mode 100644
index 0000000..f5250b3
--- /dev/null
+++ b/dubbo-kubernetes/pom.xml
@@ -0,0 +1,81 @@
+<?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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-kubernetes</artifactId>
+ <name>${project.artifactId}</name>
+ <description>The Kubernetes Integration</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metadata-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-server-mock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>3.8.0</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+
+</project>
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
new file mode 100644
index 0000000..300dae3
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
@@ -0,0 +1,205 @@
+/*
+ * 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.registry.kubernetes;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+
+import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class KubernetesMeshEnvListener implements MeshEnvListener {
+ public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListener.class);
+ private volatile static boolean usingApiServer = false;
+ private volatile static KubernetesClient kubernetesClient;
+ private volatile static String namespace;
+
+ private final Map<String, MeshAppRuleListener> appRuleListenerMap = new ConcurrentHashMap<>();
+
+ private final Map<String, Watch> vsAppWatch = new ConcurrentHashMap<>();
+ private final Map<String, Watch> drAppWatch = new ConcurrentHashMap<>();
+
+ private final Map<String, String> vsAppCache = new ConcurrentHashMap<>();
+ private final Map<String, String> drAppCache = new ConcurrentHashMap<>();
+
+ public static void injectKubernetesEnv(KubernetesClient client, String configuredNamespace) {
+ usingApiServer = true;
+ kubernetesClient = client;
+ namespace = configuredNamespace;
+ }
+
+ @Override
+ public boolean isEnable() {
+ return usingApiServer;
+ }
+
+ @Override
+ public void onSubscribe(String appName, MeshAppRuleListener listener) {
+ appRuleListenerMap.put(appName, listener);
+ logger.info("Subscribe Mesh Rule in Kubernetes. AppName: " + appName);
+
+ // subscribe VisualService
+ subscribeVs(appName);
+
+ // subscribe DestinationRule
+ subscribeDr(appName);
+
+ // notify for start
+ notifyOnce(appName);
+ }
+
+ private void subscribeVs(String appName) {
+ if (vsAppWatch.containsKey(appName)) {
+ return;
+ }
+
+ try {
+ Watch watch = kubernetesClient
+ .genericKubernetesResources(
+ MeshConstant.getVsDefinition())
+ .inNamespace(namespace)
+ .withName(appName)
+ .watch(new Watcher<GenericKubernetesResource>() {
+ @Override
+ public void eventReceived(Action action, GenericKubernetesResource resource) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+ }
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ String vsRule = new Yaml(new SafeConstructor()).dump(resource);
+ vsAppCache.put(appName, vsRule);
+ if (drAppCache.containsKey(appName)) {
+ notifyListener(vsRule, appName, drAppCache.get(appName));
+ }
+ } else {
+ appRuleListenerMap.get(appName).receiveConfigInfo("");
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+ vsAppWatch.put(appName, watch);
+ try {
+ GenericKubernetesResource vsRule = kubernetesClient
+ .genericKubernetesResources(
+ MeshConstant.getVsDefinition())
+ .inNamespace(namespace)
+ .withName(appName)
+ .get();
+ vsAppCache.put(appName, new Yaml(new SafeConstructor()).dump(vsRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when listen kubernetes crd.", e);
+ }
+ }
+
+ private void notifyListener(String vsRule, String appName, String drRule) {
+ String rule = vsRule + "\n---\n" + drRule;
+ logger.info("Notify App Rule Listener. AppName: " + appName + " Rule:" + rule);
+
+ appRuleListenerMap.get(appName).receiveConfigInfo(rule);
+ }
+
+ private void subscribeDr(String appName) {
+ if (drAppWatch.containsKey(appName)) {
+ return;
+ }
+
+ try {
+ Watch watch = kubernetesClient
+ .genericKubernetesResources(
+ MeshConstant.getDrDefinition())
+ .inNamespace(namespace)
+ .withName(appName)
+ .watch(new Watcher<GenericKubernetesResource>() {
+ @Override
+ public void eventReceived(Action action, GenericKubernetesResource resource) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+ }
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ String drRule = new Yaml(new SafeConstructor()).dump(resource);
+
+ drAppCache.put(appName, drRule);
+ if (vsAppCache.containsKey(appName)) {
+ notifyListener(vsAppCache.get(appName), appName, drRule);
+ }
+ } else {
+ appRuleListenerMap.get(appName).receiveConfigInfo("");
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+ drAppWatch.put(appName, watch);
+ try {
+ GenericKubernetesResource drRule = kubernetesClient
+ .genericKubernetesResources(
+ MeshConstant.getDrDefinition())
+ .inNamespace(namespace)
+ .withName(appName)
+ .get();
+ drAppCache.put(appName, new Yaml(new SafeConstructor()).dump(drRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when listen kubernetes crd.", e);
+ }
+ }
+
+ private void notifyOnce(String appName) {
+ if (vsAppCache.containsKey(appName) && drAppCache.containsKey(appName)) {
+ notifyListener(vsAppCache.get(appName), appName, drAppCache.get(appName));
+ }
+ }
+
+ @Override
+ public void onUnSubscribe(String appName) {
+ appRuleListenerMap.remove(appName);
+
+ if (vsAppWatch.containsKey(appName)) {
+ vsAppWatch.remove(appName).close();
+ }
+ vsAppCache.remove(appName);
+
+ if (drAppWatch.containsKey(appName)) {
+ drAppWatch.remove(appName).close();
+ }
+ drAppCache.remove(appName);
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
new file mode 100644
index 0000000..9d1c6d0
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListenerFactory.java
@@ -0,0 +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.registry.kubernetes;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class KubernetesMeshEnvListenerFactory implements MeshEnvListenerFactory {
+ public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListenerFactory.class);
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+ private MeshEnvListener listener = null;
+
+ @Override
+ public MeshEnvListener getListener() {
+ try {
+ if (initialized.compareAndSet(false, true)) {
+ listener = new NopKubernetesMeshEnvListener();
+ }
+ } catch (Throwable t) {
+ logger.info("Current Env not support Kubernetes.");
+ }
+ return listener;
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
new file mode 100644
index 0000000..b221c89
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+/**
+ * Empty implements for Kubernetes <br/>
+ * Kubernetes only support `Service Discovery` mode register <br/>
+ * Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
+ * {@link KubernetesServiceDiscovery} is the real implementation of Kubernetes
+ */
+public class KubernetesRegistry extends FailbackRegistry {
+ public KubernetesRegistry(URL url) {
+ super(url);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void doRegister(URL url) {
+
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+
+ }
+
+ @Override
+ public void doSubscribe(URL url, NotifyListener listener) {
+
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
new file mode 100644
index 0000000..fe0e047
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
@@ -0,0 +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.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+
+public class KubernetesRegistryFactory extends AbstractRegistryFactory {
+
+ @Override
+ protected String createRegistryCacheKey(URL url) {
+ return url.toFullString();
+ }
+
+ @Override
+ protected Registry createRegistry(URL url) {
+ return new KubernetesRegistry(url);
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
new file mode 100644
index 0000000..087de56
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -0,0 +1,444 @@
+/*
+ * 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.registry.kubernetes;
+
+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.registry.client.AbstractServiceDiscovery;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesConfigUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+
+import com.alibaba.fastjson.JSONObject;
+import io.fabric8.kubernetes.api.model.EndpointAddress;
+import io.fabric8.kubernetes.api.model.EndpointPort;
+import io.fabric8.kubernetes.api.model.EndpointSubset;
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
+import io.fabric8.kubernetes.client.informers.SharedIndexInformer;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+public class KubernetesServiceDiscovery extends AbstractServiceDiscovery {
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private KubernetesClient kubernetesClient;
+
+ private String currentHostname;
+
+ private final URL registryURL;
+
+ private final String namespace;
+
+ private final boolean enableRegister;
+
+ public final static String KUBERNETES_PROPERTIES_KEY = "io.dubbo/metadata";
+
+ private final static ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, SharedIndexInformer<Service>> SERVICE_INFORMER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, SharedIndexInformer<Pod>> PODS_INFORMER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, SharedIndexInformer<Endpoints>> ENDPOINTS_INFORMER = new ConcurrentHashMap<>(64);
+
+ public KubernetesServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+ super(applicationModel, registryURL);
+ Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
+ this.kubernetesClient = new KubernetesClientBuilder().withConfig(config).build();
+ this.currentHostname = System.getenv("HOSTNAME");
+ this.registryURL = registryURL;
+ this.namespace = config.getNamespace();
+ this.enableRegister = registryURL.getParameter(KubernetesClientConst.ENABLE_REGISTER, true);
+
+ boolean availableAccess;
+ try {
+ availableAccess = kubernetesClient.pods().withName(currentHostname).get() != null;
+ } catch (Throwable e) {
+ availableAccess = false;
+ }
+ if (!availableAccess) {
+ String message = "Unable to access api server. " +
+ "Please check your url config." +
+ " Master URL: " + config.getMasterUrl() +
+ " Hostname: " + currentHostname;
+ logger.error(message);
+ } else {
+ KubernetesMeshEnvListener.injectKubernetesEnv(kubernetesClient, namespace);
+ }
+ }
+
+ @Override
+ public void doDestroy() {
+ SERVICE_INFORMER.forEach((k, v) -> v.close());
+ SERVICE_INFORMER.clear();
+
+ PODS_INFORMER.forEach((k, v) -> v.close());
+ PODS_INFORMER.clear();
+
+ ENDPOINTS_INFORMER.forEach((k, v) -> v.close());
+ ENDPOINTS_INFORMER.clear();
+
+ kubernetesClient.close();
+ }
+
+ @Override
+ public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit(pod ->
+ new PodBuilder(pod)
+ .editOrNewMetadata()
+ .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JSONObject.toJSONString(serviceInstance.getMetadata()))
+ .endMetadata()
+ .build());
+ if (logger.isInfoEnabled()) {
+ logger.info("Write Current Service Instance Metadata to Kubernetes pod. " +
+ "Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ /**
+ * Comparing to {@link AbstractServiceDiscovery#doUpdate(ServiceInstance)}, unregister() is unnecessary here.
+ */
+ @Override
+ public void doUpdate(ServiceInstance serviceInstance) throws RuntimeException {
+ reportMetadata(serviceInstance.getServiceMetadata());
+ this.doRegister(serviceInstance);
+ }
+
+ @Override
+ public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit(pod ->
+ new PodBuilder(pod)
+ .editOrNewMetadata()
+ .removeFromAnnotations(KUBERNETES_PROPERTIES_KEY)
+ .endMetadata()
+ .build());
+ if (logger.isInfoEnabled()) {
+ logger.info("Remove Current Service Instance from Kubernetes pod. Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .list()
+ .getItems()
+ .stream()
+ .map(service -> service.getMetadata().getName())
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+ Endpoints endpoints = null;
+ SharedIndexInformer<Endpoints> endInformer = ENDPOINTS_INFORMER.get(serviceName);
+ if (endInformer != null) {
+ // get endpoints directly from informer local store
+ List<Endpoints> endpointsList = endInformer.getStore().list();
+ if (endpointsList.size() > 0) {
+ endpoints = endpointsList.get(0);
+ }
+ }
+ if (endpoints == null) {
+ endpoints = kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .get();
+ }
+
+ return toServiceInstance(endpoints, serviceName);
+ }
+
+ @Override
+ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+ listener.getServiceNames().forEach(serviceName -> {
+ SERVICE_UPDATE_TIME.put(serviceName, new AtomicLong(0L));
+
+ // Watch Service Endpoint Modification
+ watchEndpoints(listener, serviceName);
+
+ // Watch Pods Modification, happens when ServiceInstance updated
+ watchPods(listener, serviceName);
+
+ // Watch Service Modification, happens when Service Selector updated, used to update pods watcher
+ watchService(listener, serviceName);
+ });
+ }
+
+ private void watchEndpoints(ServiceInstancesChangedListener listener, String serviceName) {
+ SharedIndexInformer<Endpoints> endInformer = kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .inform(new ResourceEventHandler<Endpoints>() {
+ @Override
+ public void onAdd(Endpoints endpoints) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: added. Current pod name: " + currentHostname +
+ ". Endpoints is: " + endpoints);
+ }
+ notifyServiceChanged(serviceName, listener, toServiceInstance(endpoints, serviceName));
+ }
+
+ @Override
+ public void onUpdate(Endpoints oldEndpoints, Endpoints newEndpoints) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: updated. Current pod name: " + currentHostname +
+ ". The new Endpoints is: " + newEndpoints);
+ }
+ notifyServiceChanged(serviceName, listener, toServiceInstance(newEndpoints, serviceName));
+ }
+
+ @Override
+ public void onDelete(Endpoints endpoints, boolean deletedFinalStateUnknown) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: deleted. Current pod name: " + currentHostname +
+ ". Endpoints is: " + endpoints);
+ }
+ notifyServiceChanged(serviceName, listener, toServiceInstance(endpoints, serviceName));
+ }
+ });
+
+ ENDPOINTS_INFORMER.put(serviceName, endInformer);
+ }
+
+ private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return;
+ }
+
+ SharedIndexInformer<Pod> podInformer = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .inform(new ResourceEventHandler<Pod>() {
+ @Override
+ public void onAdd(Pod pod) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Event. Event type: added. Current pod name: " + currentHostname +
+ ". Pod is: " + pod);
+ }
+ }
+
+ @Override
+ public void onUpdate(Pod oldPod, Pod newPod) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Event. Event type: updated. Current pod name: " + currentHostname +
+ ". new Pod is: " + newPod);
+ }
+
+ notifyServiceChanged(serviceName, listener, getInstances(serviceName));
+ }
+
+ @Override
+ public void onDelete(Pod pod, boolean deletedFinalStateUnknown) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Event. Event type: deleted. Current pod name: " + currentHostname +
+ ". Pod is: " + pod);
+ }
+ }
+ });
+
+ PODS_INFORMER.put(serviceName, podInformer);
+ }
+
+ private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
+ SharedIndexInformer<Service> serviceInformer = kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .inform(
+ new ResourceEventHandler<Service>() {
+ @Override
+ public void onAdd(Service service) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Added Event. " +
+ "Current pod name: " + currentHostname);
+ }
+ }
+
+ @Override
+ public void onUpdate(Service oldService, Service newService) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Update Event. Update Pods Watcher. Current pod name: " + currentHostname +
+ ". The new Service is: " + newService);
+ }
+ if (PODS_INFORMER.containsKey(serviceName)) {
+ PODS_INFORMER.get(serviceName).close();
+ PODS_INFORMER.remove(serviceName);
+ }
+ watchPods(listener, serviceName);
+ }
+
+ @Override
+ public void onDelete(Service service, boolean deletedFinalStateUnknown) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Delete Event. " +
+ "Current pod name: " + currentHostname);
+ }
+ }
+ }
+ );
+
+ SERVICE_INFORMER.put(serviceName, serviceInformer);
+ }
+
+ private void notifyServiceChanged(String serviceName, ServiceInstancesChangedListener listener, List<ServiceInstance> serviceInstanceList) {
+ long receivedTime = System.nanoTime();
+
+ ServiceInstancesChangedEvent event;
+
+ event = new ServiceInstancesChangedEvent(serviceName, serviceInstanceList);
+
+ AtomicLong updateTime = SERVICE_UPDATE_TIME.get(serviceName);
+ long lastUpdateTime = updateTime.get();
+
+ if (lastUpdateTime <= receivedTime) {
+ if (updateTime.compareAndSet(lastUpdateTime, receivedTime)) {
+ listener.onEvent(event);
+ return;
+ }
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Discard Service Instance Data. " +
+ "Possible Cause: Newer message has been processed or Failed to update time record by CAS. " +
+ "Current Data received time: " + receivedTime + ". " +
+ "Newer Data received time: " + lastUpdateTime + ".");
+ }
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ private Map<String, String> getServiceSelector(String serviceName) {
+ Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
+ if (service == null) {
+ return null;
+ }
+ return service.getSpec().getSelector();
+ }
+
+ private List<ServiceInstance> toServiceInstance(Endpoints endpoints, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return new LinkedList<>();
+ }
+ Map<String, Pod> pods = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .list()
+ .getItems()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ pod -> pod.getMetadata().getName(),
+ pod -> pod));
+
+ List<ServiceInstance> instances = new LinkedList<>();
+ Set<Integer> instancePorts = new HashSet<>();
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ instancePorts.addAll(
+ endpointSubset.getPorts()
+ .stream().map(EndpointPort::getPort)
+ .collect(Collectors.toSet()));
+ }
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ for (EndpointAddress address : endpointSubset.getAddresses()) {
+ Pod pod = pods.get(address.getTargetRef().getName());
+ String ip = address.getIp();
+ if (pod == null) {
+ logger.warn("Unable to match Kubernetes Endpoint address with Pod. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ continue;
+ }
+ instancePorts.forEach(port -> {
+ ServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
+
+ String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY);
+ if (StringUtils.isNotEmpty(properties)) {
+ serviceInstance.getMetadata().putAll(JSONObject.parseObject(properties, Map.class));
+ instances.add(serviceInstance);
+ } else {
+ logger.warn("Unable to find Service Instance metadata in Pod Annotations. " +
+ "Possibly cause: provider has not been initialized successfully. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ }
+ });
+ }
+ }
+
+ return instances;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setCurrentHostname(String currentHostname) {
+ this.currentHostname = currentHostname;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setKubernetesClient(KubernetesClient kubernetesClient) {
+ this.kubernetesClient = kubernetesClient;
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
new file mode 100644
index 0000000..7d11dfa
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
@@ -0,0 +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.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+
+public class KubernetesServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+ @Override
+ protected ServiceDiscovery createDiscovery(URL registryURL) {
+ return new KubernetesServiceDiscovery(applicationModel, registryURL);
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
new file mode 100644
index 0000000..813bdd8
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/MeshConstant.java
@@ -0,0 +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.registry.kubernetes;
+
+import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
+
+public class MeshConstant {
+ public static CustomResourceDefinitionContext getVsDefinition() {
+ // TODO cache
+ return new CustomResourceDefinitionContext.Builder()
+ .withGroup("service.dubbo.apache.org")
+ .withVersion("v1alpha1")
+ .withScope("Namespaced")
+ .withName("virtualservices.service.dubbo.apache.org")
+ .withPlural("virtualservices")
+ .withKind("VirtualService").build();
+ }
+
+ public static CustomResourceDefinitionContext getDrDefinition() {
+ // TODO cache
+ return new CustomResourceDefinitionContext.Builder()
+ .withGroup("service.dubbo.apache.org")
+ .withVersion("v1alpha1")
+ .withScope("Namespaced")
+ .withName("destinationrules.service.dubbo.apache.org")
+ .withPlural("destinationrules")
+ .withKind("DestinationRule").build();
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
new file mode 100644
index 0000000..1238efb
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/NopKubernetesMeshEnvListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.registry.kubernetes;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
+
+public class NopKubernetesMeshEnvListener implements MeshEnvListener {
+
+ @Override
+ public boolean isEnable() {
+ return false;
+ }
+
+ @Override
+ public void onSubscribe(String appName, MeshAppRuleListener listener) {
+
+ }
+
+ @Override
+ public void onUnSubscribe(String appName) {
+
+ }
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
new file mode 100644
index 0000000..d4591a2
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
@@ -0,0 +1,78 @@
+/*
+ * 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.registry.kubernetes.util;
+
+public class KubernetesClientConst {
+ public static final String DEFAULT_MASTER_PLACEHOLDER = "DEFAULT_MASTER_HOST";
+ public static final String DEFAULT_MASTER_URL = "https://kubernetes.default.svc";
+
+ public final static String ENABLE_REGISTER = "enableRegister";
+
+ public final static String TRUST_CERTS = "trustCerts";
+
+ public final static String USE_HTTPS = "useHttps";
+
+ public static final String HTTP2_DISABLE = "http2Disable";
+
+ public final static String NAMESPACE = "namespace";
+
+ public final static String API_VERSION = "apiVersion";
+
+ public final static String CA_CERT_FILE = "caCertFile";
+
+ public final static String CA_CERT_DATA = "caCertData";
+
+ public final static String CLIENT_CERT_FILE = "clientCertFile";
+
+ public final static String CLIENT_CERT_DATA = "clientCertData";
+
+ public final static String CLIENT_KEY_FILE = "clientKeyFile";
+
+ public final static String CLIENT_KEY_DATA = "clientKeyData";
+
+ public final static String CLIENT_KEY_ALGO = "clientKeyAlgo";
+
+ public final static String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
+
+ public final static String OAUTH_TOKEN = "oauthToken";
+
+ public final static String USERNAME = "username";
+
+ public final static String PASSWORD = "password";
+
+ public final static String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
+
+ public final static String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
+
+ public final static String CONNECTION_TIMEOUT = "connectionTimeout";
+
+ public final static String REQUEST_TIMEOUT = "requestTimeout";
+
+ public final static String ROLLING_TIMEOUT = "rollingTimeout";
+
+ public final static String LOGGING_INTERVAL = "loggingInterval";
+
+ public final static String HTTP_PROXY = "httpProxy";
+
+ public final static String HTTPS_PROXY = "httpsProxy";
+
+ public final static String PROXY_USERNAME = "proxyUsername";
+
+ public final static String PROXY_PASSWORD = "proxyPassword";
+
+ public final static String NO_PROXY = "noProxy";
+}
diff --git a/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
new file mode 100644
index 0000000..81505ca
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
@@ -0,0 +1,118 @@
+/*
+ * 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.registry.kubernetes.util;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+
+import java.util.Base64;
+
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.API_VERSION;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_ALGO;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_DATA;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_FILE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_PASSPHRASE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CONNECTION_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_PLACEHOLDER;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_URL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP2_DISABLE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTPS_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.LOGGING_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NO_PROXY;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.OAUTH_TOKEN;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_PASSWORD;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.REQUEST_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.ROLLING_TIMEOUT;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.TRUST_CERTS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USERNAME;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USE_HTTPS;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_INTERVAL;
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_LIMIT;
+
+public class KubernetesConfigUtils {
+
+ public static Config createKubernetesConfig(URL url) {
+ // Init default config
+ Config base = Config.autoConfigure(null);
+
+ // replace config with parameters if presents
+ return new ConfigBuilder(base) //
+ .withMasterUrl(buildMasterUrl(url)) //
+ .withApiVersion(url.getParameter(API_VERSION, base.getApiVersion())) //
+ .withNamespace(url.getParameter(NAMESPACE, base.getNamespace())) //
+ .withUsername(url.getParameter(USERNAME, base.getUsername())) //
+ .withPassword(url.getParameter(PASSWORD, base.getPassword())) //
+
+ .withOauthToken(url.getParameter(OAUTH_TOKEN, base.getOauthToken())) //
+
+ .withCaCertFile(url.getParameter(CA_CERT_FILE, base.getCaCertFile())) //
+ .withCaCertData(url.getParameter(CA_CERT_DATA, decodeBase64(base.getCaCertData()))) //
+
+ .withClientKeyFile(url.getParameter(CLIENT_KEY_FILE, base.getClientKeyFile())) //
+ .withClientKeyData(url.getParameter(CLIENT_KEY_DATA, decodeBase64(base.getClientKeyData()))) //
+
+ .withClientCertFile(url.getParameter(CLIENT_CERT_FILE, base.getClientCertFile())) //
+ .withClientCertData(url.getParameter(CLIENT_CERT_DATA, decodeBase64(base.getClientCertData()))) //
+
+ .withClientKeyAlgo(url.getParameter(CLIENT_KEY_ALGO, base.getClientKeyAlgo())) //
+ .withClientKeyPassphrase(url.getParameter(CLIENT_KEY_PASSPHRASE, base.getClientKeyPassphrase())) //
+
+ .withConnectionTimeout(url.getParameter(CONNECTION_TIMEOUT, base.getConnectionTimeout())) //
+ .withRequestTimeout(url.getParameter(REQUEST_TIMEOUT, base.getRequestTimeout())) //
+ .withRollingTimeout(url.getParameter(ROLLING_TIMEOUT, base.getRollingTimeout())) //
+
+ .withWatchReconnectInterval(url.getParameter(WATCH_RECONNECT_INTERVAL, base.getWatchReconnectInterval())) //
+ .withWatchReconnectLimit(url.getParameter(WATCH_RECONNECT_LIMIT, base.getWatchReconnectLimit())) //
+ .withLoggingInterval(url.getParameter(LOGGING_INTERVAL, base.getLoggingInterval())) //
+
+ .withTrustCerts(url.getParameter(TRUST_CERTS, base.isTrustCerts())) //
+ .withHttp2Disable(url.getParameter(HTTP2_DISABLE, base.isTrustCerts())) //
+
+ .withHttpProxy(url.getParameter(HTTP_PROXY, base.getHttpProxy())) //
+ .withHttpsProxy(url.getParameter(HTTPS_PROXY, base.getHttpsProxy())) //
+ .withProxyUsername(url.getParameter(PROXY_USERNAME, base.getProxyUsername())) //
+ .withProxyPassword(url.getParameter(PROXY_PASSWORD, base.getProxyPassword())) //
+ .withNoProxy(url.getParameter(NO_PROXY, base.getNoProxy())) //
+ .build();
+ }
+
+ private static String buildMasterUrl(URL url) {
+ if (DEFAULT_MASTER_PLACEHOLDER.equalsIgnoreCase(url.getHost())) {
+ return DEFAULT_MASTER_URL;
+ }
+ return (url.getParameter(USE_HTTPS, true) ?
+ "https://" : "http://")
+ + url.getHost() + ":" + url.getPort();
+ }
+
+ private static String decodeBase64(String str) {
+ return StringUtils.isNotEmpty(str) ?
+ new String(Base64.getDecoder().decode(str)) :
+ null;
+ }
+}
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..94177d8
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesRegistryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..3e1b88e
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..4301ab8
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
new file mode 100644
index 0000000..4dfae84
--- /dev/null
+++ b/dubbo-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesMeshEnvListenerFactory
diff --git a/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
new file mode 100644
index 0000000..28aa002
--- /dev/null
+++ b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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.registry.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.EndpointsBuilder;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServiceBuilder;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
+import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
+
+@ExtendWith({MockitoExtension.class})
+public class KubernetesServiceDiscoveryTest {
+ private static final String SERVICE_NAME = "TestService";
+
+ private static final String POD_NAME = "TestServer";
+
+ public KubernetesServer mockServer = new KubernetesServer(false, true);
+
+ private NamespacedKubernetesClient mockClient;
+
+ private ServiceInstancesChangedListener mockListener = Mockito.mock(ServiceInstancesChangedListener.class);
+
+ private URL serverUrl;
+
+ private Map<String, String> selector;
+
+ private KubernetesServiceDiscovery serviceDiscovery;
+
+
+ @BeforeEach
+ public void setUp() {
+ mockServer.before();
+ mockClient = mockServer.getClient().inNamespace("dubbo-demo");
+
+ ApplicationModel applicationModel = ApplicationModel.defaultModel();
+ applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig());
+
+ serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
+ .setProtocol("kubernetes")
+ .addParameter(NAMESPACE, "dubbo-demo")
+ .addParameter(KubernetesClientConst.USE_HTTPS, "false")
+ .addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
+ serverUrl.setScopeModel(applicationModel);
+
+ this.serviceDiscovery = new KubernetesServiceDiscovery(applicationModel, serverUrl);
+
+ System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
+ System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
+
+ selector = new HashMap<>(4);
+ selector.put("l", "v");
+ Pod pod = new PodBuilder()
+ .withNewMetadata().withName(POD_NAME).withLabels(selector).endMetadata()
+ .build();
+
+ Service service = new ServiceBuilder()
+ .withNewMetadata().withName(SERVICE_NAME).endMetadata()
+ .withNewSpec().withSelector(selector).endSpec().build();
+
+ Endpoints endPoints = new EndpointsBuilder()
+ .withNewMetadata().withName(SERVICE_NAME).endMetadata()
+ .addNewSubset()
+ .addNewAddress().withIp("ip1")
+ .withNewTargetRef().withUid("uid1").withName(POD_NAME).endTargetRef().endAddress()
+ .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+ .build();
+
+ mockClient.pods().resource(pod).create();
+ mockClient.services().resource(service).create();
+ mockClient.endpoints().resource(endPoints).create();
+ }
+
+ @AfterEach
+ public void destroy() throws Exception {
+ serviceDiscovery.destroy();
+ mockClient.close();
+ mockServer.after();
+ }
+
+ @Test
+ public void testEndpointsUpdate() throws Exception {
+ serviceDiscovery.setCurrentHostname(POD_NAME);
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance(SERVICE_NAME, "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+
+ serviceDiscovery.doRegister(serviceInstance);
+
+ HashSet<String> serviceList = new HashSet<>(4);
+ serviceList.add(SERVICE_NAME);
+ Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+ Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+
+ serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+ mockClient.endpoints().withName(SERVICE_NAME)
+ .edit(endpoints ->
+ new EndpointsBuilder(endpoints)
+ .editFirstSubset()
+ .addNewAddress()
+ .withIp("ip2")
+ .withNewTargetRef().withUid("uid2").withName(POD_NAME).endTargetRef()
+ .endAddress().endSubset()
+ .build());
+
+ Thread.sleep(2000);
+ ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+ ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(mockListener, Mockito.times(2)).onEvent(eventArgumentCaptor.capture());
+ Assertions.assertEquals(2, eventArgumentCaptor.getValue().getServiceInstances().size());
+
+ serviceDiscovery.doUnregister(serviceInstance);
+ }
+
+ @Test
+ public void testPodsUpdate() throws Exception {
+ serviceDiscovery.setCurrentHostname(POD_NAME);
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance(SERVICE_NAME, "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+
+ serviceDiscovery.doRegister(serviceInstance);
+
+ HashSet<String> serviceList = new HashSet<>(4);
+ serviceList.add(SERVICE_NAME);
+ Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+ Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+
+ serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+
+ serviceInstance = new DefaultServiceInstance(SERVICE_NAME, "Test12345", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+ serviceDiscovery.doUpdate(serviceInstance);
+
+ Thread.sleep(2000);
+ ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+ ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
+ Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+
+ serviceDiscovery.doUnregister(serviceInstance);
+ }
+
+ @Test
+ public void testServiceUpdate() throws Exception {
+ serviceDiscovery.setCurrentHostname(POD_NAME);
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance(SERVICE_NAME, "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+
+ serviceDiscovery.doRegister(serviceInstance);
+
+ HashSet<String> serviceList = new HashSet<>(4);
+ serviceList.add(SERVICE_NAME);
+ Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+ Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+
+ serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+
+ selector.put("app", "test");
+ mockClient.services().withName(SERVICE_NAME)
+ .edit(service -> new ServiceBuilder(service)
+ .editSpec()
+ .addToSelector(selector)
+ .endSpec()
+ .build());
+
+ Thread.sleep(2000);
+ ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+ ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
+ Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+
+ serviceDiscovery.doUnregister(serviceInstance);
+ }
+
+ @Test
+ public void testGetInstance() {
+ serviceDiscovery.setCurrentHostname(POD_NAME);
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance(SERVICE_NAME, "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+
+ serviceDiscovery.doRegister(serviceInstance);
+
+ serviceDiscovery.doUpdate(serviceInstance);
+
+ Assertions.assertEquals(1, serviceDiscovery.getServices().size());
+ Assertions.assertEquals(1, serviceDiscovery.getInstances(SERVICE_NAME).size());
+
+ serviceDiscovery.doUnregister(serviceInstance);
+ }
+}
diff --git a/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..ca6ee9c
--- /dev/null
+++ b/dubbo-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java
index 4863f3d..f299353 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractCacheManager.java
@@ -46,7 +46,7 @@
try {
cacheStore = FileCacheStoreFactory.getInstance(filePath, fileName, enableFileCache);
Map<String, String> properties = cacheStore.loadCache(entrySize);
- logger.info("Successfully loaded mapping cache from file " + fileName + ", entries " + properties.size());
+ logger.info("Successfully loaded " + getName() + " cache from file " + fileName + ", entries " + properties.size());
for (Map.Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
index 534f000..dc4d1c7 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/AbstractServiceNameMapping.java
@@ -24,7 +24,6 @@
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.model.ScopeModelAware;
import java.util.Collections;
import java.util.HashMap;
@@ -50,7 +49,7 @@
import static org.apache.dubbo.common.utils.CollectionUtils.toTreeSet;
import static org.apache.dubbo.common.utils.StringUtils.isBlank;
-public abstract class AbstractServiceNameMapping implements ServiceNameMapping, ScopeModelAware {
+public abstract class AbstractServiceNameMapping implements ServiceNameMapping {
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected ApplicationModel applicationModel;
private final MappingCacheManager mappingCacheManager;
@@ -73,7 +72,7 @@
.getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor());
}
- @Override
+ // just for test
public void setApplicationModel(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
index 7687b04..241ba8c 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.extension.Activate;
+import static org.apache.dubbo.common.constants.CommonConstants.IPV6_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
@@ -37,9 +38,10 @@
private final String[] includedInstanceParams;
public DefaultMetadataParamsFilter() {
- this.includedInstanceParams = new String[]{HEARTBEAT_TIMEOUT_KEY, TIMESTAMP_KEY};
+ this.includedInstanceParams = new String[]{HEARTBEAT_TIMEOUT_KEY, TIMESTAMP_KEY, IPV6_KEY};
this.excludedServiceParams = new String[]{MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE,
- QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, PID_KEY, TIMESTAMP_KEY, HEARTBEAT_TIMEOUT_KEY};
+ QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY, INTERFACES, PID_KEY, TIMESTAMP_KEY, HEARTBEAT_TIMEOUT_KEY,
+ IPV6_KEY};
}
@Override
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java
index 90d44b3..2365f6c 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingCacheManager.java
@@ -21,7 +21,6 @@
import org.apache.dubbo.rpc.model.ScopeModel;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
@@ -66,10 +65,4 @@
protected String getName() {
return "mapping";
}
-
- public void update(Map<String, Set<String>> newCache) {
- for (Map.Entry<String, Set<String>> entry : newCache.entrySet()) {
- cache.put(entry.getKey(), entry.getValue());
- }
- }
}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 645eb9e..baa2559 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.metadata;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
@@ -44,6 +45,7 @@
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR;
@@ -253,6 +255,13 @@
return serviceInfo;
}
+ public List<ServiceInfo> getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) {
+ return getServices().values()
+ .stream()
+ .filter(serviceInfo -> serviceInfo.matchProtocolServiceKey(consumerProtocolServiceKey))
+ .collect(Collectors.toList());
+ }
+
public Map<String, String> getExtendParams() {
return extendParams;
}
@@ -444,6 +453,7 @@
private String group;
private String version;
private String protocol;
+ private int port = -1;
private String path; // most of the time, path is the same with the interface name.
private Map<String, String> params;
@@ -460,12 +470,14 @@
// service + group + version + protocol
private volatile transient String matchKey;
+ private volatile transient ProtocolServiceKey protocolServiceKey;
+
private transient URL url;
public ServiceInfo() {}
public ServiceInfo(URL url, List<MetadataParamsFilter> filters) {
- this(url.getServiceInterface(), url.getGroup(), url.getVersion(), url.getProtocol(), url.getPath(), null);
+ this(url.getServiceInterface(), url.getGroup(), url.getVersion(), url.getProtocol(), url.getPort(), url.getPath(), null);
this.url = url;
Map<String, String> params = extractServiceParams(url, filters);
// initialize method params caches.
@@ -473,11 +485,12 @@
this.consumerMethodParams = URLParam.initMethodParameters(consumerParams);
}
- public ServiceInfo(String name, String group, String version, String protocol, String path, Map<String, String> params) {
+ public ServiceInfo(String name, String group, String version, String protocol, int port, String path, Map<String, String> params) {
this.name = name;
this.group = group;
this.version = version;
this.protocol = protocol;
+ this.port = port;
this.path = path;
this.params = params == null ? new ConcurrentHashMap<>() : params;
@@ -584,6 +597,18 @@
return matchKey;
}
+ public boolean matchProtocolServiceKey(ProtocolServiceKey protocolServiceKey) {
+ return ProtocolServiceKey.Matcher.isMatch(protocolServiceKey, getProtocolServiceKey());
+ }
+
+ public ProtocolServiceKey getProtocolServiceKey() {
+ if (protocolServiceKey != null) {
+ return protocolServiceKey;
+ }
+ protocolServiceKey = new ProtocolServiceKey(name, version, group, protocol);
+ return protocolServiceKey;
+ }
+
private String buildServiceKey(String name, String group, String version) {
this.serviceKey = URL.buildKey(name, group, version);
return this.serviceKey;
@@ -637,6 +662,14 @@
this.protocol = protocol;
}
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
public Map<String, String> getParams() {
if (params == null) {
return Collections.emptyMap();
@@ -702,7 +735,7 @@
}
public String toDescString() {
- return this.getMatchKey() + path + new TreeMap<>(getParams());
+ return this.getMatchKey() + port + path + new TreeMap<>(getParams());
}
public void addParameter(String key, String value) {
@@ -767,12 +800,13 @@
&& Objects.equals(this.getGroup(), serviceInfo.getGroup())
&& Objects.equals(this.getName(), serviceInfo.getName())
&& Objects.equals(this.getProtocol(), serviceInfo.getProtocol())
+ && Objects.equals(this.getPort(), serviceInfo.getPort())
&& this.getParams().equals(serviceInfo.getParams());
}
@Override
public int hashCode() {
- return Objects.hash(getVersion(), getGroup(), getName(), getProtocol(), getParams());
+ return Objects.hash(getVersion(), getGroup(), getName(), getProtocol(), getPort(), getParams());
}
@Override
@@ -786,6 +820,7 @@
"group='" + group + "'," +
"version='" + version + "'," +
"protocol='" + protocol + "'," +
+ "port='" + port + "'," +
"params=" + params + "," +
"}";
}
diff --git a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java
index 4b3523a..c745c92 100644
--- a/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java
+++ b/dubbo-metadata/dubbo-metadata-processor/src/test/java/org/apache/dubbo/metadata/annotation/processing/model/SimpleTypeModel.java
@@ -2,15 +2,15 @@
* 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 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.
+ * 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.
*/
diff --git a/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
index e8a5bcc..c8c99fc 100644
--- a/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
+++ b/dubbo-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
@@ -52,8 +52,8 @@
*/
public class RedisMetadataReport extends AbstractMetadataReport {
- private final static String REDIS_DATABASE_KEY = "database";
- private final static Logger logger = LoggerFactory.getLogger(RedisMetadataReport.class);
+ private static final String REDIS_DATABASE_KEY = "database";
+ private static final Logger logger = LoggerFactory.getLogger(RedisMetadataReport.class);
// protected , for test
protected JedisPool pool;
@@ -66,7 +66,7 @@
super(url);
timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
if (url.getParameter(CLUSTER_KEY, false)) {
- jedisClusterNodes = new HashSet<HostAndPort>();
+ jedisClusterNodes = new HashSet<>();
List<URL> urls = url.getBackupUrls();
for (URL tmpUrl : urls) {
jedisClusterNodes.add(new HostAndPort(tmpUrl.getHost(), tmpUrl.getPort()));
@@ -103,7 +103,7 @@
if (StringUtils.isEmpty(content)) {
return Collections.emptyList();
}
- return new ArrayList<String>(Arrays.asList(URL.decode(content)));
+ return new ArrayList<>(Arrays.asList(URL.decode(content)));
}
@Override
diff --git a/dubbo-metrics/dubbo-metrics-api/pom.xml b/dubbo-metrics/dubbo-metrics-api/pom.xml
index cb6e664..3c31ae3 100644
--- a/dubbo-metrics/dubbo-metrics-api/pom.xml
+++ b/dubbo-metrics/dubbo-metrics-api/pom.xml
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
@@ -35,5 +36,18 @@
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.tdunning</groupId>
+ <artifactId>t-digest</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporter.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporter.java
new file mode 100644
index 0000000..4e32c7e
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporter.java
@@ -0,0 +1,169 @@
+/*
+ * 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.metrics;
+
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
+import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
+import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
+import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
+import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
+import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.metrics.MetricsReporter;
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.collector.MetricsCollector;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.metrics.collector.AggregateMetricsCollector;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.ENABLE_JVM_METRICS_KEY;
+
+/**
+ * AbstractMetricsReporter.
+ */
+public abstract class AbstractMetricsReporter implements MetricsReporter {
+
+ private final Logger logger = LoggerFactory.getLogger(AbstractMetricsReporter.class);
+
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+
+ protected final URL url;
+ protected final List<MetricsCollector> collectors = new ArrayList<>();
+ protected final CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
+
+ private final ApplicationModel applicationModel;
+ private ScheduledExecutorService collectorSyncJobExecutor = null;
+
+ private static final int DEFAULT_SCHEDULE_INITIAL_DELAY = 5;
+ private static final int DEFAULT_SCHEDULE_PERIOD = 30;
+
+ protected AbstractMetricsReporter(URL url, ApplicationModel applicationModel) {
+ this.url = url;
+ this.applicationModel = applicationModel;
+ }
+
+ @Override
+ public void init() {
+ if (initialized.compareAndSet(false, true)) {
+ addJvmMetrics();
+ initCollectors();
+ scheduleMetricsCollectorSyncJob();
+
+ doInit();
+
+ registerDubboShutdownHook();
+ }
+ }
+
+ protected void addMeterRegistry(MeterRegistry registry) {
+ compositeRegistry.add(registry);
+ }
+
+ protected ApplicationModel getApplicationModel() {
+ return applicationModel;
+ }
+
+ private void addJvmMetrics() {
+ boolean enableJvmMetrics = url.getParameter(ENABLE_JVM_METRICS_KEY, false);
+ if (enableJvmMetrics) {
+ new ClassLoaderMetrics().bindTo(compositeRegistry);
+ new JvmMemoryMetrics().bindTo(compositeRegistry);
+ new JvmGcMetrics().bindTo(compositeRegistry);
+ new ProcessorMetrics().bindTo(compositeRegistry);
+ new JvmThreadMetrics().bindTo(compositeRegistry);
+ }
+ }
+
+ private void initCollectors() {
+ applicationModel.getBeanFactory().getOrRegisterBean(AggregateMetricsCollector.class);
+
+ collectors.add(applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class));
+ collectors.add(applicationModel.getBeanFactory().getBean(AggregateMetricsCollector.class));
+ }
+
+ private void scheduleMetricsCollectorSyncJob() {
+ NamedThreadFactory threadFactory = new NamedThreadFactory("metrics-collector-sync-job", true);
+ collectorSyncJobExecutor = Executors.newScheduledThreadPool(1, threadFactory);
+ collectorSyncJobExecutor.scheduleWithFixedDelay(() -> {
+ collectors.forEach(collector -> {
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ try {
+ switch (sample.getType()) {
+ case GAUGE:
+ GaugeMetricSample gaugeSample = (GaugeMetricSample) sample;
+ List<Tag> tags = new ArrayList<>();
+ gaugeSample.getTags().forEach((k, v) -> {
+ if (v == null) {
+ v = "";
+ }
+
+ tags.add(Tag.of(k, v));
+ });
+
+ Gauge.builder(gaugeSample.getName(), gaugeSample.getSupplier())
+ .description(gaugeSample.getDescription()).tags(tags).register(compositeRegistry);
+ break;
+ case COUNTER:
+ case TIMER:
+ case LONG_TASK_TIMER:
+ case DISTRIBUTION_SUMMARY:
+ // TODO
+ break;
+ default:
+ break;
+ }
+ } catch (Exception e) {
+ logger.error("error occurred when synchronize metrics collector.", e);
+ }
+ }
+ });
+ }, DEFAULT_SCHEDULE_INITIAL_DELAY, DEFAULT_SCHEDULE_PERIOD, TimeUnit.SECONDS);
+ }
+
+ private void registerDubboShutdownHook() {
+ applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class).addCallback(this::destroy);
+ }
+
+ public void destroy() {
+ if (collectorSyncJobExecutor != null) {
+ collectorSyncJobExecutor.shutdownNow();
+ }
+
+ doDestroy();
+ }
+
+ protected abstract void doInit();
+
+ protected abstract void doDestroy();
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporterFactory.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporterFactory.java
new file mode 100644
index 0000000..59763ed
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/AbstractMetricsReporterFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metrics;
+
+import org.apache.dubbo.common.metrics.MetricsReporterFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+/**
+ * AbstractMetricsReporterFactory.
+ */
+public abstract class AbstractMetricsReporterFactory implements MetricsReporterFactory {
+
+ private final ApplicationModel applicationModel;
+
+ public AbstractMetricsReporterFactory(ApplicationModel applicationModel) {
+ this.applicationModel = applicationModel;
+ }
+
+ protected ApplicationModel getApplicationModel() {
+ return applicationModel;
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounter.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounter.java
new file mode 100644
index 0000000..3dc3666
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounter.java
@@ -0,0 +1,90 @@
+/*
+ * 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.metrics.aggregate;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wrapper around Counter like Long and Integer.
+ * <p>
+ * Maintains a ring buffer of Counter to provide count over a sliding windows of time.
+ */
+public class TimeWindowCounter {
+ private final Long[] ringBuffer;
+ private final Long[] bucketStartTimeMillis;
+ private int currentBucket;
+ private long lastRotateTimestampMillis;
+ private final long durationBetweenRotatesMillis;
+
+ public TimeWindowCounter(int bucketNum, int timeWindowSeconds) {
+ this.ringBuffer = new Long[bucketNum];
+ this.bucketStartTimeMillis = new Long[bucketNum];
+ for (int i = 0; i < bucketNum; i++) {
+ this.ringBuffer[i] = 0L;
+ this.bucketStartTimeMillis[i] = System.currentTimeMillis();
+ }
+
+ this.currentBucket = 0;
+ this.lastRotateTimestampMillis = System.currentTimeMillis();
+ this.durationBetweenRotatesMillis = TimeUnit.SECONDS.toMillis(timeWindowSeconds) / bucketNum;
+ }
+
+ public synchronized double get() {
+ return rotate();
+ }
+
+ public long bucketLivedSeconds() {
+ return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - bucketStartTimeMillis[currentBucket]);
+ }
+
+ public synchronized void increment() {
+ this.increment(1L);
+ }
+
+ public synchronized void increment(Long step) {
+ rotate();
+ for (int i = 0; i < ringBuffer.length; i++) {
+ ringBuffer[i] = ringBuffer[i] + step;
+ }
+ }
+
+ public synchronized void decrement() {
+ this.decrement(1L);
+ }
+
+ public synchronized void decrement(Long step) {
+ rotate();
+ for (int i = 0; i < ringBuffer.length; i++) {
+ ringBuffer[i] = ringBuffer[i] - step;
+ }
+ }
+
+ private Long rotate() {
+ long timeSinceLastRotateMillis = System.currentTimeMillis() - lastRotateTimestampMillis;
+ while (timeSinceLastRotateMillis > durationBetweenRotatesMillis) {
+ ringBuffer[currentBucket] = 0L;
+ bucketStartTimeMillis[currentBucket] = lastRotateTimestampMillis + durationBetweenRotatesMillis;
+ if (++currentBucket >= ringBuffer.length) {
+ currentBucket = 0;
+ }
+ timeSinceLastRotateMillis -= durationBetweenRotatesMillis;
+ lastRotateTimestampMillis += durationBetweenRotatesMillis;
+ }
+ return ringBuffer[currentBucket];
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantile.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantile.java
new file mode 100644
index 0000000..86249c8
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantile.java
@@ -0,0 +1,75 @@
+/*
+ * 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.metrics.aggregate;
+
+import com.tdunning.math.stats.TDigest;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wrapper around TDigest.
+ * <p>
+ * Maintains a ring buffer of TDigest to provide quantiles over a sliding windows of time.
+ */
+public class TimeWindowQuantile {
+ private final double compression;
+ private final TDigest[] ringBuffer;
+ private int currentBucket;
+ private long lastRotateTimestampMillis;
+ private final long durationBetweenRotatesMillis;
+
+ public TimeWindowQuantile(double compression, int bucketNum, int timeWindowSeconds) {
+ this.compression = compression;
+ this.ringBuffer = new TDigest[bucketNum];
+ for (int i = 0; i < bucketNum; i++) {
+ this.ringBuffer[i] = TDigest.createDigest(compression);
+ }
+
+ this.currentBucket = 0;
+ this.lastRotateTimestampMillis = System.currentTimeMillis();
+ this.durationBetweenRotatesMillis = TimeUnit.SECONDS.toMillis(timeWindowSeconds) / bucketNum;
+ }
+
+ public synchronized double quantile(double q) {
+ TDigest currentBucket = rotate();
+
+ // This may return Double.NaN, and it's correct behavior.
+ // see: https://github.com/prometheus/client_golang/issues/85
+ return currentBucket.quantile(q);
+ }
+
+ public synchronized void add(double value) {
+ rotate();
+ for (TDigest bucket : ringBuffer) {
+ bucket.add(value);
+ }
+ }
+
+ private TDigest rotate() {
+ long timeSinceLastRotateMillis = System.currentTimeMillis() - lastRotateTimestampMillis;
+ while (timeSinceLastRotateMillis > durationBetweenRotatesMillis) {
+ ringBuffer[currentBucket] = TDigest.createDigest(compression);
+ if (++currentBucket >= ringBuffer.length) {
+ currentBucket = 0;
+ }
+ timeSinceLastRotateMillis -= durationBetweenRotatesMillis;
+ lastRotateTimestampMillis += durationBetweenRotatesMillis;
+ }
+ return ringBuffer[currentBucket];
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java
new file mode 100644
index 0000000..302c851
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollector.java
@@ -0,0 +1,157 @@
+/*
+ * 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.metrics.collector;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.collector.MetricsCollector;
+import org.apache.dubbo.common.metrics.event.MetricsEvent;
+import org.apache.dubbo.common.metrics.event.RTEvent;
+import org.apache.dubbo.common.metrics.event.RequestEvent;
+import org.apache.dubbo.common.metrics.listener.MetricsListener;
+import org.apache.dubbo.common.metrics.model.MethodMetric;
+import org.apache.dubbo.common.metrics.model.MetricsKey;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.nested.AggregationConfig;
+import org.apache.dubbo.metrics.aggregate.TimeWindowCounter;
+import org.apache.dubbo.metrics.aggregate.TimeWindowQuantile;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.common.metrics.model.MetricsCategory.REQUESTS;
+import static org.apache.dubbo.common.metrics.model.MetricsCategory.QPS;
+import static org.apache.dubbo.common.metrics.model.MetricsCategory.RT;
+
+/**
+ * Aggregation metrics collector implementation of {@link MetricsCollector}.
+ * This collector only enabled when metrics aggregation config is enabled.
+ */
+public class AggregateMetricsCollector implements MetricsCollector, MetricsListener {
+ private int bucketNum;
+ private int timeWindowSeconds;
+
+ private final Map<MethodMetric, TimeWindowCounter> totalRequests = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, TimeWindowCounter> succeedRequests = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, TimeWindowCounter> failedRequests = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, TimeWindowCounter> businessFailedRequests = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, TimeWindowCounter> qps = new ConcurrentHashMap<>();
+ private final Map<MethodMetric, TimeWindowQuantile> rt = new ConcurrentHashMap<>();
+
+ private final ApplicationModel applicationModel;
+
+ private static final Integer DEFAULT_COMPRESSION = 100;
+ private static final Integer DEFAULT_BUCKET_NUM = 10;
+ private static final Integer DEFAULT_TIME_WINDOW_SECONDS = 120;
+
+ public AggregateMetricsCollector(ApplicationModel applicationModel) {
+ this.applicationModel = applicationModel;
+ ConfigManager configManager = applicationModel.getApplicationConfigManager();
+ MetricsConfig config = configManager.getMetrics().orElse(null);
+ if (config != null && config.getAggregation() != null && Boolean.TRUE.equals(config.getAggregation().getEnabled())) {
+ // only registered when aggregation is enabled.
+ registerListener();
+
+ AggregationConfig aggregation = config.getAggregation();
+ this.bucketNum = aggregation.getBucketNum() == null ? DEFAULT_BUCKET_NUM : aggregation.getBucketNum();
+ this.timeWindowSeconds = aggregation.getTimeWindowSeconds() == null ? DEFAULT_TIME_WINDOW_SECONDS : aggregation.getTimeWindowSeconds();
+ }
+ }
+
+ private void registerListener() {
+ applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class).addListener(this);
+ }
+
+ @Override
+ public void onEvent(MetricsEvent event) {
+ if (event instanceof RTEvent) {
+ onRTEvent((RTEvent) event);
+ } else if (event instanceof RequestEvent) {
+ onRequestEvent((RequestEvent) event);
+ }
+ }
+
+ private void onRTEvent(RTEvent event) {
+ MethodMetric metric = (MethodMetric) event.getSource();
+ Long responseTime = event.getRt();
+ TimeWindowQuantile quantile = rt.computeIfAbsent(metric, k -> new TimeWindowQuantile(DEFAULT_COMPRESSION, bucketNum, timeWindowSeconds));
+ quantile.add(responseTime);
+ }
+
+ private void onRequestEvent(RequestEvent event) {
+ MethodMetric metric = (MethodMetric) event.getSource();
+ RequestEvent.Type type = event.getType();
+ TimeWindowCounter counter = null;
+ switch (type) {
+ case TOTAL:
+ counter = totalRequests.computeIfAbsent(metric, k -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
+ TimeWindowCounter qpsCounter = qps.computeIfAbsent(metric, k -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
+ qpsCounter.increment();
+ break;
+ case SUCCEED:
+ counter = succeedRequests.computeIfAbsent(metric, k -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
+ break;
+ case FAILED:
+ counter = failedRequests.computeIfAbsent(metric, k -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
+ break;
+ case BUSINESS_FAILED:
+ counter = businessFailedRequests.computeIfAbsent(metric, k -> new TimeWindowCounter(bucketNum, timeWindowSeconds));
+ break;
+
+ default:
+ break;
+ }
+
+ if (counter != null) {
+ counter.increment();
+ }
+ }
+
+ @Override
+ public List<MetricSample> collect() {
+ List<MetricSample> list = new ArrayList<>();
+ collectRequests(list);
+ collectQPS(list);
+ collectRT(list);
+
+ return list;
+ }
+
+ private void collectRequests(List<MetricSample> list) {
+ totalRequests.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_TOTAL_AGG, k.getTags(), REQUESTS, v::get)));
+ succeedRequests.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_SUCCEED_AGG, k.getTags(), REQUESTS, v::get)));
+ failedRequests.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_FAILED_AGG, k.getTags(), REQUESTS, v::get)));
+ businessFailedRequests.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_REQUESTS_BUSINESS_FAILED_AGG, k.getTags(), REQUESTS, v::get)));
+ }
+
+ private void collectQPS(List<MetricSample> list) {
+ qps.forEach((k, v) -> list.add(new GaugeMetricSample(MetricsKey.METRIC_QPS, k.getTags(), QPS, () -> v.get() / v.bucketLivedSeconds())));
+ }
+
+ private void collectRT(List<MetricSample> list) {
+ rt.forEach((k, v) -> {
+ list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_P99, k.getTags(), RT, () -> v.quantile(0.99)));
+ list.add(new GaugeMetricSample(MetricsKey.METRIC_RT_P95, k.getTags(), RT, () -> v.quantile(0.95)));
+ });
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsCollectExecutor.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsCollectExecutor.java
new file mode 100644
index 0000000..0ae3fdf
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsCollectExecutor.java
@@ -0,0 +1,110 @@
+/*
+ * 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.metrics.filter;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.METRIC_FILTER_START_TIME;
+
+import java.util.function.Supplier;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+
+public class MetricsCollectExecutor {
+
+ private final DefaultMetricsCollector collector;
+ private final Invocation invocation;
+ private String interfaceName;
+ private String methodName;
+ private String group;
+ private String version;
+
+
+ public MetricsCollectExecutor(DefaultMetricsCollector collector, Invocation invocation) {
+ init(invocation);
+
+ this.collector = collector;
+
+ this.invocation = invocation;
+ }
+
+ public void beforeExecute() {
+ collector.increaseTotalRequests(interfaceName, methodName, group, version);
+ collector.increaseProcessingRequests(interfaceName, methodName, group, version);
+ invocation.put(METRIC_FILTER_START_TIME, System.currentTimeMillis());
+ }
+
+ public void postExecute(Result result) {
+ if (result.hasException()) {
+ this.throwExecute(result.getException());
+ return;
+ }
+ collector.increaseSucceedRequests(interfaceName, methodName, group, version);
+ endExecute();
+ }
+
+ public void throwExecute(Throwable throwable){
+ if (throwable instanceof RpcException) {
+ RpcException rpcException = (RpcException)throwable;
+ if (rpcException.isBiz()) {
+ collector.businessFailedRequests(interfaceName, methodName, group, version);
+ }else{
+ collector.increaseFailedRequests(interfaceName, methodName, group, version);
+ }
+ }
+ endExecute(()-> throwable instanceof RpcException && ((RpcException) throwable).isBiz());
+ }
+
+ private void endExecute(){
+ this.endExecute(() -> true);
+ }
+
+ private void endExecute(Supplier<Boolean> rtStat){
+ if (rtStat.get()) {
+ Long endTime = System.currentTimeMillis();
+ Long beginTime = (Long) invocation.get(METRIC_FILTER_START_TIME);
+ Long rt = endTime - beginTime;
+ collector.addRT(interfaceName, methodName, group, version, rt);
+ }
+ collector.decreaseProcessingRequests(interfaceName, methodName, group, version);
+ }
+
+ private void init(Invocation invocation) {
+ String serviceUniqueName = invocation.getTargetServiceUniqueName();
+ String methodName = invocation.getMethodName();
+ String group = null;
+ String interfaceAndVersion;
+ String[] arr = serviceUniqueName.split("/");
+ if (arr.length == 2) {
+ group = arr[0];
+ interfaceAndVersion = arr[1];
+ } else {
+ interfaceAndVersion = arr[0];
+ }
+
+ String[] ivArr = interfaceAndVersion.split(":");
+ String interfaceName = ivArr[0];
+ String version = ivArr.length == 2 ? ivArr[1] : null;
+
+ this.interfaceName = interfaceName;
+ this.methodName = methodName;
+ this.group = group;
+ this.version = version;
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsFilter.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsFilter.java
new file mode 100644
index 0000000..4e5cc13
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/filter/MetricsFilter.java
@@ -0,0 +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.metrics.filter;
+
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
+
+import java.util.function.Consumer;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.rpc.BaseFilter;
+import org.apache.dubbo.rpc.Filter;
+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.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelAware;
+
+@Activate(group = PROVIDER, order = -1)
+public class MetricsFilter implements Filter, BaseFilter.Listener, ScopeModelAware {
+
+ private DefaultMetricsCollector collector = null;
+
+ private ApplicationModel applicationModel;
+
+
+ @Override
+ public void setApplicationModel(ApplicationModel applicationModel) {
+ this.applicationModel = applicationModel;
+ collector = applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class);
+ }
+
+ @Override
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (collector == null || !collector.isCollectEnabled()) {
+ return invoker.invoke(invocation);
+ }
+ collect(invocation, MetricsCollectExecutor::beforeExecute);
+
+ return invoker.invoke(invocation);
+ }
+
+ @Override
+ public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
+ collect(invocation, collector->collector.postExecute(result));
+ }
+
+ @Override
+ public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
+ collect(invocation,collector-> collector.throwExecute(t));
+ }
+
+ private void collect(Invocation invocation, Consumer<MetricsCollectExecutor> execute) {
+ MetricsCollectExecutor collectorExecutor = new MetricsCollectExecutor(collector, invocation);
+ execute.accept(collectorExecutor);
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/DefaultMetricsService.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/DefaultMetricsService.java
new file mode 100644
index 0000000..c91e5f3
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/service/DefaultMetricsService.java
@@ -0,0 +1,97 @@
+/*
+ * 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.metrics.service;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.collector.MetricsCollector;
+import org.apache.dubbo.common.metrics.model.MetricsCategory;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.common.metrics.service.MetricsEntity;
+import org.apache.dubbo.common.metrics.service.MetricsService;
+import org.apache.dubbo.metrics.collector.AggregateMetricsCollector;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Default implementation of {@link MetricsService}
+ */
+public class DefaultMetricsService implements MetricsService {
+
+ protected final List<MetricsCollector> collectors = new ArrayList<>();
+
+ private final ApplicationModel applicationModel;
+
+ public DefaultMetricsService(ApplicationModel applicationModel) {
+ this.applicationModel = applicationModel;
+ collectors.add(applicationModel.getBeanFactory().getBean(DefaultMetricsCollector.class));
+ collectors.add(applicationModel.getBeanFactory().getBean(AggregateMetricsCollector.class));
+ }
+
+ @Override
+ public Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(List<MetricsCategory> categories) {
+ return getMetricsByCategories(null, categories);
+ }
+
+ @Override
+ public Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(String serviceUniqueName, List<MetricsCategory> categories) {
+ return getMetricsByCategories(serviceUniqueName, null, null, categories);
+ }
+
+ @Override
+ public Map<MetricsCategory, List<MetricsEntity>> getMetricsByCategories(String serviceUniqueName, String methodName, Class<?>[] parameterTypes, List<MetricsCategory> categories) {
+ Map<MetricsCategory, List<MetricsEntity>> result = new HashMap<>();
+ for (MetricsCollector collector : collectors) {
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ if (categories.contains(sample.getCategory())) {
+ List<MetricsEntity> entities = result.computeIfAbsent(sample.getCategory(), k -> new ArrayList<>());
+ entities.add(sampleToEntity(sample));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private MetricsEntity sampleToEntity(MetricSample sample) {
+ MetricsEntity entity = new MetricsEntity();
+
+ entity.setName(sample.getName());
+ entity.setTags(sample.getTags());
+ entity.setCategory(sample.getCategory());
+ switch (sample.getType()) {
+ case GAUGE:
+ GaugeMetricSample gaugeSample = (GaugeMetricSample) sample;
+ entity.setValue(gaugeSample.getSupplier().get());
+ break;
+ case COUNTER:
+ case LONG_TASK_TIMER:
+ case TIMER:
+ case DISTRIBUTION_SUMMARY:
+ default:
+ break;
+ }
+
+ return entity;
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounterTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounterTest.java
new file mode 100644
index 0000000..0b49925
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowCounterTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metrics.aggregate;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TimeWindowCounterTest {
+
+ @Test
+ public void test() throws Exception {
+ TimeWindowCounter counter = new TimeWindowCounter(12, 1);
+ counter.increment();
+ Assertions.assertEquals(counter.get(), 1);
+ counter.decrement();
+ Assertions.assertEquals(counter.get(), 0);
+ counter.increment();
+ Thread.sleep(1000);
+ Assertions.assertEquals(counter.get(), 0);
+ Assertions.assertTrue(counter.bucketLivedSeconds() < 1);
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantileTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantileTest.java
new file mode 100644
index 0000000..f3364be
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/aggregate/TimeWindowQuantileTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metrics.aggregate;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TimeWindowQuantileTest {
+
+ @Test
+ public void test() throws Exception {
+ TimeWindowQuantile quantile = new TimeWindowQuantile(100, 12, 1);
+ for (int i = 1; i <= 100; i++) {
+ quantile.add(i);
+ }
+
+ Assertions.assertEquals(quantile.quantile(0.01), 2);
+ Assertions.assertEquals(quantile.quantile(0.99), 100);
+ Thread.sleep(1000);
+ Assertions.assertEquals(quantile.quantile(0.99), Double.NaN);
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java
new file mode 100644
index 0000000..0b3317a
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/collector/AggregateMetricsCollectorTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.metrics.collector;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.model.MetricsKey;
+import org.apache.dubbo.common.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.nested.AggregationConfig;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.*;
+
+public class AggregateMetricsCollectorTest {
+
+ private ApplicationModel applicationModel;
+ private DefaultMetricsCollector defaultCollector;
+
+ private String interfaceName;
+ private String methodName;
+ private String group;
+ private String version;
+
+ @BeforeEach
+ public void setup() {
+ ApplicationConfig config = new ApplicationConfig();
+ config.setName("MockMetrics");
+
+ applicationModel = ApplicationModel.defaultModel();
+ applicationModel.getApplicationConfigManager().setApplication(config);
+
+ defaultCollector = new DefaultMetricsCollector(applicationModel);
+ defaultCollector.setCollectEnabled(true);
+ MetricsConfig metricsConfig = new MetricsConfig();
+ AggregationConfig aggregationConfig = new AggregationConfig();
+ aggregationConfig.setEnabled(true);
+ aggregationConfig.setBucketNum(12);
+ aggregationConfig.setTimeWindowSeconds(120);
+ metricsConfig.setAggregation(aggregationConfig);
+ applicationModel.getApplicationConfigManager().setMetrics(metricsConfig);
+ applicationModel.getBeanFactory().registerBean(defaultCollector);
+
+ interfaceName = "org.apache.dubbo.MockInterface";
+ methodName = "mockMethod";
+ group = "mockGroup";
+ version = "1.0.0";
+ }
+
+ @AfterEach
+ public void teardown() {
+ applicationModel.destroy();
+ }
+
+ @Test
+ public void testRequestsMetrics() {
+ AggregateMetricsCollector collector = new AggregateMetricsCollector(applicationModel);
+ defaultCollector.increaseTotalRequests(interfaceName, methodName, group, version);
+ defaultCollector.increaseSucceedRequests(interfaceName, methodName, group, version);
+ defaultCollector.increaseFailedRequests(interfaceName, methodName, group, version);
+ defaultCollector.businessFailedRequests(interfaceName,methodName,group,version);
+
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+ }
+
+ samples = collector.collect();
+ Map<String, Long> sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+ Number number = ((GaugeMetricSample) k).getSupplier().get();
+ return number.longValue();
+ }));
+
+ Assertions.assertEquals(sampleMap.get("requests.total.aggregate"), 1L);
+ Assertions.assertEquals(sampleMap.get("requests.succeed.aggregate"), 1L);
+ Assertions.assertEquals(sampleMap.get("requests.failed.aggregate"), 1L);
+ Assertions.assertEquals(sampleMap.get(MetricsKey.METRIC_REQUESTS_BUSINESS_FAILED_AGG.getName()), 1L);
+
+ Assertions.assertTrue(sampleMap.containsKey("qps"));
+ }
+
+ @Test
+ public void testRTMetrics() {
+ AggregateMetricsCollector collector = new AggregateMetricsCollector(applicationModel);
+ defaultCollector.addRT(interfaceName, methodName, group, version, 10L);
+
+ List<MetricSample> samples = collector.collect();
+ for (MetricSample sample : samples) {
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+ }
+
+ Map<String, Long> sampleMap = samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+ Number number = ((GaugeMetricSample) k).getSupplier().get();
+ return number.longValue();
+ }));
+
+ Assertions.assertTrue(sampleMap.containsKey("rt.p99"));
+ Assertions.assertTrue(sampleMap.containsKey("rt.p95"));
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java
new file mode 100644
index 0000000..dbe6806
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-api/src/test/java/org/apache/dubbo/metrics/filter/MetricsFilterTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.metrics.filter;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.dubbo.common.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.common.metrics.model.MetricsKey;
+import org.apache.dubbo.common.metrics.model.sample.MetricSample;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.rpc.AppResponse;
+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.model.ApplicationModel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class MetricsFilterTest {
+
+ private ApplicationModel applicationModel;
+ private MetricsFilter filter;
+ private DefaultMetricsCollector collector;
+ private RpcInvocation invocation;
+ private final Invoker<?> invoker = mock(Invoker.class);
+
+ private static final String INTERFACE_NAME = "org.apache.dubbo.MockInterface";
+ private static final String METHOD_NAME = "mockMethod";
+ private static final String GROUP = "mockGroup";
+ private static final String VERSION = "1.0.0";
+
+ @BeforeEach
+ public void setup() {
+ ApplicationConfig config = new ApplicationConfig();
+ config.setName("MockMetrics");
+
+ applicationModel = ApplicationModel.defaultModel();
+ applicationModel.getApplicationConfigManager().setApplication(config);
+
+ invocation = new RpcInvocation();
+ filter = new MetricsFilter();
+
+ collector = applicationModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class);
+ filter.setApplicationModel(applicationModel);
+ }
+
+ @AfterEach
+ public void teardown() {
+ applicationModel.destroy();
+ }
+
+ @Test
+ public void testCollectDisabled() {
+ given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+
+ filter.invoke(invoker, invocation);
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+ Assertions.assertTrue(metricsMap.isEmpty());
+ }
+
+ @Test
+ public void testFailedRequests() {
+ collector.setCollectEnabled(true);
+ given(invoker.invoke(invocation)).willThrow(new RpcException("failed"));
+ initParam();
+
+ try {
+ filter.invoke(invoker, invocation);
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof RpcException);
+ filter.onError(e, invoker, invocation);
+ }
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+ Assertions.assertTrue(metricsMap.containsKey("requests.failed"));
+ Assertions.assertFalse(metricsMap.containsKey("requests.succeed"));
+
+ MetricSample sample = metricsMap.get("requests.failed");
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION);
+ }
+
+
+ @Test
+ public void testBusinessFailedRequests() {
+ collector.setCollectEnabled(true);
+
+ given(invoker.invoke(invocation)).willThrow(new RpcException(RpcException.BIZ_EXCEPTION));
+ initParam();
+
+ try {
+ filter.invoke(invoker, invocation);
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof RpcException);
+ filter.onError(e, invoker, invocation);
+ }
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+ Assertions.assertTrue(metricsMap.containsKey(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED.getName()));
+ Assertions.assertFalse(metricsMap.containsKey("requests.succeed"));
+
+ MetricSample sample = metricsMap.get(MetricsKey.METRIC_REQUEST_BUSINESS_FAILED.getName());
+
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION);
+ }
+
+ @Test
+ public void testSucceedRequests() {
+ collector.setCollectEnabled(true);
+ given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+ initParam();
+
+ Result result = filter.invoke(invoker, invocation);
+
+ filter.onResponse(result, invoker, invocation);
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+ Assertions.assertFalse(metricsMap.containsKey("requests.failed"));
+ Assertions.assertTrue(metricsMap.containsKey("requests.succeed"));
+
+ MetricSample sample = metricsMap.get("requests.succeed");
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP);
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION);
+ }
+
+ @Test
+ public void testMissingGroup() {
+ collector.setCollectEnabled(true);
+ given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+ invocation.setTargetServiceUniqueName(INTERFACE_NAME + ":" + VERSION);
+ invocation.setMethodName(METHOD_NAME);
+ invocation.setParameterTypes(new Class[]{String.class});
+
+ Result result = filter.invoke(invoker, invocation);
+
+ filter.onResponse(result, invoker, invocation);
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+
+ MetricSample sample = metricsMap.get("requests.succeed");
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertNull(tags.get(TAG_GROUP_KEY));
+ Assertions.assertEquals(tags.get(TAG_VERSION_KEY), VERSION);
+ }
+
+ @Test
+ public void testMissingVersion() {
+ collector.setCollectEnabled(true);
+ given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+ invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME);
+ invocation.setMethodName(METHOD_NAME);
+ invocation.setParameterTypes(new Class[]{String.class});
+
+ Result result = filter.invoke(invoker, invocation);
+
+ filter.onResponse(result, invoker, invocation);
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+
+ MetricSample sample = metricsMap.get("requests.succeed");
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertEquals(tags.get(TAG_GROUP_KEY), GROUP);
+ Assertions.assertNull(tags.get(TAG_VERSION_KEY));
+ }
+
+ @Test
+ public void testMissingGroupAndVersion() {
+ collector.setCollectEnabled(true);
+ given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+ invocation.setTargetServiceUniqueName(INTERFACE_NAME);
+ invocation.setMethodName(METHOD_NAME);
+ invocation.setParameterTypes(new Class[]{String.class});
+
+ Result result = filter.invoke(invoker, invocation);
+
+ filter.onResponse(result, invoker, invocation);
+
+ Map<String, MetricSample> metricsMap = getMetricsMap();
+
+ MetricSample sample = metricsMap.get("requests.succeed");
+ Map<String, String> tags = sample.getTags();
+
+ Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), INTERFACE_NAME);
+ Assertions.assertEquals(tags.get(TAG_METHOD_KEY), METHOD_NAME);
+ Assertions.assertNull(tags.get(TAG_GROUP_KEY));
+ Assertions.assertNull(tags.get(TAG_VERSION_KEY));
+ }
+
+ private void initParam() {
+ invocation.setTargetServiceUniqueName(GROUP + "/" + INTERFACE_NAME + ":" + VERSION);
+ invocation.setMethodName(METHOD_NAME);
+ invocation.setParameterTypes(new Class[]{String.class});
+ }
+
+ private Map<String, MetricSample> getMetricsMap() {
+ List<MetricSample> samples = collector.collect();
+ return samples.stream().collect(Collectors.toMap(MetricSample::getName, Function.identity()));
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-prometheus/pom.xml b/dubbo-metrics/dubbo-metrics-prometheus/pom.xml
index 0020915..18f90e1 100644
--- a/dubbo-metrics/dubbo-metrics-prometheus/pom.xml
+++ b/dubbo-metrics/dubbo-metrics-prometheus/pom.xml
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
@@ -35,5 +36,23 @@
<artifactId>dubbo-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-registry-prometheus</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient_pushgateway</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporter.java b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporter.java
new file mode 100644
index 0000000..89684e7
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.metrics.prometheus;
+
+import com.sun.net.httpserver.HttpServer;
+import io.micrometer.prometheus.PrometheusConfig;
+import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
+import io.prometheus.client.exporter.PushGateway;
+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.NamedThreadFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metrics.AbstractMetricsReporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_EXPORTER_ENABLED_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_EXPORTER_METRICS_PORT_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_METRICS_PORT;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_EXPORTER_METRICS_PATH_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_METRICS_PATH;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_ENABLED_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_JOB_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_JOB_NAME;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_DEFAULT_PUSH_INTERVAL;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_USERNAME_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY;
+
+/**
+ * Metrics reporter for prometheus.
+ */
+public class PrometheusMetricsReporter extends AbstractMetricsReporter {
+
+ private final Logger logger = LoggerFactory.getLogger(PrometheusMetricsReporter.class);
+
+ private final PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
+ private ScheduledExecutorService pushJobExecutor = null;
+ private HttpServer prometheusExporterHttpServer = null;
+ private Thread httpServerThread = null;
+
+ public PrometheusMetricsReporter(URL url, ApplicationModel applicationModel) {
+ super(url, applicationModel);
+ }
+
+ @Override
+ public void doInit() {
+ addMeterRegistry(prometheusRegistry);
+ exportHttpServer();
+ schedulePushJob();
+ }
+
+ private void exportHttpServer() {
+ boolean exporterEnabled = url.getParameter(PROMETHEUS_EXPORTER_ENABLED_KEY, false);
+ if (exporterEnabled) {
+ int port = url.getParameter(PROMETHEUS_EXPORTER_METRICS_PORT_KEY, PROMETHEUS_DEFAULT_METRICS_PORT);
+ String path = url.getParameter(PROMETHEUS_EXPORTER_METRICS_PATH_KEY, PROMETHEUS_DEFAULT_METRICS_PATH);
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+
+ try {
+ prometheusExporterHttpServer = HttpServer.create(new InetSocketAddress(port), 0);
+ prometheusExporterHttpServer.createContext(path, httpExchange -> {
+ String response = prometheusRegistry.scrape();
+ httpExchange.sendResponseHeaders(200, response.getBytes().length);
+ try (OutputStream os = httpExchange.getResponseBody()) {
+ os.write(response.getBytes());
+ }
+ });
+
+ httpServerThread = new Thread(prometheusExporterHttpServer::start);
+ httpServerThread.start();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void schedulePushJob() {
+ boolean pushEnabled = url.getParameter(PROMETHEUS_PUSHGATEWAY_ENABLED_KEY, false);
+ if (pushEnabled) {
+ String baseUrl = url.getParameter(PROMETHEUS_PUSHGATEWAY_BASE_URL_KEY);
+ String job = url.getParameter(PROMETHEUS_PUSHGATEWAY_JOB_KEY, PROMETHEUS_DEFAULT_JOB_NAME);
+ int pushInterval = url.getParameter(PROMETHEUS_PUSHGATEWAY_PUSH_INTERVAL_KEY, PROMETHEUS_DEFAULT_PUSH_INTERVAL);
+ String username = url.getParameter(PROMETHEUS_PUSHGATEWAY_USERNAME_KEY);
+ String password = url.getParameter(PROMETHEUS_PUSHGATEWAY_PASSWORD_KEY);
+
+ NamedThreadFactory threadFactory = new NamedThreadFactory("prometheus-push-job", true);
+ pushJobExecutor = Executors.newScheduledThreadPool(1, threadFactory);
+ PushGateway pushGateway = new PushGateway(baseUrl);
+ if (!StringUtils.isBlank(username)) {
+ pushGateway.setConnectionFactory(new BasicAuthHttpConnectionFactory(username, password));
+ }
+
+ pushJobExecutor.scheduleWithFixedDelay(() -> push(pushGateway, job), pushInterval, pushInterval, TimeUnit.SECONDS);
+ }
+ }
+
+ protected void push(PushGateway pushGateway, String job) {
+ try {
+ pushGateway.pushAdd(prometheusRegistry.getPrometheusRegistry(), job);
+ } catch (IOException e) {
+ logger.error("Error occurred when pushing metrics to prometheus: ", e);
+ }
+ }
+
+ @Override
+ public void doDestroy() {
+ if (prometheusExporterHttpServer != null) {
+ prometheusExporterHttpServer.stop(1);
+ }
+
+ if (httpServerThread != null) {
+ httpServerThread.interrupt();
+ }
+
+ if (pushJobExecutor != null) {
+ pushJobExecutor.shutdownNow();
+ }
+ }
+
+ /**
+ * ut only
+ */
+ @Deprecated
+ public ScheduledExecutorService getPushJobExecutor() {
+ return pushJobExecutor;
+ }
+
+ /**
+ * ut only
+ */
+ @Deprecated
+ public PrometheusMeterRegistry getPrometheusRegistry() {
+ return prometheusRegistry;
+ }
+}
diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactory.java b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactory.java
new file mode 100644
index 0000000..f28aa74
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-prometheus/src/main/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.metrics.prometheus;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.metrics.MetricsReporter;
+import org.apache.dubbo.metrics.AbstractMetricsReporterFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+/**
+ * MetricsReporterFactory to create PrometheusMetricsReporter.
+ */
+public class PrometheusMetricsReporterFactory extends AbstractMetricsReporterFactory {
+
+ public PrometheusMetricsReporterFactory(ApplicationModel applicationModel) {
+ super(applicationModel);
+ }
+
+ @Override
+ public MetricsReporter createMetricsReporter(URL url) {
+ return new PrometheusMetricsReporter(url, getApplicationModel());
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactoryTest.java
similarity index 60%
copy from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java
copy to dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactoryTest.java
index 92bcfb5..f0f87a8 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java
+++ b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterFactoryTest.java
@@ -14,25 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+
+package org.apache.dubbo.metrics.prometheus;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.url.component.ServiceConfigURL;
-import org.apache.dubbo.remoting.Constants;
-
+import org.apache.dubbo.common.metrics.MetricsReporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-public class PortUnificationServerTest {
+public class PrometheusMetricsReporterFactoryTest {
@Test
- public void testBind() {
- URL url = new ServiceConfigURL(CommonConstants.TRIPLE, "localhost", 8898,
- new String[]{Constants.BIND_PORT_KEY, String.valueOf(8898)});
+ public void test() {
+ ApplicationModel applicationModel = ApplicationModel.defaultModel();
+ PrometheusMetricsReporterFactory factory = new PrometheusMetricsReporterFactory(applicationModel);
+ MetricsReporter reporter = factory.createMetricsReporter(URL.valueOf("prometheus://localhost:9090/"));
- final PortUnificationServer server = new PortUnificationServer(url);
- server.bind();
- Assertions.assertTrue(server.isBound());
+ Assertions.assertTrue(reporter instanceof PrometheusMetricsReporter);
}
}
diff --git a/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java
new file mode 100644
index 0000000..a429ce3
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-prometheus/src/test/java/org/apache/dubbo/metrics/prometheus/PrometheusMetricsReporterTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.metrics.prometheus;
+
+import io.micrometer.prometheus.PrometheusMeterRegistry;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.nested.PrometheusConfig;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_PROMETHEUS;
+
+public class PrometheusMetricsReporterTest {
+
+ private MetricsConfig metricsConfig;
+ private ApplicationModel applicationModel;
+
+ @BeforeEach
+ public void setup() {
+ metricsConfig = new MetricsConfig();
+ applicationModel = ApplicationModel.defaultModel();
+ metricsConfig.setProtocol(PROTOCOL_PROMETHEUS);
+ }
+
+ @AfterEach
+ public void teardown() {
+ applicationModel.destroy();
+ }
+
+ @Test
+ public void testJvmMetrics() {
+ metricsConfig.setEnableJvmMetrics(true);
+ PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel);
+ reporter.init();
+
+ PrometheusMeterRegistry prometheusRegistry = reporter.getPrometheusRegistry();
+ Double d1 = prometheusRegistry.getPrometheusRegistry().getSampleValue("none_exist_metric");
+ Double d2 = prometheusRegistry.getPrometheusRegistry().getSampleValue("jvm_gc_memory_promoted_bytes_total");
+
+ Assertions.assertNull(d1);
+ Assertions.assertNotNull(d2);
+ }
+
+ @Test
+ public void testExporter() {
+ int port = NetUtils.getAvailablePort();
+
+ PrometheusConfig prometheusConfig = new PrometheusConfig();
+ PrometheusConfig.Exporter exporter = new PrometheusConfig.Exporter();
+ exporter.setMetricsPort(port);
+ exporter.setMetricsPath("/metrics");
+ exporter.setEnabled(true);
+ prometheusConfig.setExporter(exporter);
+ metricsConfig.setPrometheus(prometheusConfig);
+ metricsConfig.setEnableJvmMetrics(true);
+
+ PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel);
+ reporter.init();
+
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpGet request = new HttpGet("http://localhost:" + port + "/metrics");
+ CloseableHttpResponse response = client.execute(request);
+ InputStream inputStream = response.getEntity().getContent();
+ String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
+ Assertions.assertTrue(text.contains("jvm_gc_memory_promoted_bytes_total"));
+ } catch (Exception e) {
+ Assertions.fail(e);
+ } finally {
+ reporter.destroy();
+ }
+ }
+
+ @Test
+ public void testPushgateway() {
+ PrometheusConfig prometheusConfig = new PrometheusConfig();
+ PrometheusConfig.Pushgateway pushgateway = new PrometheusConfig.Pushgateway();
+ pushgateway.setJob("mock");
+ pushgateway.setBaseUrl("localhost:9091");
+ pushgateway.setEnabled(true);
+ pushgateway.setPushInterval(1);
+ prometheusConfig.setPushgateway(pushgateway);
+ metricsConfig.setPrometheus(prometheusConfig);
+
+ PrometheusMetricsReporter reporter = new PrometheusMetricsReporter(metricsConfig.toUrl(), applicationModel);
+ reporter.init();
+
+ ScheduledExecutorService executor = reporter.getPushJobExecutor();
+ Assertions.assertTrue(executor != null && !executor.isTerminated() && !executor.isShutdown());
+
+ reporter.destroy();
+ Assertions.assertTrue(executor.isTerminated() || executor.isShutdown());
+ }
+}
diff --git a/dubbo-metrics/pom.xml b/dubbo-metrics/pom.xml
index 8c4291a..2b101fc 100644
--- a/dubbo-metrics/pom.xml
+++ b/dubbo-metrics/pom.xml
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
<module>dubbo-metrics-api</module>
diff --git a/dubbo-monitor/dubbo-monitor-api/pom.xml b/dubbo-monitor/dubbo-monitor-api/pom.xml
index 000f1d8..f7fc8dd 100644
--- a/dubbo-monitor/dubbo-monitor-api/pom.xml
+++ b/dubbo-monitor/dubbo-monitor-api/pom.xml
@@ -54,5 +54,12 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-monitor/dubbo-monitor-default/pom.xml b/dubbo-monitor/dubbo-monitor-default/pom.xml
index 4489e84..a230f36 100644
--- a/dubbo-monitor/dubbo-monitor-default/pom.xml
+++ b/dubbo-monitor/dubbo-monitor-default/pom.xml
@@ -66,5 +66,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-plugin/dubbo-qos/pom.xml b/dubbo-plugin/dubbo-qos/pom.xml
index 947fca6..cdc6797 100644
--- a/dubbo-plugin/dubbo-qos/pom.xml
+++ b/dubbo-plugin/dubbo-qos/pom.xml
@@ -64,6 +64,12 @@
<artifactId>dubbo-serialization-hessian2</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoder.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoder.java
index 52e58b5..3c559a5 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoder.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/decoder/TelnetCommandDecoder.java
@@ -25,11 +25,15 @@
public static final CommandContext decode(String str) {
CommandContext commandContext = null;
if (!StringUtils.isBlank(str)) {
+ str = str.trim();
String[] array = str.split("(?<![\\\\]) ");
if (array.length > 0) {
- String name = array[0];
String[] targetArgs = new String[array.length - 1];
System.arraycopy(array, 1, targetArgs, 0, array.length - 1);
+ String name = array[0].trim();
+ if (name.equals("invoke") && array.length > 2) {
+ targetArgs = reBuildInvokeCmdArgs(str);
+ }
commandContext = CommandContextFactory.newInstance( name, targetArgs,false);
commandContext.setOriginRequest(str);
}
@@ -38,4 +42,8 @@
return commandContext;
}
+ private static String[] reBuildInvokeCmdArgs(String cmd) {
+ return new String[] {cmd.substring(cmd.indexOf(" ") + 1).trim()};
+ }
+
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
index 306c35c..66a33d0 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
@@ -23,7 +23,6 @@
import org.apache.dubbo.rpc.model.ProviderModel;
import java.util.Collection;
-import java.util.List;
@Activate
public class ProviderReadinessProbe implements ReadinessProbe {
@@ -46,17 +45,20 @@
return true;
}
- boolean hasService = false;
+ boolean hasService = false, anyOnline = false;
for (ProviderModel providerModel : providerModelList) {
- List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
- for (ProviderModel.RegisterStatedURL statedUrl : statedUrls) {
- if (statedUrl.isRegistered()) {
- hasService = true;
- break;
- }
+ if (providerModel.getModuleModel().isInternal()) {
+ continue;
}
+ hasService = true;
+ anyOnline = anyOnline ||
+ providerModel.getStatedUrl().isEmpty() ||
+ providerModel.getStatedUrl().stream().anyMatch(ProviderModel.RegisterStatedURL::isRegistered);
}
- return hasService;
+ // no service => check pass
+ // has service and any online => check pass
+ // has service and none online => check fail
+ return !(hasService && !anyOnline);
}
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/protocol/QosProtocolWrapper.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/protocol/QosProtocolWrapper.java
index f0e420c..7fc3693 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/protocol/QosProtocolWrapper.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/protocol/QosProtocolWrapper.java
@@ -21,7 +21,9 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.qos.common.QosConstants;
+import org.apache.dubbo.qos.pu.QosWireProtocol;
import org.apache.dubbo.qos.server.Server;
+import org.apache.dubbo.remoting.api.WireProtocol;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
@@ -96,6 +98,10 @@
}
boolean qosEnable = url.getParameter(QOS_ENABLE, true);
+ WireProtocol qosWireProtocol = frameworkModel.getExtensionLoader(WireProtocol.class).getExtension("qos");
+ if(qosWireProtocol != null) {
+ ((QosWireProtocol) qosWireProtocol).setQosEnable(qosEnable);
+ }
if (!qosEnable) {
logger.info("qos won't be started because it is disabled. " +
"Please check dubbo.application.qos.enable is configured either in system property, " +
@@ -107,6 +113,11 @@
int port = url.getParameter(QOS_PORT, QosConstants.DEFAULT_PORT);
boolean acceptForeignIp = Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP, "false"));
Server server = frameworkModel.getBeanFactory().getBean(Server.class);
+
+ if (server.isStarted()) {
+ return;
+ }
+
server.setHost(host);
server.setPort(port);
server.setAcceptForeignIp(acceptForeignIp);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosDetector.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosDetector.java
new file mode 100644
index 0000000..6a91405
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosDetector.java
@@ -0,0 +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.qos.pu;
+
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+public class QosDetector implements ProtocolDetector {
+
+ private final QosHTTP1Detector qosHTTP1Detector = new QosHTTP1Detector();
+ private final TelnetDetector telnetDetector;
+ private boolean QosEnableFlag = true;
+
+ public void setQosEnableFlag(boolean qosEnableFlag) {
+ QosEnableFlag = qosEnableFlag;
+ }
+
+ public QosDetector(FrameworkModel frameworkModel) {
+ this.telnetDetector = new TelnetDetector(frameworkModel);
+ }
+
+ @Override
+ public Result detect(ChannelBuffer in) {
+ if(!QosEnableFlag) {
+ return Result.UNRECOGNIZED;
+ }
+ Result h1Res = qosHTTP1Detector.detect(in);
+ if(h1Res.equals(Result.RECOGNIZED)) {
+ return h1Res;
+ }
+ Result telRes = telnetDetector.detect(in);
+ if(telRes.equals(Result.RECOGNIZED)) {
+ return telRes;
+ }
+ if(h1Res.equals(Result.NEED_MORE_DATA) || telRes.equals(Result.NEED_MORE_DATA)) {
+ return Result.NEED_MORE_DATA;
+ }
+ return Result.UNRECOGNIZED;
+ }
+
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosHTTP1Detector.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosHTTP1Detector.java
new file mode 100644
index 0000000..9a62b84
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosHTTP1Detector.java
@@ -0,0 +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.qos.pu;
+
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+
+public class QosHTTP1Detector implements ProtocolDetector {
+ private static boolean isHttp(int magic) {
+ return magic == 'G' || magic == 'P';
+ }
+
+ @Override
+ public Result detect(ChannelBuffer in) {
+ if (in.readableBytes() < 2) {
+ return Result.NEED_MORE_DATA;
+ }
+ final int magic = in.getByte(in.readerIndex());
+ // h2 starts with "PR"
+ if (isHttp(magic) && in.getByte(in.readerIndex()+1) != 'R' ){
+ return Result.RECOGNIZED;
+ }
+ return Result.UNRECOGNIZED;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosWireProtocol.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosWireProtocol.java
new file mode 100644
index 0000000..71e6633
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/QosWireProtocol.java
@@ -0,0 +1,63 @@
+/*
+ * 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.qos.pu;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.qos.server.DubboLogo;
+import org.apache.dubbo.qos.server.handler.QosProcessHandler;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.api.AbstractWireProtocol;
+import org.apache.dubbo.remoting.api.pu.ChannelHandlerPretender;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ScopeModelAware;
+
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.ssl.SslContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Activate
+public class QosWireProtocol extends AbstractWireProtocol implements ScopeModelAware {
+
+ public QosWireProtocol(FrameworkModel frameworkModel) {
+ super(new QosDetector(frameworkModel));
+ }
+
+ public void setQosEnable(boolean flag) {
+ ((QosDetector)this.detector()).setQosEnableFlag(flag);
+ }
+
+ @Override
+ public void configServerProtocolHandler(URL url, ChannelOperator operator) {
+ // add qosProcess handler
+ QosProcessHandler handler = new QosProcessHandler(url.getOrDefaultFrameworkModel(),
+ DubboLogo.DUBBO, false);
+ List<ChannelHandler> handlers = new ArrayList<>();
+ handlers.add(new ChannelHandlerPretender(handler));
+ operator.configChannelHandler(handlers);
+ }
+
+
+ @Override
+ public void configClientPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext) {
+
+ }
+
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/TelnetDetector.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/TelnetDetector.java
new file mode 100644
index 0000000..8419993
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/pu/TelnetDetector.java
@@ -0,0 +1,95 @@
+/*
+ * 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.qos.pu;
+
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.decoder.TelnetCommandDecoder;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffers;
+import org.apache.dubbo.remoting.buffer.HeapChannelBuffer;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import io.netty.util.CharsetUtil;
+
+import static java.lang.Math.min;
+
+
+public class TelnetDetector implements ProtocolDetector {
+
+ private FrameworkModel frameworkModel;
+ private final int MaxSize = 2048;
+ private final ChannelBuffer AytPreface = new HeapChannelBuffer(new byte[]{(byte) 0xff, (byte) 0xf6});
+
+ public TelnetDetector(FrameworkModel frameworkModel) {
+ this.frameworkModel = frameworkModel;
+ }
+
+ @Override
+ public Result detect(ChannelBuffer in) {
+ if (in.readableBytes() >= MaxSize) {
+ return Result.UNRECOGNIZED;
+ }
+ Result resCommand = commandDetect(in);
+ if (resCommand.equals(Result.RECOGNIZED)){
+ return resCommand;
+ }
+ Result resAyt = telnetAytDetect(in);
+ if (resAyt.equals(Result.RECOGNIZED)) {
+ return resAyt;
+ }
+ if (resAyt.equals(Result.UNRECOGNIZED) && resCommand.equals(Result.UNRECOGNIZED)) {
+ return Result.UNRECOGNIZED;
+ }
+ return Result.NEED_MORE_DATA;
+ }
+
+ private Result commandDetect(ChannelBuffer in) {
+ // detect if remote channel send a qos command to server
+ ChannelBuffer back = in.copy();
+ byte[] backBytes = new byte[back.readableBytes()];
+ back.getBytes(back.readerIndex(), backBytes);
+
+ String s = new String(backBytes, CharsetUtil.UTF_8);
+ // trim /r/n to let parser work for input
+ s = s.trim();
+ CommandContext commandContext = TelnetCommandDecoder.decode(s);
+ if(frameworkModel.getExtensionLoader(BaseCommand.class).hasExtension(commandContext.getCommandName())){
+ return Result.RECOGNIZED;
+ }
+ return Result.UNRECOGNIZED;
+ }
+
+ private Result telnetAytDetect(ChannelBuffer in) {
+ // detect if remote channel send a telnet ayt command to server
+ int prefaceLen = AytPreface.readableBytes();
+ int bytesRead = min(in.readableBytes(), prefaceLen);
+ if(bytesRead == 0 || !ChannelBuffers.prefixEquals(in, AytPreface, bytesRead)) {
+ return Result.UNRECOGNIZED;
+ }
+ if(bytesRead == prefaceLen) {
+ // we need to consume preface because it's not a qos command
+ // consume and remember to mark, pu server handler reset reader index
+ in.readBytes(AytPreface.readableBytes());
+ in.markReaderIndex();
+ return Result.RECOGNIZED;
+ }
+ return Result.NEED_MORE_DATA;
+ }
+
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
new file mode 100644
index 0000000..cd4d62d
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
@@ -0,0 +1 @@
+qos=org.apache.dubbo.qos.pu.QosWireProtocol
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
index 1a45665..05f1edd 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
@@ -17,12 +17,12 @@
package org.apache.dubbo.qos.protocol;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.qos.command.BaseCommand;
import org.apache.dubbo.qos.server.Server;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.model.FrameworkModel;
-
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -44,6 +44,12 @@
private Invoker invoker = mock(Invoker.class);
private Protocol protocol = mock(Protocol.class);
private QosProtocolWrapper wrapper;
+
+ private URL triUrl = Mockito.mock(URL.class);
+ private Invoker triInvoker = mock(Invoker.class);
+ private Protocol triProtocol = mock(Protocol.class);
+ private QosProtocolWrapper triWrapper;
+
private Server server;
@BeforeEach
@@ -51,12 +57,25 @@
when(url.getParameter(QOS_ENABLE, true)).thenReturn(true);
when(url.getParameter(QOS_HOST)).thenReturn("localhost");
when(url.getParameter(QOS_PORT, 22222)).thenReturn(12345);
- when(url.getParameter(ACCEPT_FOREIGN_IP, true)).thenReturn(false);
- when(invoker.getUrl()).thenReturn(url);
+ when(url.getParameter(ACCEPT_FOREIGN_IP, "false")).thenReturn("false");
when(url.getProtocol()).thenReturn(REGISTRY_PROTOCOL);
- server = FrameworkModel.defaultModel().getBeanFactory().getBean(Server.class);
+ when(invoker.getUrl()).thenReturn(url);
+
wrapper = new QosProtocolWrapper(protocol);
wrapper.setFrameworkModel(FrameworkModel.defaultModel());
+
+ // url2 use tri protocol and qos.accept.foreign.ip=true
+ when(triUrl.getParameter(QOS_ENABLE, true)).thenReturn(true);
+ when(triUrl.getParameter(QOS_HOST)).thenReturn("localhost");
+ when(triUrl.getParameter(QOS_PORT, 22222)).thenReturn(12345);
+ when(triUrl.getParameter(ACCEPT_FOREIGN_IP, "false")).thenReturn("true");
+ when(triUrl.getProtocol()).thenReturn(CommonConstants.TRIPLE);
+ when(triInvoker.getUrl()).thenReturn(triUrl);
+
+ triWrapper = new QosProtocolWrapper(triProtocol);
+ triWrapper.setFrameworkModel(FrameworkModel.defaultModel());
+
+ server = FrameworkModel.defaultModel().getBeanFactory().getBean(Server.class);
}
@AfterEach
@@ -86,4 +105,23 @@
assertThat(server.isAcceptForeignIp(), is(false));
verify(protocol).refer(BaseCommand.class, url);
}
+
+ @Test
+ public void testMultiProtocol() throws Exception {
+ //tri protocol start first, acceptForeignIp = true
+ triWrapper.export(triInvoker);
+ assertThat(server.isStarted(), is(true));
+ assertThat(server.getHost(), is("localhost"));
+ assertThat(server.getPort(), is(12345));
+ assertThat(server.isAcceptForeignIp(), is(true));
+ verify(triProtocol).export(triInvoker);
+
+ //next registry protocol server still acceptForeignIp=true even though wrapper invoker url set false
+ wrapper.export(invoker);
+ assertThat(server.isStarted(), is(true));
+ assertThat(server.getHost(), is("localhost"));
+ assertThat(server.getPort(), is(12345));
+ assertThat(server.isAcceptForeignIp(), is(true));
+ verify(protocol).export(invoker);
+ }
}
diff --git a/dubbo-plugin/dubbo-reactive/pom.xml b/dubbo-plugin/dubbo-reactive/pom.xml
new file mode 100644
index 0000000..9ac35a6
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/pom.xml
@@ -0,0 +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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-plugin</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>dubbo-reactive</artifactId>
+ <packaging>jar</packaging>
+
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-triple</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.reactivestreams</groupId>
+ <artifactId>reactive-streams</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorPublisher.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorPublisher.java
new file mode 100644
index 0000000..0328c59
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorPublisher.java
@@ -0,0 +1,169 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * The middle layer between {@link org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver} and Reactive API. <p>
+ * 1. passing the data received by CallStreamObserver to Reactive consumer <br>
+ * 2. passing the request of Reactive API to CallStreamObserver
+ */
+public abstract class AbstractTripleReactorPublisher<T> extends CancelableStreamObserver<T> implements Publisher<T>, Subscription {
+
+ private boolean canRequest;
+
+ private long requested;
+
+ // weather publisher has been subscribed
+ private final AtomicBoolean SUBSCRIBED = new AtomicBoolean();
+
+ private volatile Subscriber<? super T> downstream;
+
+ protected volatile CallStreamObserver<?> subscription;
+
+ private final AtomicBoolean HAS_SUBSCRIPTION = new AtomicBoolean();
+
+ // cancel status
+ private volatile boolean isCancelled;
+
+ // complete status
+ private volatile boolean isDone;
+
+ // to help bind TripleSubscriber
+ private volatile Consumer<CallStreamObserver<?>> onSubscribe;
+
+ private volatile Runnable shutdownHook;
+
+ private final AtomicBoolean CALLED_SHUT_DOWN_HOOK = new AtomicBoolean();
+
+ public AbstractTripleReactorPublisher() {
+ }
+
+ public AbstractTripleReactorPublisher(Consumer<CallStreamObserver<?>> onSubscribe, Runnable shutdownHook) {
+ this.onSubscribe = onSubscribe;
+ this.shutdownHook = shutdownHook;
+ }
+
+ protected void onSubscribe(final CallStreamObserver<?> subscription) {
+ if (subscription != null && this.subscription == null && HAS_SUBSCRIPTION.compareAndSet(false, true)) {
+ this.subscription = subscription;
+ subscription.disableAutoFlowControl();
+ if (onSubscribe != null) {
+ onSubscribe.accept(subscription);
+ }
+ return;
+ }
+
+ throw new IllegalStateException(getClass().getSimpleName() + " supports only a single subscription");
+ }
+
+ @Override
+ public void onNext(T data) {
+ if (isDone || isCancelled) {
+ return;
+ }
+ downstream.onNext(data);
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ if (isDone || isCancelled) {
+ return;
+ }
+ isDone = true;
+ downstream.onError(throwable);
+ doPostShutdown();
+ }
+
+ @Override
+ public void onCompleted() {
+ if (isDone || isCancelled) {
+ return;
+ }
+ isDone = true;
+ downstream.onComplete();
+ doPostShutdown();
+ }
+
+ private void doPostShutdown() {
+ Runnable r = shutdownHook;
+ // CAS to confirm shutdownHook will be run only once.
+ if (r != null && CALLED_SHUT_DOWN_HOOK.compareAndSet(false, true)) {
+ shutdownHook = null;
+ r.run();
+ }
+ }
+
+ @Override
+ public void subscribe(Subscriber<? super T> subscriber) {
+ if (subscriber == null) {
+ throw new NullPointerException();
+ }
+
+ if (SUBSCRIBED.compareAndSet(false, true)) {
+ subscriber.onSubscribe(this);
+ this.downstream = subscriber;
+ if (isCancelled) {
+ this.downstream = null;
+ }
+ }
+ }
+
+ @Override
+ public void request(long l) {
+ synchronized (this) {
+ if (SUBSCRIBED.get() && canRequest) {
+ subscription.request(l >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) l);
+ } else {
+ requested += l;
+ }
+ }
+ }
+
+ @Override
+ public void startRequest() {
+ synchronized (this) {
+ if (!canRequest) {
+ canRequest = true;
+ long count = requested;
+ subscription.request(count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) count);
+ }
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (isCancelled) {
+ return;
+ }
+ isCancelled = true;
+ doPostShutdown();
+ }
+
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorSubscriber.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorSubscriber.java
new file mode 100644
index 0000000..96fa648
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/AbstractTripleReactorSubscriber.java
@@ -0,0 +1,106 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import reactor.core.CoreSubscriber;
+import reactor.util.annotation.NonNull;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The middle layer between {@link org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver} and Reactive API. <br>
+ * Passing the data from Reactive producer to CallStreamObserver.
+ */
+public abstract class AbstractTripleReactorSubscriber<T> implements Subscriber<T>, CoreSubscriber<T> {
+
+ private volatile boolean isCancelled;
+
+ protected volatile CallStreamObserver<T> downstream;
+
+ private final AtomicBoolean SUBSCRIBED = new AtomicBoolean();
+
+ private volatile Subscription subscription;
+
+ private final AtomicBoolean HAS_SUBSCRIBED = new AtomicBoolean();
+
+ // complete status
+ private volatile boolean isDone;
+
+ /**
+ * Binding the downstream, and call subscription#request(1).
+ *
+ * @param downstream downstream
+ */
+ public void subscribe(final CallStreamObserver<T> downstream) {
+ if (downstream == null) {
+ throw new NullPointerException();
+ }
+ if (this.downstream == null && SUBSCRIBED.compareAndSet(false, true)) {
+ this.downstream = downstream;
+ subscription.request(1);
+ }
+ }
+
+ @Override
+ public void onSubscribe(@NonNull final Subscription subscription) {
+ if (this.subscription == null && HAS_SUBSCRIBED.compareAndSet(false, true)) {
+ this.subscription = subscription;
+ return;
+ }
+ // onSubscribe cannot be called repeatedly
+ subscription.cancel();
+ }
+
+ @Override
+ public void onNext(T t) {
+ if (!isDone && !isCanceled()) {
+ downstream.onNext(t);
+ subscription.request(1);
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ if (!isCanceled()) {
+ isDone = true;
+ downstream.onError(throwable);
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ if (!isCanceled()) {
+ isDone = true;
+ downstream.onCompleted();
+ }
+ }
+
+ public void cancel() {
+ if (!isCancelled && subscription != null) {
+ isCancelled = true;
+ subscription.cancel();
+ }
+ }
+
+ public boolean isCanceled() {
+ return isCancelled;
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorPublisher.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorPublisher.java
new file mode 100644
index 0000000..1233dd8
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorPublisher.java
@@ -0,0 +1,44 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter;
+
+import java.util.function.Consumer;
+
+/**
+ * Used in OneToMany & ManyToOne & ManyToMany in client. <br>
+ * It is a Publisher for user subscriber to subscribe. <br>
+ * It is a StreamObserver for responseStream. <br>
+ * It is a Subscription for user subscriber to request and pass request to requestStream.
+ */
+public class ClientTripleReactorPublisher<T> extends AbstractTripleReactorPublisher<T> {
+
+ public ClientTripleReactorPublisher() {
+ }
+
+ public ClientTripleReactorPublisher(Consumer<CallStreamObserver<?>> onSubscribe, Runnable shutdownHook) {
+ super(onSubscribe, shutdownHook);
+ }
+
+ @Override
+ public void beforeStart(ClientCallToObserverAdapter<T> clientCallToObserverAdapter) {
+ super.onSubscribe(clientCallToObserverAdapter);
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorSubscriber.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorSubscriber.java
new file mode 100644
index 0000000..7d59244
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ClientTripleReactorSubscriber.java
@@ -0,0 +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.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter;
+
+/**
+ * The subscriber in client to subscribe user publisher and is subscribed by ClientStreamObserver.
+ */
+public class ClientTripleReactorSubscriber<T> extends AbstractTripleReactorSubscriber<T> {
+
+ @Override
+ public void cancel() {
+ if (!isCanceled()) {
+ super.cancel();
+ ((ClientCallToObserverAdapter<T>) downstream).cancel(new Exception("Cancelled"));
+ }
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorPublisher.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorPublisher.java
new file mode 100644
index 0000000..8f5382e
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorPublisher.java
@@ -0,0 +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.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+
+/**
+ * Used in ManyToOne and ManyToMany in server. <br>
+ * It is a Publisher for user subscriber to subscribe. <br>
+ * It is a StreamObserver for requestStream. <br>
+ * It is a Subscription for user subscriber to request and pass request to responseStream.
+ */
+public class ServerTripleReactorPublisher<T> extends AbstractTripleReactorPublisher<T> {
+
+ public ServerTripleReactorPublisher(CallStreamObserver<?> callStreamObserver) {
+ super.onSubscribe(callStreamObserver);
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorSubscriber.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorSubscriber.java
new file mode 100644
index 0000000..1e8ef5e
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/ServerTripleReactorSubscriber.java
@@ -0,0 +1,44 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.rpc.CancellationContext;
+import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+
+/**
+ * The Subscriber in server to passing the data produced by user publisher to responseStream.
+ */
+public class ServerTripleReactorSubscriber<T> extends AbstractTripleReactorSubscriber<T> {
+
+ @Override
+ public void subscribe(CallStreamObserver<T> downstream) {
+ super.subscribe(downstream);
+ if (downstream instanceof CancelableStreamObserver) {
+ CancelableStreamObserver<?> observer = (CancelableStreamObserver<?>) downstream;
+ final CancellationContext context;
+ if (observer.getCancellationContext() == null) {
+ context = new CancellationContext();
+ observer.setCancellationContext(context);
+ } else {
+ context = observer.getCancellationContext();
+ }
+ context.addListener(ctx -> super.cancel());
+ }
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorClientCalls.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorClientCalls.java
new file mode 100644
index 0000000..c23e987
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorClientCalls.java
@@ -0,0 +1,143 @@
+/*
+ * 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.reactive.calls;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.ClientTripleReactorPublisher;
+import org.apache.dubbo.reactive.ClientTripleReactorSubscriber;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.model.StubMethodDescriptor;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.stub.StubInvocationUtil;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * A collection of methods to convert client-side Reactor calls to stream calls.
+ */
+public final class ReactorClientCalls {
+
+ private ReactorClientCalls() {
+ }
+
+ /**
+ * Implements a unary -> unary call as Mono -> Mono
+ *
+ * @param invoker invoker
+ * @param monoRequest the mono with request
+ * @param methodDescriptor the method descriptor
+ * @return the mono with response
+ */
+ public static <TRequest, TResponse, TInvoker> Mono<TResponse> oneToOne(Invoker<TInvoker> invoker,
+ Mono<TRequest> monoRequest,
+ StubMethodDescriptor methodDescriptor) {
+ try {
+ return Mono.create(emitter -> monoRequest.subscribe(
+ request -> StubInvocationUtil.unaryCall(invoker, methodDescriptor, request, new StreamObserver<TResponse>() {
+ @Override
+ public void onNext(TResponse tResponse) {
+ emitter.success(tResponse);
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ emitter.error(throwable);
+ }
+
+ @Override
+ public void onCompleted() {
+ // Do nothing
+ }
+ }),
+ emitter::error
+ ));
+ } catch (Throwable throwable) {
+ return Mono.error(throwable);
+ }
+ }
+
+ /**
+ * Implements a unary -> stream call as Mono -> Flux
+ *
+ * @param invoker invoker
+ * @param monoRequest the mono with request
+ * @param methodDescriptor the method descriptor
+ * @return the flux with response
+ */
+ public static <TRequest, TResponse, TInvoker> Flux<TResponse> oneToMany(Invoker<TInvoker> invoker,
+ Mono<TRequest> monoRequest,
+ StubMethodDescriptor methodDescriptor) {
+ try {
+ return monoRequest
+ .flatMapMany(request -> {
+ ClientTripleReactorPublisher<TResponse> clientPublisher = new ClientTripleReactorPublisher<>();
+ StubInvocationUtil.serverStreamCall(invoker, methodDescriptor, request, clientPublisher);
+ return clientPublisher;
+ });
+ } catch (Throwable throwable) {
+ return Flux.error(throwable);
+ }
+ }
+
+ /**
+ * Implements a stream -> unary call as Flux -> Mono
+ *
+ * @param invoker invoker
+ * @param requestFlux the flux with request
+ * @param methodDescriptor the method descriptor
+ * @return the mono with response
+ */
+ public static <TRequest, TResponse, TInvoker> Mono<TResponse> manyToOne(Invoker<TInvoker> invoker,
+ Flux<TRequest> requestFlux,
+ StubMethodDescriptor methodDescriptor) {
+ try {
+ ClientTripleReactorSubscriber<TRequest> clientSubscriber = requestFlux.subscribeWith(new ClientTripleReactorSubscriber<>());
+ ClientTripleReactorPublisher<TResponse> clientPublisher = new ClientTripleReactorPublisher<>(
+ s -> clientSubscriber.subscribe((CallStreamObserver<TRequest>) s),
+ clientSubscriber::cancel);
+ return Mono.from(clientPublisher).doOnSubscribe(dummy ->
+ StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher));
+ } catch (Throwable throwable) {
+ return Mono.error(throwable);
+ }
+ }
+
+ /**
+ * Implements a stream -> stream call as Flux -> Flux
+ *
+ * @param invoker invoker
+ * @param requestFlux the flux with request
+ * @param methodDescriptor the method descriptor
+ * @return the flux with response
+ */
+ public static <TRequest, TResponse, TInvoker> Flux<TResponse> manyToMany(Invoker<TInvoker> invoker,
+ Flux<TRequest> requestFlux,
+ StubMethodDescriptor methodDescriptor) {
+ try {
+ ClientTripleReactorSubscriber<TRequest> clientSubscriber = requestFlux.subscribeWith(new ClientTripleReactorSubscriber<>());
+ ClientTripleReactorPublisher<TResponse> clientPublisher = new ClientTripleReactorPublisher<>(
+ s -> clientSubscriber.subscribe((CallStreamObserver<TRequest>) s),
+ clientSubscriber::cancel);
+ return Flux.from(clientPublisher).doOnSubscribe(dummy ->
+ StubInvocationUtil.biOrClientStreamCall(invoker, methodDescriptor, clientPublisher));
+ } catch (Throwable throwable) {
+ return Flux.error(throwable);
+ }
+ }
+
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorServerCalls.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorServerCalls.java
new file mode 100644
index 0000000..33c54d1
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/calls/ReactorServerCalls.java
@@ -0,0 +1,136 @@
+/*
+ * 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.reactive.calls;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.ServerTripleReactorPublisher;
+import org.apache.dubbo.reactive.ServerTripleReactorSubscriber;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * A collection of methods to convert server-side stream calls to Reactor calls.
+ */
+public final class ReactorServerCalls {
+
+ private ReactorServerCalls() {
+ }
+
+ /**
+ * Implements a unary -> unary call as Mono -> Mono
+ *
+ * @param request request
+ * @param responseObserver response StreamObserver
+ * @param func service implementation
+ */
+ public static <T, R> void oneToOne(T request,
+ StreamObserver<R> responseObserver,
+ Function<Mono<T>, Mono<R>> func) {
+ func.apply(Mono.just(request)).subscribe(res -> {
+ CompletableFuture.completedFuture(res)
+ .whenComplete((r, t) -> {
+ if (t != null) {
+ responseObserver.onError(t);
+ } else {
+ responseObserver.onNext(r);
+ responseObserver.onCompleted();
+ }
+ });
+ });
+ }
+
+ /**
+ * Implements a unary -> stream call as Mono -> Flux
+ *
+ * @param request request
+ * @param responseObserver response StreamObserver
+ * @param func service implementation
+ */
+ public static <T, R> void oneToMany(T request,
+ StreamObserver<R> responseObserver,
+ Function<Mono<T>, Flux<R>> func) {
+ try {
+ Flux<R> response = func.apply(Mono.just(request));
+ ServerTripleReactorSubscriber<R> subscriber = response.subscribeWith(new ServerTripleReactorSubscriber<>());
+ subscriber.subscribe((ServerCallToObserverAdapter<R>) responseObserver);
+ } catch (Throwable throwable) {
+ responseObserver.onError(throwable);
+ }
+ }
+
+ /**
+ * Implements a stream -> unary call as Flux -> Mono
+ *
+ * @param responseObserver response StreamObserver
+ * @param func service implementation
+ * @return request StreamObserver
+ */
+ public static <T, R> StreamObserver<T> manyToOne(StreamObserver<R> responseObserver,
+ Function<Flux<T>, Mono<R>> func) {
+ ServerTripleReactorPublisher<T> serverPublisher = new ServerTripleReactorPublisher<T>((CallStreamObserver<R>) responseObserver);
+ try {
+ Mono<R> responseMono = func.apply(Flux.from(serverPublisher));
+ responseMono.subscribe(value -> {
+ // Don't try to respond if the server has already canceled the request
+ if (!serverPublisher.isCancelled()) {
+ responseObserver.onNext(value);
+ }
+ },
+ throwable -> {
+ // Don't try to respond if the server has already canceled the request
+ if (!serverPublisher.isCancelled()) {
+ responseObserver.onError(throwable);
+ }
+ },
+ responseObserver::onCompleted
+ );
+ serverPublisher.startRequest();
+ } catch (Throwable throwable) {
+ responseObserver.onError(throwable);
+ }
+ return serverPublisher;
+ }
+
+ /**
+ * Implements a stream -> stream call as Flux -> Flux
+ *
+ * @param responseObserver response StreamObserver
+ * @param func service implementation
+ * @return request StreamObserver
+ */
+ public static <T, R> StreamObserver<T> manyToMany(StreamObserver<R> responseObserver,
+ Function<Flux<T>, Flux<R>> func) {
+ // responseObserver is also a subscription of publisher, we can use it to request more data
+ ServerTripleReactorPublisher<T> serverPublisher = new ServerTripleReactorPublisher<T>((CallStreamObserver<R>) responseObserver);
+ try {
+ Flux<R> responseFlux = func.apply(Flux.from(serverPublisher));
+ ServerTripleReactorSubscriber<R> serverSubscriber = responseFlux.subscribeWith(new ServerTripleReactorSubscriber<>());
+ serverSubscriber.subscribe((CallStreamObserver<R>) responseObserver);
+ serverPublisher.startRequest();
+ } catch (Throwable throwable) {
+ responseObserver.onError(throwable);
+ }
+
+ return serverPublisher;
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToManyMethodHandler.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToManyMethodHandler.java
new file mode 100644
index 0000000..72f9b50
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToManyMethodHandler.java
@@ -0,0 +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.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.calls.ReactorServerCalls;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.stub.StubMethodHandler;
+import reactor.core.publisher.Flux;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * The handler of ManyToMany() method for stub invocation.
+ */
+public class ManyToManyMethodHandler<T, R> implements StubMethodHandler<T, R> {
+
+ private final Function<Flux<T>, Flux<R>> func;
+
+ public ManyToManyMethodHandler(Function<Flux<T>, Flux<R>> func) {
+ this.func = func;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public CompletableFuture<StreamObserver<T>> invoke(Object[] arguments) {
+ CallStreamObserver<R> responseObserver = (CallStreamObserver<R>) arguments[0];
+ StreamObserver<T> requestObserver = ReactorServerCalls.manyToMany(responseObserver, func);
+ return CompletableFuture.completedFuture(requestObserver);
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToOneMethodHandler.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToOneMethodHandler.java
new file mode 100644
index 0000000..d74a9ab
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/ManyToOneMethodHandler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.calls.ReactorServerCalls;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.stub.StubMethodHandler;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * The handler of ManyToOne() method for stub invocation.
+ */
+public class ManyToOneMethodHandler<T, R> implements StubMethodHandler<T, R> {
+
+ private final Function<Flux<T>, Mono<R>> func;
+
+ public ManyToOneMethodHandler(Function<Flux<T>, Mono<R>> func) {
+ this.func = func;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public CompletableFuture<StreamObserver<T>> invoke(Object[] arguments) {
+ CallStreamObserver<R> responseObserver = (CallStreamObserver<R>) arguments[0];
+ StreamObserver<T> requestObserver = ReactorServerCalls.manyToOne(responseObserver, func);
+ return CompletableFuture.completedFuture(requestObserver);
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToManyMethodHandler.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToManyMethodHandler.java
new file mode 100644
index 0000000..a672c5b
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToManyMethodHandler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.calls.ReactorServerCalls;
+import org.apache.dubbo.rpc.stub.StubMethodHandler;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * The handler of OneToMany() method for stub invocation.
+ */
+public class OneToManyMethodHandler<T, R> implements StubMethodHandler<T, R> {
+
+ private final Function<Mono<T>, Flux<R>> func;
+
+ public OneToManyMethodHandler(Function<Mono<T>, Flux<R>> func) {
+ this.func = func;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public CompletableFuture<?> invoke(Object[] arguments) {
+ T request = (T) arguments[0];
+ StreamObserver<R> responseObserver = (StreamObserver<R>) arguments[1];
+ ReactorServerCalls.oneToMany(request, responseObserver, func);
+ return CompletableFuture.completedFuture(null);
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToOneMethodHandler.java b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToOneMethodHandler.java
new file mode 100644
index 0000000..f20a3ec
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/main/java/org/apache/dubbo/reactive/handler/OneToOneMethodHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.calls.ReactorServerCalls;
+import org.apache.dubbo.rpc.stub.FutureToObserverAdaptor;
+import org.apache.dubbo.rpc.stub.StubMethodHandler;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+
+/**
+ * The handler of OneToOne() method for stub invocation.
+ */
+public class OneToOneMethodHandler<T, R> implements StubMethodHandler<T, R> {
+
+ private final Function<Mono<T>, Mono<R>> func;
+
+ public OneToOneMethodHandler(Function<Mono<T>, Mono<R>> func) {
+ this.func = func;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public CompletableFuture<R> invoke(Object[] arguments) {
+ T request = (T) arguments[0];
+ CompletableFuture<R> future = new CompletableFuture<>();
+ StreamObserver<R> responseObserver = new FutureToObserverAdaptor<>(future);
+ ReactorServerCalls.oneToOne(request, responseObserver, func);
+ return future;
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java
new file mode 100644
index 0000000..bf80ca9
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.handler.ManyToManyMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+/**
+ * Unit test for ManyToManyMethodHandler
+ */
+public final class ManyToManyMethodHandlerTest {
+
+ @Test
+ public void testInvoke() throws ExecutionException, InterruptedException {
+ AtomicInteger nextCounter = new AtomicInteger();
+ AtomicInteger completeCounter = new AtomicInteger();
+ AtomicInteger errorCounter = new AtomicInteger();
+ ServerCallToObserverAdapter<String> responseObserver = Mockito.mock(ServerCallToObserverAdapter.class);
+ doAnswer(o -> nextCounter.incrementAndGet())
+ .when(responseObserver).onNext(anyString());
+ doAnswer(o -> completeCounter.incrementAndGet())
+ .when(responseObserver).onCompleted();
+ doAnswer(o -> errorCounter.incrementAndGet())
+ .when(responseObserver).onError(any(Throwable.class));
+ ManyToManyMethodHandler<String, String> handler = new ManyToManyMethodHandler<>(requestFlux ->
+ requestFlux.map(r -> r + "0"));
+ CompletableFuture<StreamObserver<String>> future = handler.invoke(new Object[]{responseObserver});
+ StreamObserver<String> requestObserver = future.get();
+ for (int i = 0; i < 10; i++) {
+ requestObserver.onNext(String.valueOf(i));
+ }
+ requestObserver.onCompleted();
+ Assertions.assertEquals(10, nextCounter.get());
+ Assertions.assertEquals(0, errorCounter.get());
+ Assertions.assertEquals(1, completeCounter.get());
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java
new file mode 100644
index 0000000..e3e3302
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.reactive.handler.ManyToOneMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+/**
+ * Unit test for ManyToOneMethodHandler
+ */
+public final class ManyToOneMethodHandlerTest {
+
+ @Test
+ public void testInvoker() throws ExecutionException, InterruptedException {
+ AtomicInteger nextCounter = new AtomicInteger();
+ AtomicInteger completeCounter = new AtomicInteger();
+ AtomicInteger errorCounter = new AtomicInteger();
+ ServerCallToObserverAdapter<String> responseObserver = Mockito.mock(ServerCallToObserverAdapter.class);
+ doAnswer(o -> nextCounter.incrementAndGet())
+ .when(responseObserver).onNext(anyString());
+ doAnswer(o -> completeCounter.incrementAndGet())
+ .when(responseObserver).onCompleted();
+ doAnswer(o -> errorCounter.incrementAndGet())
+ .when(responseObserver).onError(any(Throwable.class));
+ ManyToOneMethodHandler<String, String> handler = new ManyToOneMethodHandler<>(requestFlux ->
+ requestFlux.map(Integer::valueOf).reduce(Integer::sum).map(String::valueOf));
+ CompletableFuture<StreamObserver<String>> future = handler.invoke(new Object[]{responseObserver});
+ StreamObserver<String> requestObserver = future.get();
+ for (int i = 0; i < 10; i++) {
+ requestObserver.onNext(String.valueOf(i));
+ }
+ requestObserver.onCompleted();
+ Assertions.assertEquals(1, nextCounter.get());
+ Assertions.assertEquals(0, errorCounter.get());
+ Assertions.assertEquals(1, completeCounter.get());
+ }
+
+ @Test
+ public void testError() throws ExecutionException, InterruptedException {
+ AtomicInteger nextCounter = new AtomicInteger();
+ AtomicInteger completeCounter = new AtomicInteger();
+ AtomicInteger errorCounter = new AtomicInteger();
+ ServerCallToObserverAdapter<String> responseObserver = Mockito.mock(ServerCallToObserverAdapter.class);
+ doAnswer(o -> nextCounter.incrementAndGet())
+ .when(responseObserver).onNext(anyString());
+ doAnswer(o -> completeCounter.incrementAndGet())
+ .when(responseObserver).onCompleted();
+ doAnswer(o -> errorCounter.incrementAndGet())
+ .when(responseObserver).onError(any(Throwable.class));
+ ManyToOneMethodHandler<String, String> handler = new ManyToOneMethodHandler<>(requestFlux ->
+ requestFlux.map(Integer::valueOf).reduce(Integer::sum).map(String::valueOf));
+ CompletableFuture<StreamObserver<String>> future = handler.invoke(new Object[]{responseObserver});
+ StreamObserver<String> requestObserver = future.get();
+ for (int i = 0; i < 10; i++) {
+ if (i == 6) {
+ requestObserver.onError(new Throwable());
+ }
+ requestObserver.onNext(String.valueOf(i));
+ }
+ requestObserver.onCompleted();
+ Assertions.assertEquals(0, nextCounter.get());
+ Assertions.assertEquals(1, errorCounter.get());
+ Assertions.assertEquals(0, completeCounter.get());
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java
new file mode 100644
index 0000000..ac0c8d9
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.reactive.handler.OneToManyMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Flux;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+/**
+ * Unit test for OneToManyMethodHandler
+ */
+public final class OneToManyMethodHandlerTest {
+
+ @Test
+ public void testInvoke() {
+ String request = "1,2,3,4,5,6,7";
+ AtomicInteger nextCounter = new AtomicInteger();
+ AtomicInteger completeCounter = new AtomicInteger();
+ AtomicInteger errorCounter = new AtomicInteger();
+ ServerCallToObserverAdapter<String> responseObserver = Mockito.mock(ServerCallToObserverAdapter.class);
+ doAnswer(o -> nextCounter.incrementAndGet())
+ .when(responseObserver).onNext(anyString());
+ doAnswer(o -> completeCounter.incrementAndGet())
+ .when(responseObserver).onCompleted();
+ doAnswer(o -> errorCounter.incrementAndGet())
+ .when(responseObserver).onError(any(Throwable.class));
+ OneToManyMethodHandler<String, String> handler = new OneToManyMethodHandler<>(requestMono ->
+ requestMono.flatMapMany(r -> Flux.fromArray(r.split(","))));
+ CompletableFuture<?> future = handler.invoke(new Object[]{request, responseObserver});
+ Assertions.assertTrue(future.isDone());
+ Assertions.assertEquals(7, nextCounter.get());
+ Assertions.assertEquals(0, errorCounter.get());
+ Assertions.assertEquals(1, completeCounter.get());
+ }
+
+ @Test
+ public void testError() {
+ String request = "1,2,3,4,5,6,7";
+ AtomicInteger nextCounter = new AtomicInteger();
+ AtomicInteger completeCounter = new AtomicInteger();
+ AtomicInteger errorCounter = new AtomicInteger();
+ ServerCallToObserverAdapter<String> responseObserver = Mockito.mock(ServerCallToObserverAdapter.class);
+ doAnswer(o -> nextCounter.incrementAndGet())
+ .when(responseObserver).onNext(anyString());
+ doAnswer(o -> completeCounter.incrementAndGet())
+ .when(responseObserver).onCompleted();
+ doAnswer(o -> errorCounter.incrementAndGet())
+ .when(responseObserver).onError(any(Throwable.class));
+ OneToManyMethodHandler<String, String> handler = new OneToManyMethodHandler<>(requestMono ->
+ Flux.create(emitter -> {
+ for (int i = 0; i < 10; i++) {
+ if (i == 6) {
+ emitter.error(new Throwable());
+ } else {
+ emitter.next(String.valueOf(i));
+ }
+ }
+ }));
+ CompletableFuture<?> future = handler.invoke(new Object[]{request, responseObserver});
+ Assertions.assertTrue(future.isDone());
+ Assertions.assertEquals(6, nextCounter.get());
+ Assertions.assertEquals(1, errorCounter.get());
+ Assertions.assertEquals(0, completeCounter.get());
+ }
+}
diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToOneMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToOneMethodHandlerTest.java
new file mode 100644
index 0000000..6d6a2fc
--- /dev/null
+++ b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToOneMethodHandlerTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.reactive;
+
+import org.apache.dubbo.reactive.handler.OneToOneMethodHandler;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Unit test for OneToOneMethodHandler
+ */
+public final class OneToOneMethodHandlerTest {
+
+ @Test
+ public void testInvoke() throws ExecutionException, InterruptedException {
+ String request = "request";
+ OneToOneMethodHandler<String, String> handler = new OneToOneMethodHandler<>(requestMono ->
+ requestMono.map(r -> r + "Test"));
+ CompletableFuture<?> future = handler.invoke(new Object[]{request});
+ assertEquals("requestTest", future.get());
+ }
+}
diff --git a/dubbo-plugin/pom.xml b/dubbo-plugin/pom.xml
index ed77204..2df28c3 100644
--- a/dubbo-plugin/pom.xml
+++ b/dubbo-plugin/pom.xml
@@ -31,6 +31,7 @@
<modules>
<module>dubbo-qos</module>
<module>dubbo-auth</module>
+ <module>dubbo-reactive</module>
</modules>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
index 3762387..c9cdc67 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/ListenerRegistryWrapper.java
@@ -18,15 +18,16 @@
package org.apache.dubbo.registry;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.UrlUtils;
import java.util.List;
+import java.util.function.Consumer;
public class ListenerRegistryWrapper implements Registry {
- private static final Logger logger = LoggerFactory.getLogger(ListenerRegistryWrapper.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ListenerRegistryWrapper.class);
private final Registry registry;
private final List<RegistryServiceListener> listeners;
@@ -59,21 +60,8 @@
registry.register(url);
}
} finally {
- if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) {
- RuntimeException exception = null;
- for (RegistryServiceListener listener : listeners) {
- if (listener != null) {
- try {
- listener.onRegister(url, registry);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
+ if (!UrlUtils.isConsumer(url)) {
+ listenerEvent(serviceListener -> serviceListener.onRegister(url, registry));
}
}
}
@@ -85,21 +73,8 @@
registry.unregister(url);
}
} finally {
- if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) {
- RuntimeException exception = null;
- for (RegistryServiceListener listener : listeners) {
- if (listener != null) {
- try {
- listener.onUnregister(url, registry);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
+ if (!UrlUtils.isConsumer(url)) {
+ listenerEvent(serviceListener -> serviceListener.onUnregister(url, registry));
}
}
}
@@ -111,46 +86,18 @@
registry.subscribe(url, listener);
}
} finally {
- if (CollectionUtils.isNotEmpty(listeners)) {
- RuntimeException exception = null;
- for (RegistryServiceListener registryListener : listeners) {
- if (registryListener != null) {
- try {
- registryListener.onSubscribe(url, registry);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
- }
+ listenerEvent(serviceListener -> serviceListener.onSubscribe(url, registry));
}
}
+
@Override
public void unsubscribe(URL url, NotifyListener listener) {
try {
registry.unsubscribe(url, listener);
} finally {
- if (CollectionUtils.isNotEmpty(listeners)) {
- RuntimeException exception = null;
- for (RegistryServiceListener registryListener : listeners) {
- if (registryListener != null) {
- try {
- registryListener.onUnsubscribe(url, registry);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
- }
+ listenerEvent(serviceListener -> serviceListener.onUnsubscribe(url, registry));
+
}
}
@@ -167,4 +114,23 @@
public Registry getRegistry() {
return registry;
}
+
+ private void listenerEvent(Consumer<RegistryServiceListener> consumer) {
+ if (CollectionUtils.isNotEmpty(listeners)) {
+ RuntimeException exception = null;
+ for (RegistryServiceListener listener : listeners) {
+ if (listener != null) {
+ try {
+ consumer.accept(listener);
+ } catch (RuntimeException t) {
+ logger.error(t.getMessage(), t);
+ exception = t;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+ }
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryNotifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryNotifier.java
index 1714ae5..452f919 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryNotifier.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/RegistryNotifier.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
@@ -30,7 +30,7 @@
public abstract class RegistryNotifier {
- private static final Logger logger = LoggerFactory.getLogger(RegistryNotifier.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryNotifier.class);
private volatile long lastExecuteTime;
private volatile long lastEventTime;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java
index 0b3fea6..0ccaa13 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
@@ -51,7 +51,7 @@
* Each service discovery is bond to one application.
*/
public abstract class AbstractServiceDiscovery implements ServiceDiscovery {
- private final Logger logger = LoggerFactory.getLogger(AbstractServiceDiscovery.class);
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractServiceDiscovery.class);
private volatile boolean isDestroy;
protected final String serviceName;
@@ -67,31 +67,33 @@
protected ApplicationModel applicationModel;
public AbstractServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
- this(applicationModel.getApplicationName(), registryURL);
- this.applicationModel = applicationModel;
+ this(applicationModel, applicationModel.getApplicationName(), registryURL);
MetadataReportInstance metadataReportInstance = applicationModel.getBeanFactory().getBean(MetadataReportInstance.class);
metadataType = metadataReportInstance.getMetadataType();
this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY));
-// if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataReportInstance.getMetadataType())) {
-// this.metadataReport = metadataReportInstance.getMetadataReport(registryURL.getParameter(REGISTRY_CLUSTER_KEY));
-// } else {
-// this.metadataReport = metadataReportInstance.getNopMetadataReport();
-// }
}
public AbstractServiceDiscovery(String serviceName, URL registryURL) {
- this.applicationModel = ApplicationModel.defaultModel();
- this.registryURL = registryURL;
+ this(ApplicationModel.defaultModel(), serviceName, registryURL);
+ }
+
+ private AbstractServiceDiscovery(ApplicationModel applicationModel, String serviceName, URL registryURL) {
+ this.applicationModel = applicationModel;
this.serviceName = serviceName;
+ this.registryURL = registryURL;
this.metadataInfo = new MetadataInfo(serviceName);
boolean localCacheEnabled = registryURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true);
this.metaCacheManager = new MetaCacheManager(localCacheEnabled, getCacheNameSuffix(),
applicationModel.getFrameworkModel().getBeanFactory()
- .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor());
+ .getBean(FrameworkExecutorRepository.class).getCacheRefreshingScheduledExecutor());
}
+
@Override
public synchronized void register() throws RuntimeException {
+ if (isDestroy) {
+ return;
+ }
this.serviceInstance = createServiceInstance(this.metadataInfo);
if (!isValidInstance(this.serviceInstance)) {
logger.warn("No valid instance found, stop registering instance address to registry.");
@@ -135,6 +137,9 @@
@Override
public synchronized void unregister() throws RuntimeException {
+ if (isDestroy) {
+ return;
+ }
// fixme, this metadata info might still being shared by other instances
// unReportMetadata(this.metadataInfo);
if (!isValidInstance(this.serviceInstance)) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 074811b..38c8efd 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -30,6 +30,7 @@
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.ENDPOINTS;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
@@ -71,7 +72,7 @@
private transient Map<String, String> extendParams;
private transient List<Endpoint> endpoints;
private transient ApplicationModel applicationModel;
- private transient InstanceAddressURL instanceAddressURL = null;
+ private transient Map<String, InstanceAddressURL> instanceAddressURL = new ConcurrentHashMap<>();
public DefaultServiceInstance() {
}
@@ -245,6 +246,12 @@
return copyOfInstance;
}
+ public DefaultServiceInstance copyFrom(int port) {
+ DefaultServiceInstance copyOfInstance = new DefaultServiceInstance(this);
+ copyOfInstance.setPort(port);
+ return copyOfInstance;
+ }
+
@Override
public Map<String, String> getAllParams() {
if (extendParams == null) {
@@ -280,15 +287,13 @@
@Override
public void setServiceMetadata(MetadataInfo serviceMetadata) {
this.serviceMetadata = serviceMetadata;
- this.instanceAddressURL = null;
+ this.instanceAddressURL.clear();
}
@Override
public InstanceAddressURL toURL(String protocol) {
- if (instanceAddressURL == null) {
- instanceAddressURL = new InstanceAddressURL(this, serviceMetadata, protocol);
- }
- return instanceAddressURL;
+ return instanceAddressURL.computeIfAbsent(protocol,
+ key -> new InstanceAddressURL(this, serviceMetadata, protocol));
}
@Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index 0dcb0e6..d1d46e1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -22,7 +22,7 @@
//import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
//import org.apache.dubbo.common.config.configcenter.file.FileSystemDynamicConfiguration;
//import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
-//import org.apache.dubbo.common.logger.Logger;
+//import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
//import org.apache.dubbo.common.logger.LoggerFactory;
//import org.apache.dubbo.common.utils.StringUtils;
//
@@ -53,7 +53,7 @@
// */
//public class FileSystemServiceDiscovery extends AbstractServiceDiscovery {
//
-// private final Logger logger = LoggerFactory.getLogger(getClass());
+// private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
//
// private final Map<File, FileLock> fileLocksCache = new ConcurrentHashMap<>();
//
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index 8e8b2f9..225b6e6 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -17,6 +17,7 @@
package org.apache.dubbo.registry.client;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.url.component.URLAddress;
import org.apache.dubbo.common.url.component.URLParam;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -127,6 +128,26 @@
}
@Override
+ public URL setProtocol(String protocol) {
+ return new ServiceConfigURL(protocol, getUsername(), getPassword(), getHost(), getPort(), getPath(), getParameters(), attributes);
+ }
+
+ @Override
+ public URL setHost(String host) {
+ return new ServiceConfigURL(getProtocol(), getUsername(), getPassword(), host, getPort(), getPath(), getParameters(), attributes);
+ }
+
+ @Override
+ public URL setPort(int port) {
+ return new ServiceConfigURL(getProtocol(), getUsername(), getPassword(), getHost(), port, getPath(), getParameters(), attributes);
+ }
+
+ @Override
+ public URL setPath(String path) {
+ return new ServiceConfigURL(getProtocol(), getUsername(), getPassword(), getHost(), getPort(), path, getParameters(), attributes);
+ }
+
+ @Override
public String getAddress() {
return instance.getAddress();
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java
new file mode 100644
index 0000000..02c8c15
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java
@@ -0,0 +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.registry.client;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.InstanceMetadataChangedListener;
+import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.RevisionResolver;
+import org.apache.dubbo.registry.Constants;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
+import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+import org.apache.dubbo.rpc.service.Destroyable;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT;
+
+public class ReflectionBasedServiceDiscovery extends AbstractServiceDiscovery {
+
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+ /**
+ * Echo check if consumer is still work
+ * echo task may take a lot of time when consumer offline, create a new ScheduledThreadPool
+ */
+ private final ScheduledExecutorService echoCheckExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-Registry-EchoCheck-Consumer"));
+
+ // =================================== Provider side =================================== //
+ /**
+ * Local {@link ServiceInstance} Metadata's revision
+ */
+ private String lastMetadataRevision;
+
+ // =================================== Consumer side =================================== //
+
+ /**
+ * Local Cache of {@link ServiceInstance} Metadata
+ * <p>
+ * Key - {@link ServiceInstance} ID ( usually ip + port )
+ * Value - Json processed metadata string
+ */
+ private final ConcurrentHashMap<String, String> metadataMap = new ConcurrentHashMap<>();
+
+ /**
+ * Local Cache of {@link ServiceInstance}
+ * <p>
+ * Key - Service Name
+ * Value - List {@link ServiceInstance}
+ */
+ private final ConcurrentHashMap<String, List<ServiceInstance>> cachedServiceInstances = new ConcurrentHashMap<>();
+
+ private final MetadataServiceDelegation metadataService;
+
+ public ConcurrentMap<String, MetadataService> metadataServiceProxies = new ConcurrentHashMap<>();
+
+ /**
+ * Local Cache of Service's {@link ServiceInstance} list revision,
+ * used to check if {@link ServiceInstance} list has been updated
+ * <p>
+ * Key - ServiceName
+ * Value - a revision calculate from {@link List} of {@link ServiceInstance}
+ */
+ private final ConcurrentHashMap<String, String> serviceInstanceRevisionMap = new ConcurrentHashMap<>();
+
+ public ReflectionBasedServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+ super(applicationModel, registryURL);
+ long echoPollingCycle = registryURL.getParameter(Constants.ECHO_POLLING_CYCLE_KEY, Constants.DEFAULT_ECHO_POLLING_CYCLE);
+
+ this.metadataService = applicationModel.getBeanFactory().getOrRegisterBean(MetadataServiceDelegation.class);
+
+ // Echo check: test if consumer is offline, remove MetadataChangeListener,
+ // reduce the probability of failure when metadata update
+ echoCheckExecutor.scheduleAtFixedRate(() -> {
+ Map<String, InstanceMetadataChangedListener> listenerMap = metadataService.getInstanceMetadataChangedListenerMap();
+ Iterator<Map.Entry<String, InstanceMetadataChangedListener>> iterator = listenerMap.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry<String, InstanceMetadataChangedListener> entry = iterator.next();
+ try {
+ entry.getValue().echo(CommonConstants.DUBBO);
+ } catch (RpcException e) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send echo message to consumer error. Possible cause: consumer is offline.");
+ }
+ iterator.remove();
+ }
+ }
+ }, echoPollingCycle, echoPollingCycle, TimeUnit.MILLISECONDS);
+ }
+
+ public void doInitialize(URL registryURL) {
+
+ }
+
+ @Override
+ public void doDestroy() throws Exception {
+ metadataMap.clear();
+ serviceInstanceRevisionMap.clear();
+ echoCheckExecutor.shutdown();
+ }
+
+ private void updateInstanceMetadata(ServiceInstance serviceInstance) {
+ String metadataString = JSONObject.toJSONString(serviceInstance.getMetadata());
+ String metadataRevision = RevisionResolver.calRevision(metadataString);
+
+ // check if metadata updated
+ if (!metadataRevision.equalsIgnoreCase(lastMetadataRevision)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Update Service Instance Metadata of DNS registry. Newer metadata: " + metadataString);
+ }
+
+ lastMetadataRevision = metadataRevision;
+
+ // save the newest metadata to local
+ metadataService.exportInstanceMetadata(metadataString);
+
+ // notify to consumer
+ Map<String, InstanceMetadataChangedListener> listenerMap = metadataService.getInstanceMetadataChangedListenerMap();
+ Iterator<Map.Entry<String, InstanceMetadataChangedListener>> iterator = listenerMap.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry<String, InstanceMetadataChangedListener> entry = iterator.next();
+ try {
+ entry.getValue().onEvent(metadataString);
+ } catch (RpcException e) {
+ // 1-7 - Failed to notify registry event.
+ // The updating of metadata to consumer is a type of registry event.
+
+ logger.warn(REGISTRY_FAILED_NOTIFY_EVENT, "consumer is offline", "",
+ "Notify to consumer error, removing listener.");
+
+ // remove listener if consumer is offline
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+ updateInstanceMetadata(serviceInstance);
+ }
+
+ @Override
+ public void doUpdate(ServiceInstance serviceInstance) throws RuntimeException {
+ updateInstanceMetadata(serviceInstance);
+ }
+
+ @Override
+ public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
+ // notify empty message to consumer
+ metadataService.exportInstanceMetadata("");
+ metadataService.getInstanceMetadataChangedListenerMap().forEach((consumerId, listener) -> listener.onEvent(""));
+ metadataService.getInstanceMetadataChangedListenerMap().clear();
+ }
+
+ @SuppressWarnings("unchecked")
+ public final void fillServiceInstance(DefaultServiceInstance serviceInstance) {
+ String hostId = serviceInstance.getAddress();
+ if (metadataMap.containsKey(hostId)) {
+ // Use cached metadata.
+ // Metadata will be updated by provider callback
+
+ String metadataString = metadataMap.get(hostId);
+ serviceInstance.setMetadata(JSONObject.parseObject(metadataString, Map.class));
+ } else {
+ // refer from MetadataUtils, this proxy is different from the one used to refer exportedURL
+ MetadataService metadataService = getMetadataServiceProxy(serviceInstance);
+
+ String consumerId = ScopeModelUtil.getApplicationModel(registryURL.getScopeModel()).getApplicationName() + NetUtils.getLocalHost();
+ String metadata = metadataService.getAndListenInstanceMetadata(
+ consumerId, metadataString -> {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Receive callback: " + metadataString + serviceInstance);
+ }
+ if (StringUtils.isEmpty(metadataString)) {
+ // provider is shutdown
+ metadataMap.remove(hostId);
+ } else {
+ metadataMap.put(hostId, metadataString);
+ }
+ });
+ metadataMap.put(hostId, metadata);
+ serviceInstance.setMetadata(JSONObject.parseObject(metadata, Map.class));
+ }
+ }
+
+ public final void notifyListener(String serviceName, ServiceInstancesChangedListener listener, List<ServiceInstance> instances) {
+ String serviceInstanceRevision = RevisionResolver.calRevision(JSONObject.toJSONString(instances));
+ boolean changed = !serviceInstanceRevision.equalsIgnoreCase(
+ serviceInstanceRevisionMap.put(serviceName, serviceInstanceRevision));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Service changed event received (possibly because of DNS polling). " +
+ "Service Instance changed: " + changed + " Service Name: " + serviceName);
+ }
+
+ if (changed) {
+ List<ServiceInstance> oldServiceInstances = cachedServiceInstances.getOrDefault(serviceName, new LinkedList<>());
+
+ // remove expired invoker
+ Set<ServiceInstance> allServiceInstances = new HashSet<>(oldServiceInstances.size() + instances.size());
+ allServiceInstances.addAll(oldServiceInstances);
+ allServiceInstances.addAll(instances);
+
+ oldServiceInstances.forEach(allServiceInstances::remove);
+
+ allServiceInstances.forEach(this::destroyMetadataServiceProxy);
+
+ cachedServiceInstances.put(serviceName, instances);
+ listener.onEvent(new ServiceInstancesChangedEvent(serviceName, instances));
+ }
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+ return Collections.emptyList();
+ }
+
+ private String computeKey(ServiceInstance serviceInstance) {
+ return serviceInstance.getServiceName() + "##" + serviceInstance.getAddress() + "##" +
+ ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance);
+ }
+
+ private synchronized MetadataService getMetadataServiceProxy(ServiceInstance instance) {
+ return metadataServiceProxies.computeIfAbsent(computeKey(instance), k -> MetadataUtils.referProxy(instance).getProxy());
+ }
+
+ private synchronized void destroyMetadataServiceProxy(ServiceInstance instance) {
+ String key = computeKey(instance);
+ if (metadataServiceProxies.containsKey(key)) {
+ Object metadataServiceProxy = metadataServiceProxies.remove(key);
+ if (metadataServiceProxy instanceof Destroyable) {
+ ((Destroyable) metadataServiceProxy).$destroy();
+ }
+ }
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public final ConcurrentHashMap<String, List<ServiceInstance>> getCachedServiceInstances() {
+ return cachedServiceInstances;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryFactory.java
index 2332f04..2168095 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryFactory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryFactory.java
@@ -35,7 +35,6 @@
* Get the instance of {@link ServiceDiscovery}
*
* @param registryURL the {@link URL} to connect the registry
- * @param model, the application model context
* @return non-null
*/
ServiceDiscovery getServiceDiscovery(URL registryURL);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 30eb983..098f645 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.metadata.AbstractServiceNameMapping;
@@ -66,7 +66,7 @@
*/
public class ServiceDiscoveryRegistry extends FailbackRegistry {
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
private final ServiceDiscovery serviceDiscovery;
@@ -299,8 +299,8 @@
protected void subscribeURLs(URL url, NotifyListener listener, Set<String> serviceNames) {
serviceNames = toTreeSet(serviceNames);
String serviceNamesKey = toStringKeys(serviceNames);
- String protocolServiceKey = url.getProtocolServiceKey();
- logger.info(String.format("Trying to subscribe from apps %s for service key %s, ", serviceNamesKey, protocolServiceKey));
+ String serviceKey = url.getServiceKey();
+ logger.info(String.format("Trying to subscribe from apps %s for service key %s, ", serviceNamesKey, serviceKey));
// register ServiceInstancesChangedListener
Lock appSubscriptionLock = getAppSubscription(serviceNamesKey);
@@ -322,7 +322,7 @@
if (!serviceInstancesChangedListener.isDestroyed()) {
serviceInstancesChangedListener.setUrl(url);
listener.addServiceListener(serviceInstancesChangedListener);
- serviceInstancesChangedListener.addListenerAndNotify(protocolServiceKey, listener);
+ serviceInstancesChangedListener.addListenerAndNotify(url, listener);
serviceDiscovery.addServiceInstancesChangedListener(serviceInstancesChangedListener);
} else {
logger.info(String.format("Listener of %s has been destroyed by another thread.", serviceNamesKey));
@@ -348,7 +348,7 @@
}
private class DefaultMappingListener implements MappingListener {
- private final Logger logger = LoggerFactory.getLogger(DefaultMappingListener.class);
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultMappingListener.class);
private final URL url;
private Set<String> oldApps;
private NotifyListener listener;
@@ -398,7 +398,7 @@
Lock appSubscriptionLock = getAppSubscription(appKey);
try {
appSubscriptionLock.lock();
- oldListener.removeListener(url.getProtocolServiceKey(), listener);
+ oldListener.removeListener(url.getServiceKey(), listener);
if (!oldListener.hasListeners()) {
oldListener.destroy();
removeAppSubscriptionLock(appKey);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index bfe1b8b..a3e68cd 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -16,10 +16,12 @@
*/
package org.apache.dubbo.registry.client;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -30,26 +32,36 @@
import org.apache.dubbo.registry.ProviderFirstParams;
import org.apache.dubbo.registry.integration.AbstractConfiguratorListener;
import org.apache.dubbo.registry.integration.DynamicDirectory;
+import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
+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.RpcServiceContext;
import org.apache.dubbo.rpc.cluster.Configurator;
import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import org.apache.dubbo.rpc.model.ModuleModel;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_HASHMAP_LOAD_FACTOR;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
@@ -58,19 +70,21 @@
import static org.apache.dubbo.rpc.model.ScopeModelUtil.getModuleModel;
public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> {
- private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceDiscoveryRegistryDirectory.class);
/**
* instance address to invoker mapping.
* The initial value is null and the midway may be assigned to null, please use the local variable reference
*/
- private volatile Map<String, Invoker<T>> urlInvokerMap;
+ private volatile Map<ProtocolServiceKeyWithAddress, Invoker<T>> urlInvokerMap;
private volatile ReferenceConfigurationListener referenceConfigurationListener;
private volatile boolean enableConfigurationListen = true;
private volatile List<URL> originalUrls = null;
private volatile Map<String, String> overrideQueryMap;
private final Set<String> providerFirstParams;
private final ModuleModel moduleModel;
+ private final ProtocolServiceKey consumerProtocolServiceKey;
+ private final Map<ProtocolServiceKey, URL> customizedConsumerUrlMap = new ConcurrentHashMap<>();
public ServiceDiscoveryRegistryDirectory(Class<T> serviceType, URL url) {
super(serviceType, url);
@@ -94,6 +108,9 @@
}
}
+ String protocol = consumerUrl.getParameter(PROTOCOL_KEY, consumerUrl.getProtocol());
+ consumerProtocolServiceKey = new ProtocolServiceKey(consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(),
+ !CommonConstants.CONSUMER.equals(protocol) ? protocol : null);
}
@Override
@@ -224,15 +241,15 @@
}
// use local reference to avoid NPE as this.urlInvokerMap will be set null concurrently at destroyAllInvokers().
- Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap;
+ Map<ProtocolServiceKeyWithAddress, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap;
// can't use local reference as oldUrlInvokerMap's mappings might be removed directly at toInvokers().
- Map<String, Invoker<T>> oldUrlInvokerMap = null;
+ Map<ProtocolServiceKeyWithAddress, Invoker<T>> oldUrlInvokerMap = null;
if (localUrlInvokerMap != null) {
// the initial capacity should be set greater than the maximum number of entries divided by the load factor to avoid resizing.
oldUrlInvokerMap = new LinkedHashMap<>(Math.round(1 + localUrlInvokerMap.size() / DEFAULT_HASHMAP_LOAD_FACTOR));
localUrlInvokerMap.forEach(oldUrlInvokerMap::put);
}
- Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls);// Translate url list to Invoker map
+ Map<ProtocolServiceKeyWithAddress, Invoker<T>> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls);// Translate url list to Invoker map
logger.info("Refreshed invoker size " + newUrlInvokerMap.size());
if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
@@ -266,21 +283,27 @@
* @param urls
* @return invokers
*/
- private Map<String, Invoker<T>> toInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, List<URL> urls) {
- Map<String, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1));
+ private Map<ProtocolServiceKeyWithAddress, Invoker<T>> toInvokers(Map<ProtocolServiceKeyWithAddress, Invoker<T>> oldUrlInvokerMap, List<URL> urls) {
+ Map<ProtocolServiceKeyWithAddress, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1));
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
+
for (URL url : urls) {
InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url;
if (EMPTY_PROTOCOL.equals(instanceAddressURL.getProtocol())) {
continue;
}
if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(instanceAddressURL.getProtocol())) {
- logger.error(new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() +
+
+ // 4-1 - Unsupported protocol
+
+ logger.error(PROTOCOL_UNSUPPORTED, "protocol extension does not installed", "", "Unsupported protocol.",
+ new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() +
" in notified url: " + instanceAddressURL + " from registry " + getUrl().getAddress() +
" to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).getSupportedExtensions()));
+
continue;
}
@@ -291,33 +314,58 @@
instanceAddressURL = overrideWithConfigurator(instanceAddressURL);
}
- Invoker<T> invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.get(instanceAddressURL.getAddress());
- if (invoker == null || urlChanged(invoker, instanceAddressURL)) { // Not in the cache, refer again
- try {
- boolean enabled = true;
- if (instanceAddressURL.hasParameter(DISABLED_KEY)) {
- enabled = !instanceAddressURL.getParameter(DISABLED_KEY, false);
- } else {
- enabled = instanceAddressURL.getParameter(ENABLED_KEY, true);
+ // filter all the service available (version wildcard, group wildcard, protocol wildcard)
+ int port = instanceAddressURL.getPort();
+ List<ProtocolServiceKey> matchedProtocolServiceKeys = instanceAddressURL.getMetadataInfo()
+ .getMatchedServiceInfos(consumerProtocolServiceKey)
+ .stream()
+ .filter(serviceInfo -> serviceInfo.getPort() <= 0 || serviceInfo.getPort() == port)
+ .map(MetadataInfo.ServiceInfo::getProtocolServiceKey)
+ .collect(Collectors.toList());
+
+ // see org.apache.dubbo.common.ProtocolServiceKey.isSameWith
+ // check if needed to override the consumer url
+ boolean shouldWrap = matchedProtocolServiceKeys.size() != 1 || !consumerProtocolServiceKey.isSameWith(matchedProtocolServiceKeys.get(0));
+
+ for (ProtocolServiceKey matchedProtocolServiceKey : matchedProtocolServiceKeys) {
+ ProtocolServiceKeyWithAddress protocolServiceKeyWithAddress = new ProtocolServiceKeyWithAddress(matchedProtocolServiceKey, instanceAddressURL.getAddress());
+ Invoker<T> invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.get(protocolServiceKeyWithAddress);
+ if (invoker == null || urlChanged(invoker, instanceAddressURL, matchedProtocolServiceKey)) { // Not in the cache, refer again
+ try {
+ boolean enabled;
+ if (instanceAddressURL.hasParameter(DISABLED_KEY)) {
+ enabled = !instanceAddressURL.getParameter(DISABLED_KEY, false);
+ } else {
+ enabled = instanceAddressURL.getParameter(ENABLED_KEY, true);
+ }
+ if (enabled) {
+ if (shouldWrap) {
+ URL newConsumerUrl = customizedConsumerUrlMap.computeIfAbsent(matchedProtocolServiceKey,
+ k -> consumerUrl.setProtocol(k.getProtocol())
+ .addParameter(CommonConstants.GROUP_KEY, k.getGroup())
+ .addParameter(CommonConstants.VERSION_KEY, k.getVersion()));
+ RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl);
+ invoker = new InstanceWrappedInvoker<>(protocol.refer(serviceType, instanceAddressURL), newConsumerUrl, matchedProtocolServiceKey);
+ } else {
+ invoker = protocol.refer(serviceType, instanceAddressURL);
+ }
+ }
+ } catch (Throwable t) {
+ logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + instanceAddressURL + ")" + t.getMessage(), t);
}
- if (enabled) {
- invoker = protocol.refer(serviceType, instanceAddressURL);
+ if (invoker != null) { // Put new invoker in cache
+ newUrlInvokerMap.put(protocolServiceKeyWithAddress, invoker);
}
- } catch (Throwable t) {
- logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + instanceAddressURL + ")" + t.getMessage(), t);
+ } else {
+ newUrlInvokerMap.put(protocolServiceKeyWithAddress, invoker);
+ oldUrlInvokerMap.remove(protocolServiceKeyWithAddress, invoker);
}
- if (invoker != null) { // Put new invoker in cache
- newUrlInvokerMap.put(instanceAddressURL.getAddress(), invoker);
- }
- } else {
- newUrlInvokerMap.put(instanceAddressURL.getAddress(), invoker);
- oldUrlInvokerMap.remove(instanceAddressURL.getAddress(), invoker);
}
}
return newUrlInvokerMap;
}
- private boolean urlChanged(Invoker<T> invoker, InstanceAddressURL newURL) {
+ private boolean urlChanged(Invoker<T> invoker, InstanceAddressURL newURL, ProtocolServiceKey protocolServiceKey) {
InstanceAddressURL oldURL = (InstanceAddressURL) invoker.getUrl();
if (!newURL.getInstance().equals(oldURL.getInstance())) {
@@ -335,16 +383,35 @@
}
}
- MetadataInfo.ServiceInfo oldServiceInfo = oldURL.getMetadataInfo().getValidServiceInfo(getConsumerUrl().getProtocolServiceKey());
+ MetadataInfo.ServiceInfo oldServiceInfo = oldURL.getMetadataInfo().getValidServiceInfo(protocolServiceKey.toString());
if (null == oldServiceInfo) {
return false;
}
- return !oldServiceInfo.equals(newURL.getMetadataInfo().getValidServiceInfo(getConsumerUrl().getProtocolServiceKey()));
+ return !oldServiceInfo.equals(newURL.getMetadataInfo().getValidServiceInfo(protocolServiceKey.toString()));
}
private List<Invoker<T>> toMergeInvokerList(List<Invoker<T>> invokers) {
- return invokers;
+ List<Invoker<T>> mergedInvokers = new ArrayList<>();
+ Map<String, List<Invoker<T>>> groupMap = new HashMap<>();
+ for (Invoker<T> invoker : invokers) {
+ String group = invoker.getUrl().getGroup("");
+ groupMap.computeIfAbsent(group, k -> new ArrayList<>());
+ groupMap.get(group).add(invoker);
+ }
+
+ if (groupMap.size() == 1) {
+ mergedInvokers.addAll(groupMap.values().iterator().next());
+ } else if (groupMap.size() > 1) {
+ for (List<Invoker<T>> groupList : groupMap.values()) {
+ StaticDirectory<T> staticDirectory = new StaticDirectory<>(groupList);
+ staticDirectory.buildRouterChain();
+ mergedInvokers.add(cluster.join(staticDirectory, false));
+ }
+ } else {
+ mergedInvokers = invokers;
+ }
+ return mergedInvokers;
}
/**
@@ -352,7 +419,7 @@
*/
@Override
protected void destroyAllInvokers() {
- Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+ Map<ProtocolServiceKeyWithAddress, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
if (localUrlInvokerMap != null) {
for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
try {
@@ -375,7 +442,7 @@
* @param oldUrlInvokerMap
* @param newUrlInvokerMap
*/
- private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
+ private void destroyUnusedInvokers(Map<ProtocolServiceKeyWithAddress, Invoker<T>> oldUrlInvokerMap, Map<ProtocolServiceKeyWithAddress, Invoker<T>> newUrlInvokerMap) {
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
destroyAllInvokers();
return;
@@ -385,7 +452,7 @@
return;
}
- for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
+ for (Map.Entry<ProtocolServiceKeyWithAddress, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
Invoker<T> invoker = entry.getValue();
if (invoker != null) {
try {
@@ -461,4 +528,88 @@
});
}
}
+
+ public static final class ProtocolServiceKeyWithAddress extends ProtocolServiceKey {
+ private final String address;
+
+ public ProtocolServiceKeyWithAddress(ProtocolServiceKey protocolServiceKey, String address) {
+ super(protocolServiceKey.getInterfaceName(), protocolServiceKey.getVersion(), protocolServiceKey.getGroup(), protocolServiceKey.getProtocol());
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ ProtocolServiceKeyWithAddress that = (ProtocolServiceKeyWithAddress) o;
+ return Objects.equals(address, that.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), address);
+ }
+ }
+
+ public static final class InstanceWrappedInvoker<T> implements Invoker<T> {
+ private final Invoker<T> originInvoker;
+ private final URL newConsumerUrl;
+ private final ProtocolServiceKey protocolServiceKey;
+
+ public InstanceWrappedInvoker(Invoker<T> originInvoker, URL newConsumerUrl, ProtocolServiceKey protocolServiceKey) {
+ this.originInvoker = originInvoker;
+ this.newConsumerUrl = newConsumerUrl;
+ this.protocolServiceKey = protocolServiceKey;
+ }
+
+ @Override
+ public Class<T> getInterface() {
+ return originInvoker.getInterface();
+ }
+
+ @Override
+ public Result invoke(Invocation invocation) throws RpcException {
+ // override consumer url with real protocol service key
+ RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl);
+ // recreate invocation due to the protocol service key changed
+ RpcInvocation copiedInvocation = new RpcInvocation(invocation.getTargetServiceUniqueName(),
+ invocation.getServiceModel(), invocation.getMethodName(), invocation.getServiceName(), protocolServiceKey.toString(),
+ invocation.getParameterTypes(), invocation.getArguments(), invocation.getObjectAttachments(),
+ invocation.getInvoker(), invocation.getAttributes(),
+ invocation instanceof RpcInvocation ? ((RpcInvocation) invocation).getInvokeMode() : null);
+ copiedInvocation.setObjectAttachment(CommonConstants.GROUP_KEY, protocolServiceKey.getGroup());
+ copiedInvocation.setObjectAttachment(CommonConstants.VERSION_KEY, protocolServiceKey.getVersion());
+ return originInvoker.invoke(copiedInvocation);
+ }
+
+ @Override
+ public URL getUrl() {
+ RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl);
+ return originInvoker.getUrl();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl);
+ return originInvoker.isAvailable();
+ }
+
+ @Override
+ public void destroy() {
+ RpcContext.getServiceContext().setConsumerUrl(newConsumerUrl);
+ originInvoker.destroy();
+ }
+ }
+
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index f58e9ac..8cb3072 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -16,14 +16,15 @@
*/
package org.apache.dubbo.registry.client.event.listener;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
-import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
import org.apache.dubbo.registry.NotifyListener;
@@ -33,12 +34,13 @@
import org.apache.dubbo.registry.client.event.RetryServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
+import org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -52,9 +54,8 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import static java.util.Collections.emptySet;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_REFRESH_ADDRESS;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY;
import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION;
@@ -67,7 +68,7 @@
*/
public class ServiceInstancesChangedListener {
- private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceInstancesChangedListener.class);
protected final Set<String> serviceNames;
protected final ServiceDiscovery serviceDiscovery;
@@ -77,17 +78,15 @@
protected AtomicBoolean destroyed = new AtomicBoolean(false);
protected Map<String, List<ServiceInstance>> allInstances;
- protected Map<String, Object> serviceUrls;
+ protected Map<String, List<ProtocolServiceKeyWithUrls>> serviceUrls;
private volatile long lastRefreshTime;
private final Semaphore retryPermission;
private volatile ScheduledFuture<?> retryFuture;
private final ScheduledExecutorService scheduler;
private volatile boolean hasEmptyMetadata;
+ private final Set<ServiceInstanceNotificationCustomizer> serviceInstanceNotificationCustomizers;
- // protocols subscribe by default, specify the protocol that should be subscribed through 'consumer.protocol'.
- private static final String[] SUPPORTED_PROTOCOLS = new String[]{"dubbo", "tri", "rest"};
- public static final String CONSUMER_PROTOCOL_SUFFIX = ":consumer";
public ServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
this.serviceNames = serviceNames;
@@ -96,8 +95,9 @@
this.allInstances = new HashMap<>();
this.serviceUrls = new HashMap<>();
retryPermission = new Semaphore(1);
- this.scheduler = ScopeModelUtil.getApplicationModel(serviceDiscovery == null || serviceDiscovery.getUrl() == null ? null : serviceDiscovery.getUrl().getScopeModel())
- .getBeanFactory().getBean(FrameworkExecutorRepository.class).getMetadataRetryExecutor();
+ ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(serviceDiscovery == null || serviceDiscovery.getUrl() == null ? null : serviceDiscovery.getUrl().getScopeModel());
+ this.scheduler = applicationModel.getBeanFactory().getBean(FrameworkExecutorRepository.class).getMetadataRetryExecutor();
+ this.serviceInstanceNotificationCustomizers = applicationModel.getExtensionLoader(ServiceInstanceNotificationCustomizer.class).getSupportedExtensionInstances();
}
/**
@@ -127,7 +127,7 @@
}
Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
- Map<String, Map<String, Set<String>>> localServiceToRevisions = new HashMap<>();
+ Map<ServiceInfo, Set<String>> localServiceToRevisions = new HashMap<>();
// grouping all instances of this app(service name) by revision
for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
@@ -149,7 +149,14 @@
for (Map.Entry<String, List<ServiceInstance>> entry : revisionToInstances.entrySet()) {
String revision = entry.getKey();
List<ServiceInstance> subInstances = entry.getValue();
- MetadataInfo metadata = serviceDiscovery.getRemoteMetadata(revision, subInstances);
+
+ MetadataInfo metadata = subInstances.stream()
+ .map(ServiceInstance::getServiceMetadata)
+ .filter(Objects::nonNull)
+ .filter(m -> revision.equals(m.getRevision()))
+ .findFirst()
+ .orElseGet(() -> serviceDiscovery.getRemoteMetadata(revision, subInstances));
+
parseMetadata(revision, metadata, localServiceToRevisions);
// update metadata into each instance, in case new instance created.
for (ServiceInstance tmpInstance : subInstances) {
@@ -175,63 +182,57 @@
}
logger.warn("Address refresh try task submitted");
}
+
// return if all metadata is empty, this notification will not take effect.
if (emptyNum == revisionToInstances.size()) {
- logger.error("Address refresh failed because of Metadata Server failure, wait for retry or new address refresh event.");
+ // 1-17 - Address refresh failed.
+ logger.error(REGISTRY_FAILED_REFRESH_ADDRESS, "metadata Server failure", "",
+ "Address refresh failed because of Metadata Server failure, wait for retry or new address refresh event.");
+
return;
}
}
hasEmptyMetadata = false;
- Map<String, Map<Set<String>, Object>> protocolRevisionsToUrls = new HashMap<>();
- Map<String, Object> newServiceUrls = new HashMap<>();
- for (Map.Entry<String, Map<String, Set<String>>> entry : localServiceToRevisions.entrySet()) {
- String protocol = entry.getKey();
- entry.getValue().forEach((protocolServiceKey, revisions) -> {
- Map<Set<String>, Object> revisionsToUrls = protocolRevisionsToUrls.computeIfAbsent(protocol, k -> new HashMap<>());
- Object urls = revisionsToUrls.get(revisions);
- if (urls == null) {
- urls = getServiceUrlsCache(revisionToInstances, revisions, protocol);
- revisionsToUrls.put(revisions, urls);
- }
+ Map<String, Map<Integer, Map<Set<String>, Object>>> protocolRevisionsToUrls = new HashMap<>();
+ Map<String, List<ProtocolServiceKeyWithUrls>> newServiceUrls = new HashMap<>();
+ for (Map.Entry<ServiceInfo, Set<String>> entry : localServiceToRevisions.entrySet()) {
+ ServiceInfo serviceInfo = entry.getKey();
+ Set<String> revisions = entry.getValue();
- newServiceUrls.put(protocolServiceKey, urls);
- });
+ Map<Integer, Map<Set<String>, Object>> portToRevisions = protocolRevisionsToUrls.computeIfAbsent(serviceInfo.getProtocol(), k -> new HashMap<>());
+ Map<Set<String>, Object> revisionsToUrls = portToRevisions.computeIfAbsent(serviceInfo.getPort(), k -> new HashMap<>());
+ Object urls = revisionsToUrls.get(revisions);
+ if (urls == null) {
+ urls = getServiceUrlsCache(revisionToInstances, revisions, serviceInfo.getProtocol(), serviceInfo.getPort());
+ revisionsToUrls.put(revisions, urls);
+ }
+
+ List<ProtocolServiceKeyWithUrls> list = newServiceUrls.computeIfAbsent(serviceInfo.getPath(), k -> new LinkedList<>());
+ list.add(new ProtocolServiceKeyWithUrls(serviceInfo.getProtocolServiceKey(), (List<URL>) urls));
}
this.serviceUrls = newServiceUrls;
this.notifyAddressChanged();
}
- public synchronized void addListenerAndNotify(String serviceKey, NotifyListener listener) {
+ public synchronized void addListenerAndNotify(URL url, NotifyListener listener) {
if (destroyed.get()) {
return;
}
- Set<NotifyListenerWithKey> notifyListeners = this.listeners.computeIfAbsent(serviceKey, _k -> new ConcurrentHashSet<>());
- // {@code protocolServiceKeysToConsume} will be specific protocols configured in reference config or default protocols supported by framework.
- Set<String> protocolServiceKeysToConsume = getProtocolServiceKeyList(serviceKey, listener);
- // Add current listener to serviceKey set, there will have more than one listener when multiple references of one same service is configured.
- NotifyListenerWithKey listenerWithKey = new NotifyListenerWithKey(serviceKey, protocolServiceKeysToConsume, listener);
+ Set<NotifyListenerWithKey> notifyListeners = this.listeners.computeIfAbsent(url.getServiceKey(), _k -> new ConcurrentHashSet<>());
+ String protocol = listener.getConsumerUrl().getParameter(PROTOCOL_KEY, url.getProtocol());
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(url.getServiceInterface(), url.getVersion(), url.getGroup(),
+ !CommonConstants.CONSUMER.equals(protocol) ? protocol : null);
+ NotifyListenerWithKey listenerWithKey = new NotifyListenerWithKey(protocolServiceKey, listener);
notifyListeners.add(listenerWithKey);
// Aggregate address and notify on subscription.
- List<URL> urls;
- if (protocolServiceKeysToConsume.size() > 1) {
- urls = new ArrayList<>();
- for (String protocolServiceKey : protocolServiceKeysToConsume) {
- List<URL> urlsOfProtocol = getAddresses(protocolServiceKey, listener.getConsumerUrl());
- if (CollectionUtils.isNotEmpty(urlsOfProtocol)) {
- logger.info(String.format("Found %s urls of protocol service key %s ", urlsOfProtocol.size(), protocolServiceKey));
- urls.addAll(urlsOfProtocol);
- }
- }
- } else {
- urls = getAddresses(protocolServiceKeysToConsume.iterator().next(), listener.getConsumerUrl());
- }
+ List<URL> urls = getAddresses(protocolServiceKey, listener.getConsumerUrl());
if (CollectionUtils.isNotEmpty(urls)) {
- logger.info(String.format("Notify serviceKey: %s, listener: %s with %s urls on subscription", serviceKey, listener, urls.size()));
+ logger.info(String.format("Notify serviceKey: %s, listener: %s with %s urls on subscription", protocolServiceKey, listener, urls.size()));
listener.notify(urls);
}
}
@@ -244,9 +245,7 @@
// synchronized method, no need to use DCL
Set<NotifyListenerWithKey> notifyListeners = this.listeners.get(serviceKey);
if (notifyListeners != null) {
- NotifyListenerWithKey listenerWithKey = new NotifyListenerWithKey(serviceKey, notifyListener);
- // Remove from global listeners
- notifyListeners.remove(listenerWithKey);
+ notifyListeners.removeIf(listener -> listener.getNotifyListener().equals(notifyListener));
// ServiceKey has no listener, remove set
if (notifyListeners.size() == 0) {
@@ -308,6 +307,9 @@
String appName = event.getServiceName();
List<ServiceInstance> appInstances = event.getServiceInstances();
logger.info("Received instance notification, serviceName: " + appName + ", instances: " + appInstances.size());
+ for (ServiceInstanceNotificationCustomizer serviceInstanceNotificationCustomizer : serviceInstanceNotificationCustomizers) {
+ serviceInstanceNotificationCustomizer.customize(appInstances);
+ }
allInstances.put(appName, appInstances);
lastRefreshTime = System.currentTimeMillis();
}
@@ -345,23 +347,28 @@
return emptyMetadataNum;
}
- protected Map<String, Map<String, Set<String>>> parseMetadata(String revision, MetadataInfo metadata, Map<String, Map<String, Set<String>>> localServiceToRevisions) {
+ protected Map<ServiceInfo, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<ServiceInfo, Set<String>> localServiceToRevisions) {
Map<String, ServiceInfo> serviceInfos = metadata.getServices();
for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
- String protocol = entry.getValue().getProtocol();
- String protocolServiceKey = entry.getValue().getMatchKey();
- Map<String, Set<String>> map = localServiceToRevisions.computeIfAbsent(protocol, _p -> new HashMap<>());
- Set<String> set = map.computeIfAbsent(protocolServiceKey, _k -> new TreeSet<>());
+ Set<String> set = localServiceToRevisions.computeIfAbsent(entry.getValue(), _k -> new TreeSet<>());
set.add(revision);
}
return localServiceToRevisions;
}
- protected Object getServiceUrlsCache(Map<String, List<ServiceInstance>> revisionToInstances, Set<String> revisions, String protocol) {
+ protected Object getServiceUrlsCache(Map<String, List<ServiceInstance>> revisionToInstances, Set<String> revisions, String protocol, int port) {
List<URL> urls = new ArrayList<>();
for (String r : revisions) {
for (ServiceInstance i : revisionToInstances.get(r)) {
+ if (port > 0) {
+ if (i.getPort() == port) {
+ urls.add(i.toURL(protocol).setScopeModel(i.getApplicationModel()));
+ } else {
+ urls.add(((DefaultServiceInstance) i).copyFrom(port).toURL(protocol).setScopeModel(i.getApplicationModel()));
+ }
+ continue;
+ }
// different protocols may have ports specified in meta
if (ServiceInstanceMetadataUtils.hasEndpoints(i)) {
DefaultServiceInstance.Endpoint endpoint = ServiceInstanceMetadataUtils.getEndpoint(i, protocol);
@@ -376,8 +383,22 @@
return urls;
}
- protected List<URL> getAddresses(String serviceProtocolKey, URL consumerURL) {
- return (List<URL>) serviceUrls.get(serviceProtocolKey);
+ protected List<URL> getAddresses(ProtocolServiceKey protocolServiceKey, URL consumerURL) {
+ List<ProtocolServiceKeyWithUrls> protocolServiceKeyWithUrlsList = serviceUrls.get(protocolServiceKey.getInterfaceName());
+ List<URL> urls = new ArrayList<>();
+ if (protocolServiceKeyWithUrlsList != null) {
+ for (ProtocolServiceKeyWithUrls protocolServiceKeyWithUrls : protocolServiceKeyWithUrlsList) {
+ if (ProtocolServiceKey.Matcher.isMatch(protocolServiceKey, protocolServiceKeyWithUrls.getProtocolServiceKey())) {
+ urls.addAll(protocolServiceKeyWithUrls.getUrls());
+ }
+ }
+ }
+ if (serviceUrls.containsKey(CommonConstants.ANY_VALUE)) {
+ for (ProtocolServiceKeyWithUrls protocolServiceKeyWithUrls : serviceUrls.get(CommonConstants.ANY_VALUE)) {
+ urls.addAll(protocolServiceKeyWithUrls.getUrls());
+ }
+ }
+ return urls;
}
/**
@@ -389,28 +410,10 @@
// 2 multiple subscription listener of the same service
for (NotifyListenerWithKey listenerWithKey : listenerSet) {
NotifyListener notifyListener = listenerWithKey.getNotifyListener();
- if (listenerWithKey.getProtocolServiceKeys().size() == 1) {// 2.1 if one specific protocol is specified
- String protocolServiceKey = listenerWithKey.getProtocolServiceKeys().iterator().next();
- //FIXME, group wildcard match
- List<URL> urls = toUrlsWithEmpty(getAddresses(protocolServiceKey, notifyListener.getConsumerUrl()));
- logger.info("Notify service " + protocolServiceKey + " with urls " + urls.size());
- notifyListener.notify(urls);
- } else {// 2.2 multiple protocols or no protocol(using default protocols) set
- List<URL> urls = new ArrayList<>();
- int effectiveProtocolNum = 0;
- for (String protocolServiceKey : listenerWithKey.getProtocolServiceKeys()) {
- List<URL> tmpUrls = getAddresses(protocolServiceKey, notifyListener.getConsumerUrl());
- if (CollectionUtils.isNotEmpty(tmpUrls)) {
- logger.info("Found " + urls.size() + " urls of protocol service key " + protocolServiceKey);
- effectiveProtocolNum++;
- urls.addAll(tmpUrls);
- }
- }
- logger.info("Notify service " + serviceKey + " with " + urls.size() + " urls from " + effectiveProtocolNum + " different protocols");
- urls = toUrlsWithEmpty(urls);
- notifyListener.notify(urls);
- }
+ List<URL> urls = toUrlsWithEmpty(getAddresses(listenerWithKey.getProtocolServiceKey(), notifyListener.getConsumerUrl()));
+ logger.info("Notify service " + listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size());
+ notifyListener.notify(urls);
}
});
}
@@ -425,9 +428,7 @@
if (CollectionUtils.isEmpty(urls) && !emptyProtectionEnabled) {
// notice that the service of this.url may not be the same as notify listener.
- URL empty = URLBuilder.from(this.url)
- .setProtocol(EMPTY_PROTOCOL)
- .build();
+ URL empty = URLBuilder.from(this.url).setProtocol(EMPTY_PROTOCOL).build();
urls.add(empty);
}
return urls;
@@ -472,46 +473,6 @@
return Objects.hash(getClass(), getServiceNames());
}
- /**
- * Calculate the protocol list that the consumer cares about.
- *
- * @param serviceKey possible input serviceKey includes
- * 1. {group}/{interface}:{version}, if protocol is not specified
- * 2. {group}/{interface}:{version}:{user specified protocols}
- * @param listener listener also contains the user specified protocols
- * @return protocol list with the format {group}/{interface}:{version}:{protocol}
- */
- protected Set<String> getProtocolServiceKeyList(String serviceKey, NotifyListener listener) {
- if (StringUtils.isEmpty(serviceKey)) {
- return emptySet();
- }
-
- Set<String> result = new HashSet<>();
- String protocol = listener.getConsumerUrl().getParameter(PROTOCOL_KEY);
- if (serviceKey.endsWith(CONSUMER_PROTOCOL_SUFFIX)) {
- serviceKey = serviceKey.substring(0, serviceKey.indexOf(CONSUMER_PROTOCOL_SUFFIX));
- }
-
- if (StringUtils.isNotEmpty(protocol)) {
- int protocolIndex = serviceKey.indexOf(":" + protocol);
- if (protocol.contains(",") && protocolIndex != -1) {
- serviceKey = serviceKey.substring(0, protocolIndex);
- String[] specifiedProtocols = protocol.split(",");
- for (String specifiedProtocol : specifiedProtocols) {
- result.add(serviceKey + GROUP_CHAR_SEPARATOR + specifiedProtocol);
- }
- } else {
- result.add(serviceKey);
- }
- } else {
- for (String supportedProtocol : SUPPORTED_PROTOCOLS) {
- result.add(serviceKey + GROUP_CHAR_SEPARATOR + supportedProtocol);
- }
- }
-
- return result;
- }
-
protected class AddressRefreshRetryTask implements Runnable {
private final RetryServiceInstancesChangedEvent retryEvent;
private final Semaphore retryPermission;
@@ -529,26 +490,16 @@
}
public static class NotifyListenerWithKey {
- private final String serviceKey;
- private final Set<String> protocolServiceKeys;
+ private final ProtocolServiceKey protocolServiceKey;
private final NotifyListener notifyListener;
- public NotifyListenerWithKey(String protocolServiceKey, Set<String> protocolServiceKeys, NotifyListener notifyListener) {
- this.serviceKey = protocolServiceKey;
- this.protocolServiceKeys = (protocolServiceKeys == null ? new ConcurrentHashSet<>() : protocolServiceKeys);
+ public NotifyListenerWithKey(ProtocolServiceKey protocolServiceKey, NotifyListener notifyListener) {
+ this.protocolServiceKey = protocolServiceKey;
this.notifyListener = notifyListener;
}
- public NotifyListenerWithKey(String protocolServiceKey, NotifyListener notifyListener) {
- this(protocolServiceKey, null, notifyListener);
- }
-
- public String getServiceKey() {
- return serviceKey;
- }
-
- public Set<String> getProtocolServiceKeys() {
- return protocolServiceKeys;
+ public ProtocolServiceKey getProtocolServiceKey() {
+ return protocolServiceKey;
}
public NotifyListener getNotifyListener() {
@@ -564,12 +515,30 @@
return false;
}
NotifyListenerWithKey that = (NotifyListenerWithKey) o;
- return Objects.equals(serviceKey, that.serviceKey) && Objects.equals(notifyListener, that.notifyListener);
+ return Objects.equals(protocolServiceKey, that.protocolServiceKey) && Objects.equals(notifyListener, that.notifyListener);
}
@Override
public int hashCode() {
- return Objects.hash(serviceKey, notifyListener);
+ return Objects.hash(protocolServiceKey, notifyListener);
+ }
+ }
+
+ public static class ProtocolServiceKeyWithUrls {
+ private final ProtocolServiceKey protocolServiceKey;
+ private final List<URL> urls;
+
+ public ProtocolServiceKeyWithUrls(ProtocolServiceKey protocolServiceKey, List<URL> urls) {
+ this.protocolServiceKey = protocolServiceKey;
+ this.urls = urls;
+ }
+
+ public ProtocolServiceKey getProtocolServiceKey() {
+ return protocolServiceKey;
+ }
+
+ public List<URL> getUrls() {
+ return urls;
}
}
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/MetadataServiceDelegation.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceDelegation.java
similarity index 97%
rename from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/MetadataServiceDelegation.java
rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceDelegation.java
index 451bf75..5cd7556 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/MetadataServiceDelegation.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceDelegation.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.metadata;
+package org.apache.dubbo.registry.client.metadata;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.resource.Disposable;
import org.apache.dubbo.common.utils.StringUtils;
@@ -50,7 +50,8 @@
* Implementation providing remote RPC service to facilitate the query of metadata information.
*/
public class MetadataServiceDelegation implements MetadataService, Disposable {
- Logger logger = LoggerFactory.getLogger(getClass());
+ ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
private final ApplicationModel applicationModel;
private final RegistryManager registryManager;
private ConcurrentMap<String, InstanceMetadataChangedListener> instanceMetadataChangedListenerMap = new ConcurrentHashMap<>();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
index 2850faa..d8cf528 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
@@ -18,7 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigItem;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
@@ -40,7 +40,7 @@
public class MetadataServiceNameMapping extends AbstractServiceNameMapping {
- private final Logger logger = LoggerFactory.getLogger(getClass());
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
private static final List<String> IGNORED_SERVICE_INTERFACES = Collections.singletonList(MetadataService.class.getName());
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizer.java
index 3611a62..f5786c2 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceHostPortCustomizer.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client.metadata;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.metadata.MetadataInfo;
@@ -31,11 +31,13 @@
import java.util.Set;
import java.util.SortedSet;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER;
+
/**
* The {@link ServiceInstanceCustomizer} to customize the {@link ServiceInstance#getPort() port} of service instance.
*/
public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomizer {
- private static final Logger logger = LoggerFactory.getLogger(ServiceInstanceHostPortCustomizer.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceInstanceHostPortCustomizer.class);
@Override
@@ -71,7 +73,14 @@
}
if (host == null || port == -1) {
- logger.warn("The default preferredProtocol \"" + preferredProtocol + "\" is not found, fall back to the strategy that pick the first found protocol. Please try to modify the config of dubbo.application.protocol");
+
+ // 4-2 - Can't find an instance URL using the default preferredProtocol.
+
+ logger.warn(PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER, "typo in preferred protocol", "",
+ "Can't find an instance URL using the default preferredProtocol \"" + preferredProtocol + "\", " +
+ "falling back to the strategy that pick the first found protocol. " +
+ "Please try modifying the config of dubbo.application.protocol");
+
URL url = urls.iterator().next();
host = url.getHost();
port = url.getPort();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
index 702e636..0803445 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
@@ -18,10 +18,11 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.DefaultServiceInstance.Endpoint;
@@ -35,6 +36,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
@@ -56,7 +58,7 @@
* @since 2.7.5
*/
public class ServiceInstanceMetadataUtils {
- private static final Logger LOGGER = LoggerFactory.getLogger(ServiceInstanceMetadataUtils.class);
+ private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(ServiceInstanceMetadataUtils.class);
/**
* The prefix of {@link MetadataService} : "dubbo.metadata-service."
@@ -118,8 +120,10 @@
* @return <code>null</code> if not exits
*/
public static String getExportedServicesRevision(ServiceInstance serviceInstance) {
- Map<String, String> metadata = serviceInstance.getMetadata();
- return metadata.get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME);
+ return Optional.ofNullable(serviceInstance.getServiceMetadata())
+ .map(MetadataInfo::getRevision)
+ .filter(StringUtils::isNotEmpty)
+ .orElse(serviceInstance.getMetadata(EXPORTED_SERVICES_REVISION_PROPERTY_NAME));
}
/**
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceNotificationCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceNotificationCustomizer.java
new file mode 100644
index 0000000..9a60999
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceNotificationCustomizer.java
@@ -0,0 +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.registry.client.metadata;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.List;
+
+@SPI
+public interface ServiceInstanceNotificationCustomizer {
+
+ void customize(List<ServiceInstance> serviceInstance);
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java
new file mode 100644
index 0000000..c50dc36
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SpringCloudServiceInstanceNotificationCustomizer.java
@@ -0,0 +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.registry.client.metadata;
+
+import org.apache.dubbo.common.ProtocolServiceKey;
+import org.apache.dubbo.metadata.MetadataInfo;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class SpringCloudServiceInstanceNotificationCustomizer implements ServiceInstanceNotificationCustomizer {
+ @Override
+ public void customize(List<ServiceInstance> serviceInstance) {
+ for (ServiceInstance instance : serviceInstance) {
+ if ("SPRING_CLOUD".equals(instance.getMetadata("preserved.register.source"))) {
+ MetadataInfo.ServiceInfo serviceInfo = new MetadataInfo.ServiceInfo("*", "*", "*", "rest", instance.getPort(), "*", new HashMap<>());
+ MetadataInfo metadataInfo = new MetadataInfo(instance.getServiceName(), "SPRING_CLOUD", new ConcurrentHashMap<>(Collections.singletonMap("*", serviceInfo))) {
+ @Override
+ public List<ServiceInfo> getMatchedServiceInfos(ProtocolServiceKey consumerProtocolServiceKey) {
+ getServices().putIfAbsent(consumerProtocolServiceKey.getServiceKeyString(),
+ new MetadataInfo.ServiceInfo(consumerProtocolServiceKey.getInterfaceName(),
+ consumerProtocolServiceKey.getGroup(), consumerProtocolServiceKey.getVersion(),
+ consumerProtocolServiceKey.getProtocol(), instance.getPort(), consumerProtocolServiceKey.getInterfaceName(), new HashMap<>()));
+ return super.getMatchedServiceInfos(consumerProtocolServiceKey);
+ }
+ };
+
+
+ instance.setServiceMetadata(metadataInfo);
+ }
+ }
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
index f2cc886..b36139a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.metadata.MetadataService;
@@ -44,6 +44,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_MISSING_METADATA_CONFIG_PORT;
import static org.apache.dubbo.common.utils.StringUtils.isBlank;
import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE;
import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
@@ -58,7 +59,7 @@
*/
public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuilder {
- private final Logger logger = LoggerFactory.getLogger(getClass());
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
public static final String NAME = "standard";
@@ -111,8 +112,7 @@
.addParameter(THREADPOOL_KEY, "cached")
.addParameter(THREADS_KEY, "100")
.addParameter(CORE_THREADS_KEY, "2")
- .addParameter(RETRIES_KEY, 0);
-
+ .addParameter(RETRIES_KEY, 0);
// add parameters
params.forEach(urlBuilder::addParameter);
@@ -125,17 +125,31 @@
private URL generateUrlWithoutMetadata(String serviceName, String host, Integer instancePort) {
Integer port = metadataServicePort;
if (port == null || port < 1) {
- logger.warn("Metadata Service Port is not provided, since DNS is not able to negotiate the metadata port " +
- "between Provider and Consumer, will try to use instance port as the default metadata port.");
+
+ // 1-18 - Metadata Service Port should be specified for consumer.
+
+ logger.warn(REGISTRY_MISSING_METADATA_CONFIG_PORT, "missing configuration of metadata service port", "",
+ "Metadata Service Port is not provided. Since DNS is not able to negotiate the metadata port " +
+ "between Provider and Consumer, Dubbo will try using instance port as the default metadata port.");
+
port = instancePort;
}
if (port == null || port < 1) {
+
+ // 1-18 - Metadata Service Port should be specified for consumer.
+
String message = "Metadata Service Port should be specified for consumer. " +
"Please set dubbo.application.metadataServicePort and " +
"make sure it has been set on provider side. " +
"ServiceName: " + serviceName + " Host: " + host;
- throw new IllegalStateException(message);
+
+ IllegalStateException illegalStateException = new IllegalStateException(message);
+
+ logger.error(REGISTRY_MISSING_METADATA_CONFIG_PORT, "missing configuration of metadata service port", "",
+ message, illegalStateException);
+
+ throw illegalStateException;
}
URLBuilder urlBuilder = new URLBuilder()
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
index 331053f..0c3d90a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/DefaultMigrationAddressComparator.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client.migration;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
@@ -30,7 +30,7 @@
import java.util.concurrent.ConcurrentHashMap;
public class DefaultMigrationAddressComparator implements MigrationAddressComparator {
- private static final Logger logger = LoggerFactory.getLogger(DefaultMigrationAddressComparator.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultMigrationAddressComparator.class);
private static final String MIGRATION_THRESHOLD = "dubbo.application.migration.threshold";
private static final String DEFAULT_THRESHOLD_STRING = "0.0";
private static final float DEFAULT_THREAD = 0f;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
index 8f41a30..fc525e9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationInvoker.java
@@ -255,7 +255,7 @@
try {
Thread.sleep(delay * 1000L);
} catch (InterruptedException e) {
- logger.error("Interrupter when waiting for address notify!" + e);
+ logger.error("Interrupted when waiting for address notify!" + e);
}
} else {
// do not wait address notify by default
@@ -264,7 +264,7 @@
try {
latch.await(delay, TimeUnit.SECONDS);
} catch (InterruptedException e) {
- logger.error("Interrupter when waiting for address notify!" + e);
+ logger.error("Interrupted when waiting for address notify!" + e);
}
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
index 6d99dae..6ce0f27 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleHandler.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client.migration;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.status.reporter.FrameworkStatusReportService;
import org.apache.dubbo.registry.client.migration.model.MigrationRule;
@@ -25,7 +25,7 @@
public class MigrationRuleHandler<T> {
public static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "dubbo.application.migration.step";
- private static final Logger logger = LoggerFactory.getLogger(MigrationRuleHandler.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MigrationRuleHandler.class);
private MigrationClusterInvoker<T> migrationInvoker;
private MigrationStep currentStep;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
index c7cbb03..9623e8a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationRuleListener.java
@@ -22,7 +22,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -63,7 +63,7 @@
*/
@Activate
public class MigrationRuleListener implements RegistryProtocolListener, ConfigurationListener {
- private static final Logger logger = LoggerFactory.getLogger(MigrationRuleListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MigrationRuleListener.class);
private static final String DUBBO_SERVICEDISCOVERY_MIGRATION = "DUBBO_SERVICEDISCOVERY_MIGRATION";
private static final String MIGRATION_DELAY_KEY = "dubbo.application.migration.delay";
private static final int MIGRATION_DEFAULT_DELAY_TIME = 60000;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java
index 572aa22..b48c79c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/ServiceDiscoveryMigrationInvoker.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.client.migration;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.client.migration.model.MigrationRule;
@@ -31,7 +31,7 @@
import java.util.concurrent.CountDownLatch;
public class ServiceDiscoveryMigrationInvoker<T> extends MigrationInvoker<T> {
- private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryMigrationInvoker.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ServiceDiscoveryMigrationInvoker.class);
public ServiceDiscoveryMigrationInvoker(RegistryProtocol registryProtocol, Cluster cluster, Registry registry, Class<T> type, URL url, URL consumerUrl) {
super(registryProtocol, cluster, registry, type, url, consumerUrl);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java
index 074dc1c..75dbd7d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java
@@ -21,7 +21,7 @@
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.cluster.Configurator;
@@ -35,6 +35,7 @@
import java.util.Set;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG;
import static org.apache.dubbo.rpc.Constants.ACCESS_LOG_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.RULE_KEY;
@@ -45,7 +46,7 @@
* AbstractConfiguratorListener
*/
public abstract class AbstractConfiguratorListener implements ConfigurationListener {
- private static final Logger logger = LoggerFactory.getLogger(AbstractConfiguratorListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractConfiguratorListener.class);
protected List<Configurator> configurators = Collections.emptyList();
protected GovernanceRuleRepository ruleRepository;
@@ -107,8 +108,12 @@
// parseConfigurators will recognize app/service config automatically.
urls = ConfigParser.parseConfigurators(rawConfig);
} catch (Exception e) {
- logger.warn("Failed to parse raw dynamic config and it will not take effect, the raw config is: "
+ // 1-14 - Failed to parse raw dynamic config.
+
+ logger.warn(REGISTRY_FAILED_PARSE_DYNAMIC_CONFIG, "", "",
+ "Failed to parse raw dynamic config and it will not take effect, the raw config is: "
+ rawConfig + ", cause: " + e.getMessage());
+
return false;
}
List<URL> safeUrls = urls.stream()
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index bd78c53..167cdd2 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
@@ -46,6 +46,9 @@
import java.util.List;
import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_SERVICE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_UNREGISTER_URL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
@@ -59,7 +62,7 @@
*/
public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
- private static final Logger logger = LoggerFactory.getLogger(DynamicDirectory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DynamicDirectory.class);
protected final Cluster cluster;
@@ -204,7 +207,10 @@
List<Invoker<T>> result = routerChain.route(getConsumerUrl(), invokers, invocation);
return result == null ? BitList.emptyList() : result;
} catch (Throwable t) {
- logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+ // 2-1 - Failed to execute routing.
+ logger.error(CLUSTER_FAILED_SITE_SELECTION, "", "",
+ "Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+
return BitList.emptyList();
}
}
@@ -295,15 +301,20 @@
registry.unregister(getRegisteredConsumerUrl());
}
} catch (Throwable t) {
- logger.warn("unexpected error when unregister service " + serviceKey + " from registry: " + registry.getUrl(), t);
+ // 1-8: Failed to unregister / unsubscribe url on destroy.
+ logger.warn(REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "",
+ "unexpected error when unregister service " + serviceKey + " from registry: " + registry.getUrl(), t);
}
+
// unsubscribe.
try {
if (getSubscribeUrl() != null && registry != null && registry.isAvailable()) {
registry.unsubscribe(getSubscribeUrl(), this);
}
} catch (Throwable t) {
- logger.warn("unexpected error when unsubscribe service " + serviceKey + " from registry: " + registry.getUrl(), t);
+ // 1-8: Failed to unregister / unsubscribe url on destroy.
+ logger.warn(REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "",
+ "unexpected error when unsubscribe service " + serviceKey + " from registry: " + registry.getUrl(), t);
}
ExtensionLoader<AddressListener> addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class);
@@ -318,7 +329,9 @@
try {
destroyAllInvokers();
} catch (Throwable t) {
- logger.warn("Failed to destroy service " + serviceKey, t);
+ // 1-15 - Failed to destroy service.
+ logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "",
+ "Failed to destroy service " + serviceKey, t);
}
routerChain.destroy();
invokersChangedListener = null;
@@ -333,7 +346,9 @@
try {
destroyAllInvokers();
} catch (Throwable t) {
- logger.warn("Failed to destroy service " + serviceKey, t);
+ // 1-15 - Failed to destroy service.
+ logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "",
+ "Failed to destroy service " + serviceKey, t);
}
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index d169880..25389e3 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.url.component.DubboServiceAddressURL;
import org.apache.dubbo.common.url.component.ServiceAddressURL;
@@ -34,6 +34,7 @@
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Configurator;
import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
@@ -61,6 +62,13 @@
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_SERVICE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNSUPPORTED_CATEGORY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED_CONVERT_URL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REFER_INVOKER;
import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
@@ -81,7 +89,7 @@
* RegistryDirectory
*/
public class RegistryDirectory<T> extends DynamicDirectory<T> {
- private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryDirectory.class);
private final ConsumerConfigurationListener consumerConfigurationListener;
private ReferenceConfigurationListener referenceConfigurationListener;
@@ -224,8 +232,13 @@
// use local reference to avoid NPE as this.cachedInvokerUrls will be set null by destroyAllInvokers().
Set<URL> localCachedInvokerUrls = this.cachedInvokerUrls;
if (invokerUrls.isEmpty() && localCachedInvokerUrls != null) {
- logger.warn("Service" + serviceKey + " received empty address list with no EMPTY protocol set, trigger empty protection.");
+
+ // 1-4 Empty address.
+ logger.warn(REGISTRY_EMPTY_ADDRESS, "configuration ", "",
+ "Service" + serviceKey + " received empty address list with no EMPTY protocol set, trigger empty protection.");
+
invokerUrls.addAll(localCachedInvokerUrls);
+
} else {
localCachedInvokerUrls = new HashSet<>();
localCachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
@@ -246,7 +259,7 @@
}
Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls);// Translate url list to Invoker map
- /**
+ /*
* If the calculation is wrong, it is not processed.
*
* 1. The protocol configured by the client is inconsistent with the protocol of the server.
@@ -255,8 +268,16 @@
*
*/
if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
- logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
- .toString()));
+
+ // 3-1 - Failed to convert the URL address into Invokers.
+
+ logger.error(
+ PROXY_FAILED_CONVERT_URL, "inconsistency between the client protocol and the protocol of the server",
+ "", "urls to invokers error",
+ new IllegalStateException(
+ "urls to invokers error. invokerUrls.size :" +
+ invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));
+
return;
}
@@ -333,7 +354,7 @@
}
/**
- * Turn urls into invokers, and if url has been refer, will not re-reference.
+ * Turn urls into invokers, and if url has been referred, will not re-reference.
* the items that will be put into newUrlInvokeMap will be removed from oldUrlInvokerMap.
*
* @param oldUrlInvokerMap it might be modified during the process.
@@ -347,33 +368,15 @@
}
String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
for (URL providerUrl : urls) {
- // If protocol is configured at the reference side, only the matching protocol is selected
- if (queryProtocols != null && queryProtocols.length() > 0) {
- boolean accept = false;
- String[] acceptProtocols = queryProtocols.split(",");
- for (String acceptProtocol : acceptProtocols) {
- if (providerUrl.getProtocol().equals(acceptProtocol)) {
- accept = true;
- break;
- }
- }
- if (!accept) {
- continue;
- }
- }
- if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
+ if (!checkProtocolValid(queryProtocols, providerUrl)) {
continue;
}
- if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
- logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
- " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
- " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
- getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).getSupportedExtensions()));
- continue;
- }
+
URL url = mergeUrl(providerUrl);
- // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
+ // Cache key is url that does not merge with consumer side parameters,
+ // regardless of how the consumer combines parameters,
+ // if the server url changes, then refer again
Invoker<T> invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.remove(url);
if (invoker == null) { // Not in the cache, refer again
try {
@@ -387,7 +390,18 @@
invoker = protocol.refer(serviceType, url);
}
} catch (Throwable t) {
- logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+
+ // Thrown by AbstractProtocol.optimizeSerialization()
+ if (t instanceof RpcException && t.getMessage().contains("serialization optimizer")) {
+ // 4-2 - serialization optimizer class initialization failed.
+ logger.error(PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER, "typo in optimizer class", "",
+ "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+
+ } else {
+ // 4-3 - Failed to refer invoker by other reason.
+ logger.error(PROTOCOL_FAILED_REFER_INVOKER, "", "",
+ "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+ }
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(url, invoker);
@@ -399,6 +413,44 @@
return newUrlInvokerMap;
}
+ private boolean checkProtocolValid(String queryProtocols, URL providerUrl) {
+ // If protocol is configured at the reference side, only the matching protocol is selected
+ if (queryProtocols != null && queryProtocols.length() > 0) {
+ boolean accept = false;
+
+ String[] acceptProtocols = queryProtocols.split(",");
+ for (String acceptProtocol : acceptProtocols) {
+ if (providerUrl.getProtocol().equals(acceptProtocol)) {
+ accept = true;
+ break;
+ }
+ }
+
+ if (!accept) {
+ return false;
+ }
+ }
+
+ if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
+ return false;
+ }
+
+ if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
+
+ // 4-1 - Unsupported protocol
+
+ logger.error(PROTOCOL_UNSUPPORTED, "protocol extension does not installed", "", "Unsupported protocol.",
+ new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
+ " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
+ " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
+ getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).getSupportedExtensions()));
+
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Merge url parameters. the order is: override > -D >Consumer > Provider
*
@@ -494,7 +546,9 @@
try {
invoker.destroyAll();
} catch (Throwable t) {
- logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
+ // 1-15 - Failed to destroy service
+ logger.warn(REGISTRY_FAILED_DESTROY_SERVICE, "", "",
+ "Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
}
}
localUrlInvokerMap.clear();
@@ -547,8 +601,12 @@
APP_DYNAMIC_CONFIGURATORS_CATEGORY.equals(category)) {
return true;
}
- logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " +
+
+ // 1-16 - Unsupported category in NotifyListener
+ logger.warn(REGISTRY_UNSUPPORTED_CATEGORY, "", "",
+ "Unsupported category " + category + " in notified url: " + url + " from registry " +
getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
+
return false;
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
index 85db0e7..05387f5 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.timer.HashedWheelTimer;
@@ -78,6 +78,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.IPV6_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -136,21 +137,21 @@
public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = {
APPLICATION_KEY, CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, CLUSTER_KEY, CONNECTIONS_KEY, DEPRECATED_KEY,
GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, TOKEN_KEY, VERSION_KEY, WARMUP_KEY,
- WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY, SIDE_KEY
+ WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY, SIDE_KEY, IPV6_KEY
};
public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = {
APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
};
- private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryProtocol.class);
+
private final Map<String, ServiceConfigurationListener> serviceConfigurationListeners = new ConcurrentHashMap<>();
//To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
//provider url <--> exporter
private final ConcurrentMap<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<>();
protected Protocol protocol;
protected ProxyFactory proxyFactory;
- //protected RegistryFactory registryFactory;
private ConcurrentMap<URL, ReExportTask> reExportFailedTasks = new ConcurrentHashMap<>();
private HashedWheelTimer retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128);
@@ -180,11 +181,6 @@
this.protocol = protocol;
}
- // Cannot inject registryFactory (application scope) into protocol (framework scope)
-// public void setRegistryFactory(RegistryFactory registryFactory) {
-// this.registryFactory = registryFactory;
-// }
-
public void setProxyFactory(ProxyFactory proxyFactory) {
this.proxyFactory = proxyFactory;
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/AbstractRetryTask.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/AbstractRetryTask.java
index de790d8..425430b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/AbstractRetryTask.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/retry/AbstractRetryTask.java
@@ -18,7 +18,7 @@
package org.apache.dubbo.registry.retry;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.timer.Timeout;
import org.apache.dubbo.common.timer.Timer;
@@ -28,6 +28,7 @@
import java.util.concurrent.TimeUnit;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EXECUTE_RETRYING_TASK;
import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD;
import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_TIMES;
import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY;
@@ -38,7 +39,7 @@
*/
public abstract class AbstractRetryTask implements TimerTask {
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
/**
* url for retry task
@@ -113,8 +114,12 @@
return;
}
if (times > retryTimes) {
- // reach the most times of retry.
- logger.warn("Final failed to execute task " + taskName + ", url: " + url + ", retry " + retryTimes + " times.");
+ // 1-13 - failed to execute the retrying task.
+
+ logger.warn(
+ REGISTRY_EXECUTE_RETRYING_TASK, "registry center offline", "Check the registry server.",
+ "Final failed to execute task " + taskName + ", url: " + url + ", retry " + retryTimes + " times.");
+
return;
}
if (logger.isInfoEnabled()) {
@@ -123,7 +128,12 @@
try {
doRetry(url, registry, timeout);
} catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
- logger.warn("Failed to execute task " + taskName + ", url: " + url + ", waiting for again, cause:" + t.getMessage(), t);
+
+ // 1-13 - failed to execute the retrying task.
+
+ logger.warn(REGISTRY_EXECUTE_RETRYING_TASK, "registry center offline", "Check the registry server.",
+ "Failed to execute task " + taskName + ", url: " + url + ", waiting for again, cause:" + t.getMessage(), t);
+
// reput this task when catch exception.
reput(timeout, retryPeriod);
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java
index 516a974..01a3e3e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistry.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.support;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.utils.CollectionUtils;
@@ -40,6 +40,7 @@
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -60,6 +61,11 @@
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.FILE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_NOTIFY_EVENT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DESTROY_UNREGISTER_URL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_READ_WRITE_CACHE_FILE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_DELETE_LOCKFILE;
import static org.apache.dubbo.common.constants.RegistryConstants.ACCEPTS_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY;
import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_KEY;
@@ -70,7 +76,10 @@
import static org.apache.dubbo.registry.Constants.USER_HOME;
/**
- * AbstractRegistry. (SPI, Prototype, ThreadSafe)
+ * <p>
+ * Provides a fail-safe registry service backed by cache file. The consumer/provider can still find each other when registry center crashed.
+ *
+ * (SPI, Prototype, ThreadSafe)
*/
public abstract class AbstractRegistry implements Registry {
@@ -84,7 +93,8 @@
private static final long DEFAULT_INTERVAL_SAVE_PROPERTIES = 500L;
// Log output
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
// Local disk cache, where the special key value.registries records the list of registry centers, and the others are the list of notified service providers
private final Properties properties = new Properties();
// File cache timing writing
@@ -103,7 +113,9 @@
protected RegistryManager registryManager;
protected ApplicationModel applicationModel;
- public AbstractRegistry(URL url) {
+ private static final String CAUSE_MULTI_DUBBO_USING_SAME_FILE = "multiple Dubbo instance are using the same file";
+
+ protected AbstractRegistry(URL url) {
setUrl(url);
registryManager = url.getOrDefaultApplicationModel().getBeanFactory().getBean(RegistryManager.class);
localCacheEnabled = url.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true);
@@ -112,19 +124,36 @@
if (localCacheEnabled) {
// Start file save timer
syncSaveFile = url.getParameter(REGISTRY_FILESAVE_SYNC_KEY, false);
+
String defaultFilename = System.getProperty(USER_HOME) + DUBBO_REGISTRY + url.getApplication() +
"-" + url.getAddress().replaceAll(":", "-") + CACHE;
+
String filename = url.getParameter(FILE_KEY, defaultFilename);
File file = null;
+
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
if (!file.getParentFile().mkdirs()) {
- throw new IllegalArgumentException("Invalid registry cache file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
+
+ IllegalArgumentException illegalArgumentException = new IllegalArgumentException(
+ "Invalid registry cache file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
+
+ if (logger != null) {
+ // 1-9 failed to read / save registry cache file.
+
+ logger.error(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, "cache directory inaccessible",
+ "Try adjusting permission of the directory.",
+ "failed to create directory", illegalArgumentException);
+ }
+
+ throw illegalArgumentException;
}
}
}
+
this.file = file;
+
// When starting the subscription center,
// we need to read the local cache file for future Registry fault tolerance processing.
loadProperties();
@@ -191,13 +220,22 @@
if (!lockfile.exists()) {
lockfile.createNewFile();
}
+
try (RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
- FileChannel channel = raf.getChannel()) {
+ FileChannel channel = raf.getChannel()) {
FileLock lock = channel.tryLock();
if (lock == null) {
- throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", " +
+
+ IOException ioException = new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", " +
"ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
+
+ // 1-9 failed to read / save registry cache file.
+ logger.warn(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ "Adjust dubbo.registry.file.", ioException);
+
+ throw ioException;
}
+
// Save
try {
if (!file.exists()) {
@@ -228,29 +266,39 @@
}
} catch (Throwable e) {
savePropertiesRetryTimes.incrementAndGet();
+
if (savePropertiesRetryTimes.get() >= MAX_RETRY_TIMES_SAVE_PROPERTIES) {
if (e instanceof OverlappingFileLockException) {
// fix #9341, ignore OverlappingFileLockException
logger.info("Failed to save registry cache file for file overlapping lock exception, file name " + file.getName());
} else {
- logger.warn("Failed to save registry cache file after retrying " + MAX_RETRY_TIMES_SAVE_PROPERTIES + " times, cause: " + e.getMessage(), e);
+ // 1-9 failed to read / save registry cache file.
+ logger.warn(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ "Failed to save registry cache file after retrying " + MAX_RETRY_TIMES_SAVE_PROPERTIES + " times, cause: " + e.getMessage(), e);
}
+
savePropertiesRetryTimes.set(0);
return;
}
+
if (version < lastCacheChanged.get()) {
savePropertiesRetryTimes.set(0);
return;
} else {
registryCacheExecutor.schedule(() -> doSaveProperties(lastCacheChanged.incrementAndGet()), DEFAULT_INTERVAL_SAVE_PROPERTIES, TimeUnit.MILLISECONDS);
}
+
if (!(e instanceof OverlappingFileLockException)) {
- logger.warn("Failed to save registry cache file, will retry, cause: " + e.getMessage(), e);
+ logger.warn(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE,
+ "However, the retrying count limit is not exceeded. Dubbo will still try.",
+ "Failed to save registry cache file, will retry, cause: " + e.getMessage(), e);
}
} finally {
if (lockfile != null) {
if (!lockfile.delete()) {
- logger.warn(String.format("Failed to delete lock file [%s]", lockfile.getName()));
+ // 1-10 Failed to delete lock file.
+ logger.warn(REGISTRY_FAILED_DELETE_LOCKFILE, "", "",
+ String.format("Failed to delete lock file [%s]", lockfile.getName()));
}
}
}
@@ -266,13 +314,24 @@
logger.info("Loaded registry cache file " + file);
}
} catch (IOException e) {
- logger.warn(e.getMessage(), e);
+ // 1-9 failed to read / save registry cache file.
+ logger.warn(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ e.getMessage(), e);
+
} catch (Throwable e) {
- logger.warn("Failed to load registry cache file " + file, e);
+ // 1-9 failed to read / save registry cache file.
+ logger.warn(REGISTRY_FAILED_READ_WRITE_CACHE_FILE, CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ "Failed to load registry cache file " + file, e);
}
}
public List<URL> getCacheUrls(URL url) {
+ Map<String, List<URL>> categoryNotified = notified.get(url);
+ if (CollectionUtils.isNotEmptyMap(categoryNotified)) {
+ List<URL> urls = categoryNotified.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
+ return urls;
+ }
+
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
@@ -422,7 +481,9 @@
try {
notify(url, listener, filterEmpty(url, urls));
} catch (Throwable t) {
- logger.error("Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t);
+ // 1-7: Failed to notify registry event.
+ logger.error(REGISTRY_FAILED_NOTIFY_EVENT, "consumer is offline", "",
+ "Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t);
}
}
}
@@ -430,7 +491,7 @@
}
/**
- * Notify changes from the Provider side.
+ * Notify changes from the provider side.
*
* @param url consumer side url
* @param listener listener
@@ -444,7 +505,8 @@
throw new IllegalArgumentException("notify listener == null");
}
if ((CollectionUtils.isEmpty(urls)) && !ANY_VALUE.equals(url.getServiceInterface())) {
- logger.warn("Ignore empty notify urls for subscribe url " + url);
+ // 1-4 Empty address.
+ logger.warn(REGISTRY_EMPTY_ADDRESS, "", "", "Ignore empty notify urls for subscribe url " + url);
return;
}
if (logger.isInfoEnabled()) {
@@ -468,6 +530,7 @@
List<URL> categoryList = entry.getValue();
categoryNotified.put(category, categoryList);
listener.notify(categoryList);
+
// We will update our cache file after each notification.
// When our Registry has a subscribed failure due to network jitter, we can return at least the existing cache URL.
if (localCacheEnabled) {
@@ -521,7 +584,9 @@
logger.info("Destroy unregister url " + url);
}
} catch (Throwable t) {
- logger.warn("Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
+ // 1-8: Failed to unregister / unsubscribe url on destroy.
+ logger.warn(REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "",
+ "Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
}
}
}
@@ -537,7 +602,9 @@
logger.info("Destroy unsubscribe url " + url);
}
} catch (Throwable t) {
- logger.warn("Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
+ // 1-8: Failed to unregister / unsubscribe url on destroy.
+ logger.warn(REGISTRY_FAILED_DESTROY_UNREGISTER_URL, "", "",
+ "Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
}
}
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
index 3933c2c..3400ac1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
@@ -14,11 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.dubbo.registry.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
@@ -29,6 +30,7 @@
import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_CREATE_INSTANCE;
import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY;
import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
@@ -39,7 +41,7 @@
*/
public abstract class AbstractRegistryFactory implements RegistryFactory, ScopeModelAware {
- private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRegistryFactory.class);
+ private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(AbstractRegistryFactory.class);
private RegistryManager registryManager;
protected ApplicationModel applicationModel;
@@ -69,9 +71,11 @@
.removeAttribute(EXPORT_KEY)
.removeAttribute(REFER_KEY)
.build();
+
String key = createRegistryCacheKey(url);
Registry registry = null;
boolean check = url.getParameter(CHECK_KEY, true) && url.getPort() != 0;
+
// Lock the registry access process to ensure a single instance of the registry
registryManager.getRegistryLock().lock();
try {
@@ -81,11 +85,13 @@
if (null != defaultNopRegistry) {
return defaultNopRegistry;
}
+
registry = registryManager.getRegistry(key);
if (registry != null) {
return registry;
}
- //create registry by spi/ioc
+
+ // create registry by spi/ioc
registry = createRegistry(url);
if (check && registry == null) {
throw new IllegalStateException("Can not create registry " + url);
@@ -98,7 +104,9 @@
if (check) {
throw new RuntimeException("Can not create registry " + url, e);
} else {
- LOGGER.warn("Failed to obtain or create registry ", e);
+ // 1-11 Failed to obtain or create registry (service) object.
+ LOGGER.warn(REGISTRY_FAILED_CREATE_INSTANCE, "", "",
+ "Failed to obtain or create registry ", e);
}
} finally {
// Release the lock
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
index ae26aae..a6e2ee3 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
@@ -20,7 +20,7 @@
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.URLStrParser;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
import org.apache.dubbo.common.url.component.DubboServiceAddressURL;
@@ -56,30 +56,46 @@
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SEPARATOR_ENCODED;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ADDRESS_INVALID;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_EMPTY_ADDRESS;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_URL_EVICTING;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_PROPERTY_MISSPELLING;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_NO_PARAMETERS_URL;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_CLEAR_CACHED_URLS;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
/**
- * Useful for registries who's sdk returns raw string as provider instance, for example, zookeeper and etcd.
+ * <p>
+ * Based on FailbackRegistry, it adds a URLAddress and URLParam cache to save RAM space.
+ *
+ * <p>
+ * It's useful for registries whose sdk returns raw string as provider instance. For example, Zookeeper and etcd.
+ *
+ * @see org.apache.dubbo.registry.support.FailbackRegistry
+ * @see org.apache.dubbo.registry.support.AbstractRegistry
*/
public abstract class CacheableFailbackRegistry extends FailbackRegistry {
- private static final Logger logger = LoggerFactory.getLogger(CacheableFailbackRegistry.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CacheableFailbackRegistry.class);
+
private static String[] VARIABLE_KEYS = new String[]{ENCODED_TIMESTAMP_KEY, ENCODED_PID_KEY};
protected Map<String, URLAddress> stringAddress = new ConcurrentHashMap<>();
protected Map<String, URLParam> stringParam = new ConcurrentHashMap<>();
+
private ScheduledExecutorService cacheRemovalScheduler;
private int cacheRemovalTaskIntervalInMillis;
private int cacheClearWaitingThresholdInMillis;
+
private Map<ServiceAddressURL, Long> waitForRemove = new ConcurrentHashMap<>();
private Semaphore semaphore = new Semaphore(1);
private final Map<String, String> extraParameters;
protected final Map<URL, Map<String, ServiceAddressURL>> stringUrls = new ConcurrentHashMap<>();
- public CacheableFailbackRegistry(URL url) {
+ protected CacheableFailbackRegistry(URL url) {
super(url);
extraParameters = new HashMap<>(8);
extraParameters.put(CHECK_KEY, String.valueOf(false));
@@ -96,7 +112,10 @@
try {
result = Integer.parseInt(str);
} catch (NumberFormatException e) {
- logger.warn("Invalid registry properties configuration key " + key + ", value " + str);
+ // 0-2 Property type mismatch.
+
+ logger.warn(COMMON_PROPERTY_MISSPELLING, "typo in property value", "This property requires an integer value.",
+ "Invalid registry properties configuration key " + key + ", value " + str);
}
}
return result;
@@ -110,7 +129,7 @@
protected void evictURLCache(URL url) {
Map<String, ServiceAddressURL> oldURLs = stringUrls.remove(url);
try {
- if (oldURLs != null && oldURLs.size() > 0) {
+ if (CollectionUtils.isNotEmptyMap(oldURLs)) {
logger.info("Evicting urls for service " + url.getServiceKey() + ", size " + oldURLs.size());
Long currentTimestamp = System.currentTimeMillis();
for (Map.Entry<String, ServiceAddressURL> entry : oldURLs.entrySet()) {
@@ -123,35 +142,60 @@
}
}
} catch (Exception e) {
- logger.warn("Failed to evict url for " + url.getServiceKey(), e);
+ // It seems that the most possible statement that causes exception is the 'schedule()' method.
+
+ // The executor that FrameworkExecutorRepository.nextScheduledExecutor() method returns
+ // is made by Executors.newSingleThreadScheduledExecutor().
+
+ // After observing the code of ScheduledThreadPoolExecutor.delayedExecute,
+ // it seems that it only throws RejectedExecutionException when the thread pool is shutdown.
+
+ // When? FrameworkExecutorRepository gets destroyed.
+
+ // 1-3: URL evicting failed.
+ logger.warn(REGISTRY_FAILED_URL_EVICTING, "thread pool getting destroyed", "",
+ "Failed to evict url for " + url.getServiceKey(), e);
}
}
protected List<URL> toUrlsWithoutEmpty(URL consumer, Collection<String> providers) {
// keep old urls
Map<String, ServiceAddressURL> oldURLs = stringUrls.get(consumer);
+
// create new urls
Map<String, ServiceAddressURL> newURLs = new HashMap<>((int) (providers.size() / 0.75f + 1));
+
+ // remove 'release', 'dubbo', 'methods', timestamp, 'dubbo.tag' parameter
+ // in consumer URL.
URL copyOfConsumer = removeParamsFromConsumer(consumer);
+
if (oldURLs == null) {
for (String rawProvider : providers) {
+ // remove VARIABLE_KEYS(timestamp,pid..) in provider url.
rawProvider = stripOffVariableKeys(rawProvider);
+
+ // create DubboServiceAddress object using provider url, consumer url, and extra parameters.
ServiceAddressURL cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters());
if (cachedURL == null) {
- logger.warn("Invalid address, failed to parse into URL " + rawProvider);
+ // 1-1: Address invalid.
+ logger.warn(REGISTRY_ADDRESS_INVALID, "mismatch of service group and version settings", "",
+ "Invalid address, failed to parse into URL " + rawProvider);
+
continue;
}
newURLs.put(rawProvider, cachedURL);
}
} else {
- // maybe only default , or "env" + default
+ // maybe only default, or "env" + default
for (String rawProvider : providers) {
rawProvider = stripOffVariableKeys(rawProvider);
ServiceAddressURL cachedURL = oldURLs.remove(rawProvider);
if (cachedURL == null) {
cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters());
if (cachedURL == null) {
- logger.warn("Invalid address, failed to parse into URL " + rawProvider);
+ logger.warn(REGISTRY_ADDRESS_INVALID, "mismatch of service group and version settings", "",
+ "Invalid address, failed to parse into URL " + rawProvider);
+
continue;
}
}
@@ -168,6 +212,7 @@
protected List<URL> toUrlsWithEmpty(URL consumer, String path, Collection<String> providers) {
List<URL> urls = new ArrayList<>(1);
boolean isProviderPath = path.endsWith(PROVIDERS_CATEGORY);
+
if (isProviderPath) {
if (CollectionUtils.isNotEmpty(providers)) {
urls = toUrlsWithoutEmpty(consumer, providers);
@@ -186,7 +231,8 @@
String category = i < 0 ? path : path.substring(i + 1);
if (!PROVIDERS_CATEGORY.equals(category) || !getUrl().getParameter(ENABLE_EMPTY_PROTECTION_KEY, true)) {
if (PROVIDERS_CATEGORY.equals(category)) {
- logger.warn("Service " + consumer.getServiceKey() + " received empty address list and empty protection is disabled, will clear current available addresses");
+ logger.warn(REGISTRY_EMPTY_ADDRESS, "", "",
+ "Service " + consumer.getServiceKey() + " received empty address list and empty protection is disabled, will clear current available addresses");
}
URL empty = URLBuilder.from(consumer)
.setProtocol(EMPTY_PROTOCOL)
@@ -199,32 +245,59 @@
return urls;
}
+ /**
+ * Create DubboServiceAddress object using provider url, consumer url, and extra parameters.
+ *
+ * @param rawProvider provider url string
+ * @param consumerURL URL object of consumer
+ * @param extraParameters extra parameters
+ * @return created DubboServiceAddressURL object
+ */
protected ServiceAddressURL createURL(String rawProvider, URL consumerURL, Map<String, String> extraParameters) {
+
boolean encoded = true;
+
// use encoded value directly to avoid URLDecoder.decode allocation.
int paramStartIdx = rawProvider.indexOf(ENCODED_QUESTION_MARK);
- if (paramStartIdx == -1) {// if ENCODED_QUESTION_MARK does not show, mark as not encoded.
+
+ if (paramStartIdx == -1) {
+ // if ENCODED_QUESTION_MARK does not show, mark as not encoded.
encoded = false;
}
+
+ // split by (encoded) question mark.
+ // part[0] => protocol + ip address + interface.
+ // part[1] => parameters (metadata).
String[] parts = URLStrParser.parseRawURLToArrays(rawProvider, paramStartIdx);
+
if (parts.length <= 1) {
- logger.warn("Received url without any parameters " + rawProvider);
+ // 1-5 Received URL without any parameters.
+ logger.warn(REGISTRY_NO_PARAMETERS_URL, "", "",
+ "Received url without any parameters " + rawProvider);
+
return DubboServiceAddressURL.valueOf(rawProvider, consumerURL);
}
String rawAddress = parts[0];
String rawParams = parts[1];
+
+ // Workaround for 'effectively final': duplicate the encoded variable.
boolean isEncoded = encoded;
+
+ // PathURLAddress if it's using dubbo protocol.
URLAddress address = stringAddress.computeIfAbsent(rawAddress, k -> URLAddress.parse(k, getDefaultURLProtocol(), isEncoded));
address.setTimestamp(System.currentTimeMillis());
URLParam param = stringParam.computeIfAbsent(rawParams, k -> URLParam.parse(k, isEncoded, extraParameters));
param.setTimestamp(System.currentTimeMillis());
- ServiceAddressURL cachedURL = createServiceURL(address, param, consumerURL);
- if (isMatch(consumerURL, cachedURL)) {
- return cachedURL;
+ // create service URL using cached address, param, and consumer URL.
+ ServiceAddressURL cachedServiceAddressURL = createServiceURL(address, param, consumerURL);
+
+ if (isMatch(consumerURL, cachedServiceAddressURL)) {
+ return cachedServiceAddressURL;
}
+
return null;
}
@@ -314,7 +387,9 @@
protected abstract boolean isMatch(URL subscribeUrl, URL providerUrl);
-
+ /**
+ * The cached URL removal task, which will be run on a scheduled thread pool. (It will be run after a delay.)
+ */
private class RemovalTask implements Runnable {
@Override
public void run() {
@@ -341,7 +416,11 @@
}
}
} catch (Throwable t) {
- logger.error("Error occurred when clearing cached URLs", t);
+ // 1-6 Error when clearing cached URLs.
+
+ logger.error(REGISTRY_FAILED_CLEAR_CACHED_URLS, "", "",
+ "Error occurred when clearing cached URLs", t);
+
} finally {
semaphore.release();
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java
index 1477a91..d17c0da 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/FailbackRegistry.java
@@ -40,7 +40,8 @@
import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY;
/**
- * FailbackRegistry. (SPI, Prototype, ThreadSafe)
+ * A template implementation of registry service that provides auto-retry ability.
+ * (SPI, Prototype, ThreadSafe)
*/
public abstract class FailbackRegistry extends AbstractRegistry {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/RegistryManager.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/RegistryManager.java
index 6e74b7a..7a2e187 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/RegistryManager.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/RegistryManager.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.support;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.Registry;
@@ -36,11 +36,13 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_FETCH_INSTANCE;
+
/**
* Application Level, used to collect Registries
*/
public class RegistryManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(RegistryManager.class);
+ private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(RegistryManager.class);
private ApplicationModel applicationModel;
@@ -124,8 +126,11 @@
protected Registry getDefaultNopRegistryIfDestroyed() {
if (destroyed.get()) {
- LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " +
+ // 1-12 Failed to fetch (server) instance since the registry instances have been destroyed.
+ LOGGER.warn(REGISTRY_FAILED_FETCH_INSTANCE, "misuse of the methods", "",
+ "All registry instances have been destroyed, failed to fetch any instance. " +
"Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of.");
+
return DEFAULT_NOP_REGISTRY;
}
return null;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer
new file mode 100644
index 0000000..7746df6
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCustomizer
@@ -0,0 +1 @@
+spring-cloud=org.apache.dubbo.registry.client.metadata.SpringCloudServiceInstanceNotificationCustomizer
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
index 0e52a74..946e0ee 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/CacheableFailbackRegistryTest.java
@@ -34,7 +34,7 @@
import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
-public class CacheableFailbackRegistryTest {
+class CacheableFailbackRegistryTest {
static String service;
static URL serviceUrl;
@@ -70,7 +70,7 @@
}
@Test
- public void testFullURLCache() {
+ void testFullURLCache() {
final AtomicReference<Integer> resCount = new AtomicReference<>(0);
registry = new MockCacheableRegistryImpl(registryUrl);
URL url = URLStrParser.parseEncodedStr(urlStr);
@@ -101,7 +101,7 @@
}
@Test
- public void testURLAddressCache() {
+ void testURLAddressCache() {
final AtomicReference<Integer> resCount = new AtomicReference<>(0);
registry = new MockCacheableRegistryImpl(registryUrl);
URL url = URLStrParser.parseEncodedStr(urlStr);
@@ -127,7 +127,7 @@
}
@Test
- public void testURLParamCache() {
+ void testURLParamCache() {
final AtomicReference<Integer> resCount = new AtomicReference<>(0);
registry = new MockCacheableRegistryImpl(registryUrl);
URL url = URLStrParser.parseEncodedStr(urlStr);
@@ -153,7 +153,7 @@
}
@Test
- public void testRemove() {
+ void testRemove() {
final AtomicReference<Integer> resCount = new AtomicReference<>(0);
registry = new MockCacheableRegistryImpl(registryUrl);
URL url = URLStrParser.parseEncodedStr(urlStr);
@@ -193,7 +193,7 @@
}
@Test
- public void testEmptyProtection() {
+ void testEmptyProtection() {
final AtomicReference<Integer> resCount = new AtomicReference<>(0);
final AtomicReference<List<URL>> currentUrls = new AtomicReference<>();
final List<URL> EMPTY_LIST = new ArrayList<>();
@@ -239,6 +239,4 @@
assertEquals(EMPTY_LIST, currentUrls.get());
}
-
-
}
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryTest.java
index 6ce0410..40546c7 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.registry.client;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.metadata.AbstractServiceNameMapping;
import org.apache.dubbo.metadata.ServiceNameMapping;
@@ -43,7 +44,6 @@
import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.metadata.ServiceNameMapping.toStringKeys;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -220,10 +220,10 @@
// check different protocol
Map<String, Set<ServiceInstancesChangedListener.NotifyListenerWithKey>> serviceListeners = multiAppsInstanceListener.getServiceListeners();
assertEquals(2, serviceListeners.size());
- assertEquals(1, serviceListeners.get(url.getProtocolServiceKey()).size());
- assertEquals(1, serviceListeners.get(url2.getProtocolServiceKey()).size());
- String protocolServiceKey = url2.getServiceKey() + GROUP_CHAR_SEPARATOR + url2.getParameter(PROTOCOL_KEY, DUBBO);
- assertTrue(serviceListeners.get(url2.getProtocolServiceKey()).contains(new ServiceInstancesChangedListener.NotifyListenerWithKey(protocolServiceKey, testServiceListener2)));
+ assertEquals(1, serviceListeners.get(url.getServiceKey()).size());
+ assertEquals(1, serviceListeners.get(url2.getServiceKey()).size());
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(url2.getServiceInterface(), url2.getVersion(), url2.getGroup(), url2.getParameter(PROTOCOL_KEY, DUBBO));
+ assertTrue(serviceListeners.get(url2.getServiceKey()).contains(new ServiceInstancesChangedListener.NotifyListenerWithKey(protocolServiceKey, testServiceListener2)));
}
/**
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/MockServiceInstancesChangedListener.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/MockServiceInstancesChangedListener.java
index 7c187ee..26f82b4 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/MockServiceInstancesChangedListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/MockServiceInstancesChangedListener.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.registry.client.event.listener;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
@@ -35,8 +36,8 @@
}
@Override
- public List<URL> getAddresses(String serviceProtocolKey, URL consumerURL) {
- return super.getAddresses(serviceProtocolKey, consumerURL);
+ public List<URL> getAddresses(ProtocolServiceKey protocolServiceKey, URL consumerURL) {
+ return super.getAddresses(protocolServiceKey, consumerURL);
}
public Map<String, Set<NotifyListenerWithKey>> getServiceListeners() {
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
index b8c6961..7bab0b8 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.registry.client.event.listener;
+import org.apache.dubbo.common.ProtocolServiceKey;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.common.utils.LRUCache;
@@ -49,7 +50,6 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -62,7 +62,6 @@
import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
@@ -219,7 +218,8 @@
Assertions.assertEquals(1, allInstances.size());
Assertions.assertEquals(3, allInstances.get("app1").size());
- List<URL> serviceUrls = listener.getAddresses(service1 + ":dubbo", consumerURL);
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo");
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
Assertions.assertEquals(3, serviceUrls.size());
assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
}
@@ -240,7 +240,8 @@
Assertions.assertEquals(1, allInstances.size());
Assertions.assertEquals(3, allInstances.get("app1").size());
- List<URL> serviceUrls = listener.getAddresses(service1 + ":dubbo", consumerURL);
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo");
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
Assertions.assertEquals(3, serviceUrls.size());
assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
@@ -271,12 +272,16 @@
Assertions.assertEquals(3, allInstances.get("app1").size());
Assertions.assertEquals(4, allInstances.get("app2").size());
- List<URL> serviceUrls = listener.getAddresses(service1 + ":dubbo", consumerURL);
+ ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo");
+
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL);
Assertions.assertEquals(7, serviceUrls.size());
- List<URL> serviceUrls2 = listener.getAddresses(service2 + ":dubbo", consumerURL);
+ List<URL> serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL);
Assertions.assertEquals(4, serviceUrls2.size());
assertTrue(serviceUrls2.get(0).getIp().contains("30.10."));
- List<URL> serviceUrls3 = listener.getAddresses(service3 + ":dubbo", consumerURL);
+ List<URL> serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL);
Assertions.assertEquals(2, serviceUrls3.size());
assertTrue(serviceUrls3.get(0).getIp().contains("30.10."));
}
@@ -308,13 +313,17 @@
Assertions.assertEquals(0, allInstances.get("app1").size());
Assertions.assertEquals(4, allInstances.get("app2").size());
- List<URL> serviceUrls = listener.getAddresses(service1 + ":dubbo", consumerURL);
+ ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey3 = new ProtocolServiceKey(service3, null, null, "dubbo");
+
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL);
Assertions.assertEquals(4, serviceUrls.size());
assertTrue(serviceUrls.get(0).getIp().contains("30.10."));
- List<URL> serviceUrls2 = listener.getAddresses(service2 + ":dubbo", consumerURL);
+ List<URL> serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL);
Assertions.assertEquals(4, serviceUrls2.size());
assertTrue(serviceUrls2.get(0).getIp().contains("30.10."));
- List<URL> serviceUrls3 = listener.getAddresses(service3 + ":dubbo", consumerURL);
+ List<URL> serviceUrls3 = listener.getAddresses(protocolServiceKey3, consumerURL);
Assertions.assertEquals(2, serviceUrls3.size());
assertTrue(serviceUrls3.get(0).getIp().contains("30.10."));
@@ -328,9 +337,9 @@
Assertions.assertEquals(0, allInstances_app2.get("app1").size());
Assertions.assertEquals(0, allInstances_app2.get("app2").size());
- assertTrue(isEmpty(listener.getAddresses(service1 + ":dubbo", consumerURL)));
- assertTrue(isEmpty(listener.getAddresses(service2 + ":dubbo", consumerURL)));
- assertTrue(isEmpty(listener.getAddresses(service3 + ":dubbo", consumerURL)));
+ assertTrue(isEmpty(listener.getAddresses(protocolServiceKey1, consumerURL)));
+ assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL)));
+ assertTrue(isEmpty(listener.getAddresses(protocolServiceKey3, consumerURL)));
}
// 正常场景。检查instance listener -> service listener(Directory)地址推送流程
@@ -345,8 +354,8 @@
when(demoServiceListener.getConsumerUrl()).thenReturn(consumerURL);
NotifyListener demoService2Listener = Mockito.mock(NotifyListener.class);
when(demoService2Listener.getConsumerUrl()).thenReturn(consumerURL2);
- listener.addListenerAndNotify(consumerURL.getProtocolServiceKey(), demoServiceListener);
- listener.addListenerAndNotify(consumerURL2.getProtocolServiceKey(), demoService2Listener);
+ listener.addListenerAndNotify(consumerURL, demoServiceListener);
+ listener.addListenerAndNotify(consumerURL2, demoService2Listener);
// notify app1 instance change
ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances);
listener.onEvent(app1_event);
@@ -378,7 +387,7 @@
// test service listener still get notified when added after instance notification.
NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class);
when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3);
- listener.addListenerAndNotify(consumerURL3.getProtocolServiceKey(), demoService3Listener);
+ listener.addListenerAndNotify(consumerURL3, demoService3Listener);
Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList());
}
@@ -397,10 +406,10 @@
when(demoService2Listener1.getConsumerUrl()).thenReturn(consumerURL2);
NotifyListener demoService2Listener2 = Mockito.mock(NotifyListener.class);
when(demoService2Listener2.getConsumerUrl()).thenReturn(consumerURL2);
- listener.addListenerAndNotify(consumerURL.getProtocolServiceKey(), demoServiceListener1);
- listener.addListenerAndNotify(consumerURL.getProtocolServiceKey(), demoServiceListener2);
- listener.addListenerAndNotify(consumerURL2.getProtocolServiceKey(), demoService2Listener1);
- listener.addListenerAndNotify(consumerURL2.getProtocolServiceKey(), demoService2Listener2);
+ listener.addListenerAndNotify(consumerURL, demoServiceListener1);
+ listener.addListenerAndNotify(consumerURL, demoServiceListener2);
+ listener.addListenerAndNotify(consumerURL2, demoService2Listener1);
+ listener.addListenerAndNotify(consumerURL2, demoService2Listener2);
// notify app1 instance change
ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1Instances);
listener.onEvent(app1_event);
@@ -432,7 +441,7 @@
// test service listener still get notified when added after instance notification.
NotifyListener demoService3Listener = Mockito.mock(NotifyListener.class);
when(demoService3Listener.getConsumerUrl()).thenReturn(consumerURL3);
- listener.addListenerAndNotify(consumerURL3.getProtocolServiceKey(), demoService3Listener);
+ listener.addListenerAndNotify(consumerURL3, demoService3Listener);
Mockito.verify(demoService3Listener, Mockito.times(1)).notify(Mockito.anyList());
}
@@ -448,15 +457,15 @@
// no protocol specified, consume all instances
NotifyListener demoServiceListener1 = Mockito.mock(NotifyListener.class);
when(demoServiceListener1.getConsumerUrl()).thenReturn(noProtocolConsumerURL);
- listener.addListenerAndNotify(noProtocolConsumerURL.getProtocolServiceKey(), demoServiceListener1);
+ listener.addListenerAndNotify(noProtocolConsumerURL, demoServiceListener1);
// multiple protocols specified
NotifyListener demoServiceListener2 = Mockito.mock(NotifyListener.class);
when(demoServiceListener2.getConsumerUrl()).thenReturn(multipleProtocolsConsumerURL);
- listener.addListenerAndNotify(multipleProtocolsConsumerURL.getProtocolServiceKey(), demoServiceListener2);
+ listener.addListenerAndNotify(multipleProtocolsConsumerURL, demoServiceListener2);
// one protocol specified
NotifyListener demoServiceListener3 = Mockito.mock(NotifyListener.class);
when(demoServiceListener3.getConsumerUrl()).thenReturn(singleProtocolsConsumerURL);
- listener.addListenerAndNotify(singleProtocolsConsumerURL.getProtocolServiceKey(), demoServiceListener3);
+ listener.addListenerAndNotify(singleProtocolsConsumerURL, demoServiceListener3);
// notify app1 instance change
ServiceInstancesChangedEvent app1_event = new ServiceInstancesChangedEvent("app1", app1InstancesMultipleProtocols);
@@ -479,9 +488,119 @@
Assertions.assertEquals(1, single_protocol_notifiedUrls.size());
}
- // revision 异常场景。第一次启动,完全拿不到metadata,只能通知部分地址
+ /**
+ * Test subscribe multiple groups
+ */
@Test
@Order(8)
+ public void testSubscribeMultipleGroups() {
+ Set<String> serviceNames = new HashSet<>();
+ serviceNames.add("app1");
+ listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
+
+ // notify instance change
+ ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances);
+ listener.onEvent(event);
+
+ Map<String, List<ServiceInstance>> allInstances = listener.getAllInstances();
+ Assertions.assertEquals(1, allInstances.size());
+ Assertions.assertEquals(3, allInstances.get("app1").size());
+
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo");
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, ",group1", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "*", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "group1", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(0, serviceUrls.size());
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,group2", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(0, serviceUrls.size());
+
+ protocolServiceKey = new ProtocolServiceKey(service1, null, "group1,,group2", "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+ }
+
+ /**
+ * Test subscribe multiple versions
+ */
+ @Test
+ @Order(9)
+ public void testSubscribeMultipleVersions() {
+ Set<String> serviceNames = new HashSet<>();
+ serviceNames.add("app1");
+ listener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
+
+ // notify instance change
+ ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent("app1", app1Instances);
+ listener.onEvent(event);
+
+ Map<String, List<ServiceInstance>> allInstances = listener.getAllInstances();
+ Assertions.assertEquals(1, allInstances.size());
+ Assertions.assertEquals(3, allInstances.get("app1").size());
+
+ ProtocolServiceKey protocolServiceKey = new ProtocolServiceKey(service1, null, null, "dubbo");
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, "", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, "*", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, ",1.0.0", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, "1.0.0,,1.0.1", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(3, serviceUrls.size());
+ assertTrue(serviceUrls.get(0) instanceof InstanceAddressURL);
+
+ protocolServiceKey = new ProtocolServiceKey(service1, "1.0.1,1.0.0", null, "dubbo");
+ serviceUrls = listener.getAddresses(protocolServiceKey, consumerURL);
+ Assertions.assertEquals(0, serviceUrls.size());
+ }
+
+ // revision 异常场景。第一次启动,完全拿不到metadata,只能通知部分地址
+ @Test
+ @Order(10)
public void testRevisionFailureOnStartup() {
Set<String> serviceNames = new HashSet<>();
serviceNames.add("app1");
@@ -490,8 +609,12 @@
ServiceInstancesChangedEvent failed_revision_event = new ServiceInstancesChangedEvent("app1", app1FailedInstances);
listener.onEvent(failed_revision_event);
- List<URL> serviceUrls = listener.getAddresses(service1 + ":dubbo", consumerURL);
- List<URL> serviceUrls2 = listener.getAddresses(service2 + ":dubbo", consumerURL);
+
+ ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo");
+
+ List<URL> serviceUrls = listener.getAddresses(protocolServiceKey1, consumerURL);
+ List<URL> serviceUrls2 = listener.getAddresses(protocolServiceKey2, consumerURL);
assertTrue(isNotEmpty(serviceUrls));
assertTrue(isNotEmpty(serviceUrls2));
@@ -499,7 +622,7 @@
// revision 异常场景。运行中地址通知,拿不到revision就用老版本revision
@Test
- @Order(9)
+ @Order(11)
public void testRevisionFailureOnNotification() {
Set<String> serviceNames = new HashSet<>();
serviceNames.add("app1");
@@ -524,8 +647,11 @@
listener.onEvent(event2);
// event2 did not really take effect
- Assertions.assertEquals(3, listener.getAddresses(service1 + ":dubbo", consumerURL).size());
- assertTrue(isEmpty(listener.getAddresses(service2 + ":dubbo", consumerURL)));
+ ProtocolServiceKey protocolServiceKey1 = new ProtocolServiceKey(service1, null, null, "dubbo");
+ ProtocolServiceKey protocolServiceKey2 = new ProtocolServiceKey(service2, null, null, "dubbo");
+
+ Assertions.assertEquals(3, listener.getAddresses(protocolServiceKey1, consumerURL).size());
+ assertTrue(isEmpty(listener.getAddresses(protocolServiceKey2, consumerURL)));
//
init();
@@ -536,16 +662,16 @@
e.printStackTrace();
}
// check recovered after retry.
- List<URL> serviceUrls_after_retry = listener.getAddresses(service1 + ":dubbo", consumerURL);
+ List<URL> serviceUrls_after_retry = listener.getAddresses(protocolServiceKey1, consumerURL);
Assertions.assertEquals(5, serviceUrls_after_retry.size());
- List<URL> serviceUrls2_after_retry = listener.getAddresses(service2 + ":dubbo", consumerURL);
+ List<URL> serviceUrls2_after_retry = listener.getAddresses(protocolServiceKey2, consumerURL);
Assertions.assertEquals(2, serviceUrls2_after_retry.size());
}
// Abnormal case. Instance does not have revision
@Test
- @Order(10)
+ @Order(12)
public void testInstanceWithoutRevision() {
Set<String> serviceNames = new HashSet<>();
serviceNames.add("app1");
@@ -559,39 +685,6 @@
assertTrue(true);
}
- /**
- * Test calculation of subscription protocols
- */
- @Test
- public void testGetProtocolServiceKeyList() {
- NotifyListener listener = Mockito.mock(NotifyListener.class);
-
- Set<String> serviceNames = new HashSet<>();
- serviceNames.add("app1");
- ServiceDiscovery serviceDiscovery = Mockito.mock(ServiceDiscovery.class);
- ServiceInstancesChangedListener instancesChangedListener = new ServiceInstancesChangedListener(serviceNames, serviceDiscovery);
-
- URL url1 = URL.valueOf("tri://localhost/Service?protocol=tri");
- when(listener.getConsumerUrl()).thenReturn(url1);
- Set<String> keyList11 = instancesChangedListener.getProtocolServiceKeyList(url1.getProtocolServiceKey(), listener);
- assertEquals(getExpectedSet(Arrays.asList("Service:tri")), keyList11);
-
- URL url2 = URL.valueOf("consumer://localhost/Service?group=group&version=1.0");
- when(listener.getConsumerUrl()).thenReturn(url2);
- Set<String> keyList12 = instancesChangedListener.getProtocolServiceKeyList(url2.getProtocolServiceKey(), listener);
- assertEquals(getExpectedSet(Arrays.asList("group/Service:1.0:tri", "group/Service:1.0:dubbo", "group/Service:1.0:rest")), keyList12);
-
- URL url3 = URL.valueOf("dubbo://localhost/Service?protocol=dubbo&group=group&version=1.0");
- when(listener.getConsumerUrl()).thenReturn(url3);
- Set<String> keyList21 = instancesChangedListener.getProtocolServiceKeyList(url3.getProtocolServiceKey(), listener);
- assertEquals(getExpectedSet(Arrays.asList("group/Service:1.0:dubbo")), keyList21);
-
- URL url4 = URL.valueOf("dubbo,tri://localhost/Service?protocol=dubbo,tri&group=group&version=1.0");
- when(listener.getConsumerUrl()).thenReturn(url4);
- Set<String> keyList23 = instancesChangedListener.getProtocolServiceKeyList(url4.getProtocolServiceKey(), listener);
- assertEquals(getExpectedSet(Arrays.asList("group/Service:1.0:dubbo", "group/Service:1.0:tri")), keyList23);
- }
-
Set<String> getExpectedSet(List<String> list) {
return new HashSet<>(list);
}
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java
index f967915..48b3f66 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastRegistry.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.multicast;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
@@ -71,7 +71,7 @@
public class MulticastRegistry extends FailbackRegistry {
// logging output
- private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MulticastRegistry.class);
private static final int DEFAULT_MULTICAST_PORT = 1234;
diff --git a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
index 0da009c..fde87af 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
@@ -39,7 +39,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class MulticastRegistryTest {
+class MulticastRegistryTest {
private String service = "org.apache.dubbo.test.injvmServie";
private URL registryUrl = URL.valueOf("multicast://239.239.239.239/");
@@ -50,7 +50,7 @@
private MulticastRegistry registry = new MulticastRegistry(registryUrl);
@BeforeEach
- public void setUp() {
+ void setUp() {
registry.register(serviceUrl);
}
@@ -58,7 +58,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}.
*/
@Test
- public void testUrlError() {
+ void testUrlError() {
Assertions.assertThrows(UnknownHostException.class, () -> {
try {
URL errorUrl = URL.valueOf("multicast://mullticast.local/");
@@ -73,7 +73,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}.
*/
@Test
- public void testAnyHost() {
+ void testAnyHost() {
Assertions.assertThrows(IllegalStateException.class, () -> {
URL errorUrl = URL.valueOf("multicast://0.0.0.0/");
new MulticastRegistry(errorUrl);
@@ -84,7 +84,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}.
*/
@Test
- public void testGetCustomPort() {
+ void testGetCustomPort() {
int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000));
URL customPortUrl = URL.valueOf("multicast://239.239.239.239:" + port);
MulticastRegistry multicastRegistry = new MulticastRegistry(customPortUrl);
@@ -95,7 +95,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#getRegistered()}.
*/
@Test
- public void testRegister() {
+ void testRegister() {
Set<URL> registered;
// clear first
registered = registry.getRegistered();
@@ -117,7 +117,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#unregister(URL)}.
*/
@Test
- public void testUnregister() {
+ void testUnregister() {
Set<URL> registered;
// register first
@@ -137,7 +137,7 @@
* .
*/
@Test
- public void testSubscribe() {
+ void testSubscribe() {
// verify listener
final URL[] notifyUrl = new URL[1];
for (int i = 0; i < 10; i++) {
@@ -159,7 +159,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#unsubscribe(URL, NotifyListener)}
*/
@Test
- public void testUnsubscribe() {
+ void testUnsubscribe() {
// subscribe first
registry.subscribe(consumerUrl, new NotifyListener() {
@Override
@@ -186,7 +186,7 @@
* Test method for {@link MulticastRegistry#isAvailable()}
*/
@Test
- public void testAvailability() {
+ void testAvailability() {
int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000));
MulticastRegistry registry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.8:" + port));
assertTrue(registry.isAvailable());
@@ -196,7 +196,7 @@
* Test method for {@link MulticastRegistry#destroy()}
*/
@Test
- public void testDestroy() {
+ void testDestroy() {
MulticastSocket socket = registry.getMulticastSocket();
assertFalse(socket.isClosed());
@@ -210,7 +210,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}
*/
@Test
- public void testDefaultPort() {
+ void testDefaultPort() {
MulticastRegistry multicastRegistry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.7"));
try {
MulticastSocket multicastSocket = multicastRegistry.getMulticastSocket();
@@ -224,7 +224,7 @@
* Test method for {@link org.apache.dubbo.registry.multicast.MulticastRegistry#MulticastRegistry(URL)}
*/
@Test
- public void testCustomedPort() {
+ void testCustomedPort() {
int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000));
MulticastRegistry multicastRegistry = new MulticastRegistry(URL.valueOf("multicast://224.5.6.7:" + port));
try {
@@ -236,7 +236,7 @@
}
@Test
- public void testMulticastAddress() {
+ void testMulticastAddress() {
InetAddress multicastAddress = null;
MulticastSocket multicastSocket = null;
try {
diff --git a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
index be9c9c9..a28e8ad 100644
--- a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
+++ b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
@@ -19,7 +19,10 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
@@ -35,15 +38,18 @@
import java.util.stream.Collectors;
import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
/**
* MultipleRegistry
*/
public class MultipleRegistry extends AbstractRegistry {
+ public static final Logger LOGGER = LoggerFactory.getLogger(MultipleRegistry.class);
public static final String REGISTRY_FOR_SERVICE = "service-registry";
public static final String REGISTRY_FOR_REFERENCE = "reference-registry";
+ public static final String REGISTRY_SEPARATOR = "separator";
private final Map<String, Registry> serviceRegistries = new ConcurrentHashMap<>(4);
private final Map<String, Registry> referenceRegistries = new ConcurrentHashMap<>(4);
private final Map<NotifyListener, MultipleNotifyListenerWrapper> multipleNotifyListenerMap = new ConcurrentHashMap<>(32);
@@ -86,7 +92,9 @@
}
protected void initServiceRegistry(URL url, Map<String, Registry> registryMap) {
- origServiceRegistryURLs = url.getParameter(REGISTRY_FOR_SERVICE, new ArrayList<>());
+ String serviceRegistryString = url.getParameter(REGISTRY_FOR_SERVICE);
+ char separator = url.getParameter(REGISTRY_SEPARATOR, COMMA_SEPARATOR).charAt(0);
+ origServiceRegistryURLs = StringUtils.splitToList(serviceRegistryString, separator);
effectServiceRegistryURLs = this.filterServiceRegistry(origServiceRegistryURLs);
for (String tmpUrl : effectServiceRegistryURLs) {
if (registryMap.get(tmpUrl) != null) {
@@ -101,7 +109,9 @@
}
protected void initReferenceRegistry(URL url, Map<String, Registry> registryMap) {
- origReferenceRegistryURLs = url.getParameter(REGISTRY_FOR_REFERENCE, new ArrayList<>());
+ String serviceRegistryString = url.getParameter(REGISTRY_FOR_REFERENCE);
+ char separator = url.getParameter(REGISTRY_SEPARATOR, COMMA_SEPARATOR).charAt(0);
+ origReferenceRegistryURLs = StringUtils.splitToList(serviceRegistryString, separator);
effectReferenceRegistryURLs = this.filterReferenceRegistry(origReferenceRegistryURLs);
for (String tmpUrl : effectReferenceRegistryURLs) {
if (registryMap.get(tmpUrl) != null) {
@@ -292,15 +302,56 @@
}
continue;
}
- notifyURLs.addAll(tmpUrls);
+ URL registryURL = singleNotifyListener.getRegistry().getUrl();
+ aggregateRegistryUrls(notifyURLs, tmpUrls, registryURL);
}
// if no notify URL, add empty protocol URL
if (emptyURL != null && notifyURLs.isEmpty()) {
notifyURLs.add(emptyURL);
+ LOGGER.info("No provider after aggregation, notify url with EMPTY protocol.");
+ } else {
+ LOGGER.info("Aggregated provider url size " + notifyURLs.size());
}
+
this.notify(notifyURLs);
}
+ /**
+ * Aggregate urls from different registries into one unified list while appending registry specific 'attachments' into each url.
+ *
+ * These 'attachments' can be very useful for traffic management among registries.
+ *
+ * @param notifyURLs unified url list
+ * @param singleURLs single registry url list
+ * @param registryURL single registry configuration url
+ */
+ public static void aggregateRegistryUrls(List<URL> notifyURLs, List<URL> singleURLs, URL registryURL) {
+ String registryAttachments = registryURL.getParameter("attachments");
+ if (StringUtils.isNotBlank(registryAttachments)) {
+ LOGGER.info("Registry attachments " + registryAttachments + " found, will append to provider urls, urls size " + singleURLs.size());
+ String[] pairs = registryAttachments.split(COMMA_SEPARATOR);
+ Map<String, String> attachments = new HashMap<>(pairs.length);
+ for (String rawPair : pairs) {
+ String[] keyValuePair = rawPair.split("=");
+ if (keyValuePair.length == 2) {
+ String key = keyValuePair[0];
+ String value = keyValuePair[1];
+ attachments.put(key, value);
+ }
+ }
+
+ for (URL tmpUrl : singleURLs) {
+ for (Map.Entry<String, String> entry : attachments.entrySet()) {
+ tmpUrl = tmpUrl.addParameterIfAbsent(entry.getKey(), entry.getValue());
+ }
+ notifyURLs.add(tmpUrl);
+ }
+ } else {
+ LOGGER.info("Single registry " + registryURL + " has url size " + singleURLs.size());
+ notifyURLs.addAll(singleURLs);
+ }
+ }
+
@Override
public void notify(List<URL> urls) {
sourceNotifyListener.notify(urls);
@@ -338,5 +389,11 @@
public List<URL> getUrlList() {
return urlList;
}
+
+ public Registry getRegistry() {
+ return registry;
+ }
+
+
}
}
diff --git a/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
index 441956a..dfa04ec 100644
--- a/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
+++ b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
@@ -175,4 +175,23 @@
Assertions.assertEquals("empty", list.get(0).getProtocol());
}
+ @Test
+ public void testAggregation() {
+ List<URL> result = new ArrayList<URL>();
+ List<URL> listToAggregate = new ArrayList<URL>();
+ URL url1= URL.valueOf("dubbo://127.0.0.1:20880/service1");
+ URL url2= URL.valueOf("dubbo://127.0.0.1:20880/service1");
+ listToAggregate.add(url1);
+ listToAggregate.add(url2);
+
+ URL registryURL = URL.valueOf("mock://127.0.0.1/RegistryService?attachments=zone=hangzhou,tag=middleware&enable-empty-protection=false");
+
+ MultipleRegistry.MultipleNotifyListenerWrapper.aggregateRegistryUrls(result, listToAggregate, registryURL);
+
+ Assertions.assertEquals(2, result.size());
+ Assertions.assertEquals(2, result.get(0).getParameters().size());
+ Assertions.assertEquals("hangzhou", result.get(0).getParameter("zone"));
+ Assertions.assertEquals("middleware", result.get(1).getParameter("tag"));
+ }
+
}
diff --git a/dubbo-registry/dubbo-registry-nacos/pom.xml b/dubbo-registry/dubbo-registry-nacos/pom.xml
index ab914db..c0918d5 100644
--- a/dubbo-registry/dubbo-registry-nacos/pom.xml
+++ b/dubbo-registry/dubbo-registry-nacos/pom.xml
@@ -54,6 +54,13 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java
index 7d7a7d8..de4dc69 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosNamingServiceWrapper.java
@@ -43,10 +43,6 @@
return namingService.getServerStatus();
}
- public void subscribe(String serviceName, EventListener eventListener) throws NacosException {
- namingService.subscribe(handleInnerSymbol(serviceName), eventListener);
- }
-
public void subscribe(String serviceName, String group, EventListener eventListener) throws NacosException {
namingService.subscribe(handleInnerSymbol(serviceName), group, eventListener);
}
@@ -72,12 +68,8 @@
namingService.deregisterInstance(handleInnerSymbol(serviceName), group, instance);
}
- public ListView<String> getServicesOfServer(int pageNo, int pageSize, String parameter) throws NacosException {
- return namingService.getServicesOfServer(pageNo, pageSize, parameter);
- }
-
- public List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException {
- return namingService.selectInstances(handleInnerSymbol(serviceName), healthy);
+ public ListView<String> getServicesOfServer(int pageNo, int pageSize, String group) throws NacosException {
+ return namingService.getServicesOfServer(pageNo, pageSize, group);
}
public List<Instance> selectInstances(String serviceName, String group, boolean healthy) throws NacosException {
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
index d2f749c..fa47d05 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.url.component.DubboServiceAddressURL;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
@@ -125,7 +125,7 @@
* The interval in second of lookup Nacos service names(only for Dubbo-OPS)
*/
private static final long LOOKUP_INTERVAL = Long.getLong("nacos.service.names.lookup.interval", 30);
- private static final Logger logger = LoggerFactory.getLogger(NacosRegistry.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosRegistry.class);
private final NacosNamingServiceWrapper namingService;
/**
* {@link ScheduledExecutorService} lookup Nacos service names(only for Dubbo-OPS)
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
index 1bf83cc..6435111 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
@@ -17,9 +17,11 @@
package org.apache.dubbo.registry.nacos;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.function.ThrowableFunction;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceDiscovery;
import org.apache.dubbo.registry.client.ServiceInstance;
@@ -28,8 +30,9 @@
import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils;
import org.apache.dubbo.rpc.model.ApplicationModel;
-import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.naming.listener.Event;
+import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
@@ -37,10 +40,13 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
+import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static org.apache.dubbo.common.function.ThrowableConsumer.execute;
import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.createNamingService;
+import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.getGroup;
import static org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils.toInstance;
/**
@@ -53,26 +59,33 @@
private final Logger logger = LoggerFactory.getLogger(getClass());
- private NacosNamingServiceWrapper namingService;
+ private final String group;
+
+ private final NacosNamingServiceWrapper namingService;
+
+ private static final String NACOS_SD_USE_DEFAULT_GROUP_KEY = "dubbo.nacos-service-discovery.use-default-group";
+
+ private final ConcurrentHashMap<String, NacosEventListener> eventListeners = new ConcurrentHashMap<>();
public NacosServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
super(applicationModel, registryURL);
this.namingService = createNamingService(registryURL);
+ // backward compatibility for 3.0.x
+ this.group = Boolean.parseBoolean(ConfigurationUtils.getProperty(applicationModel, NACOS_SD_USE_DEFAULT_GROUP_KEY, "false")) ?
+ DEFAULT_GROUP: getGroup(registryURL);
}
@Override
public void doDestroy() throws Exception {
this.namingService.shutdown();
+ this.eventListeners.clear();
}
@Override
public void doRegister(ServiceInstance serviceInstance) {
execute(namingService, service -> {
Instance instance = toInstance(serviceInstance);
- // Should not register real group for ServiceInstance
- // Or will cause consumer unable to fetch all of the providers from every group
- // Provider's group is invisible for consumer
- service.registerInstance(instance.getServiceName(), Constants.DEFAULT_GROUP, instance);
+ service.registerInstance(instance.getServiceName(), group, instance);
});
}
@@ -80,20 +93,14 @@
public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
execute(namingService, service -> {
Instance instance = toInstance(serviceInstance);
- // Should not register real group for ServiceInstance
- // Or will cause consumer unable to fetch all of the providers from every group
- // Provider's group is invisible for consumer
- service.deregisterInstance(instance.getServiceName(), Constants.DEFAULT_GROUP, instance);
+ service.deregisterInstance(instance.getServiceName(), group, instance);
});
}
@Override
public Set<String> getServices() {
return ThrowableFunction.execute(namingService, service -> {
- // Should not register real group for ServiceInstance
- // Or will cause consumer unable to fetch all of the providers from every group
- // Provider's group is invisible for consumer
- ListView<String> view = service.getServicesOfServer(0, Integer.MAX_VALUE, Constants.DEFAULT_GROUP);
+ ListView<String> view = service.getServicesOfServer(0, Integer.MAX_VALUE, group);
return new LinkedHashSet<>(view.getData());
});
}
@@ -101,7 +108,7 @@
@Override
public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
return ThrowableFunction.execute(namingService, service ->
- service.selectInstances(serviceName, Constants.DEFAULT_GROUP, true)
+ service.selectInstances(serviceName, group, true)
.stream().map((i) -> NacosNamingServiceUtils.toServiceInstance(registryURL, i))
.collect(Collectors.toList())
);
@@ -114,18 +121,68 @@
if (!instanceListeners.add(listener)) {
return;
}
- execute(namingService, service -> listener.getServiceNames().forEach(serviceName -> {
- try {
- service.subscribe(serviceName, Constants.DEFAULT_GROUP, e -> { // Register Nacos EventListener
- if (e instanceof NamingEvent) {
- NamingEvent event = (NamingEvent) e;
- handleEvent(event, listener);
- }
- });
- } catch (NacosException e) {
- logger.error("add nacos service instances changed listener fail ", e);
+ for (String serviceName : listener.getServiceNames()) {
+ NacosEventListener nacosEventListener = eventListeners.get(serviceName);
+ if (nacosEventListener != null) {
+ nacosEventListener.addListener(listener);
+ } else {
+ try {
+ nacosEventListener = new NacosEventListener();
+ nacosEventListener.addListener(listener);
+ namingService.subscribe(serviceName, group, nacosEventListener);
+ eventListeners.put(serviceName, nacosEventListener);
+ } catch (NacosException e) {
+ logger.error("add nacos service instances changed listener fail ", e);
+ }
}
- }));
+ }
+ }
+
+ @Override
+ public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {
+ if (!instanceListeners.remove(listener)) {
+ return;
+ }
+ for (String serviceName : listener.getServiceNames()) {
+ NacosEventListener nacosEventListener = eventListeners.get(serviceName);
+ if (nacosEventListener != null) {
+ nacosEventListener.removeListener(listener);
+ if (nacosEventListener.isEmpty()) {
+ eventListeners.remove(serviceName);
+ try {
+ namingService.unsubscribe(serviceName, group, nacosEventListener);
+ } catch (NacosException e) {
+ logger.error("remove nacos service instances changed listener fail ", e);
+ }
+ }
+ }
+ }
+ }
+
+ public class NacosEventListener implements EventListener {
+ private final Set<ServiceInstancesChangedListener> listeners = new ConcurrentHashSet<>();
+
+ @Override
+ public void onEvent(Event e) {
+ if (e instanceof NamingEvent) {
+ for (ServiceInstancesChangedListener listener : listeners) {
+ NamingEvent event = (NamingEvent) e;
+ handleEvent(event, listener);
+ }
+ }
+ }
+
+ public void addListener(ServiceInstancesChangedListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(ServiceInstancesChangedListener listener) {
+ listeners.remove(listener);
+ }
+
+ public boolean isEmpty() {
+ return listeners.isEmpty();
+ }
}
@Override
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
index 33a8da4..6771bf0 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/util/NacosNamingServiceUtils.java
@@ -17,7 +17,7 @@
package org.apache.dubbo.registry.nacos.util;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
@@ -39,7 +39,9 @@
import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME;
+import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
import static org.apache.dubbo.common.utils.StringConstantFieldValuePredicate.of;
@@ -50,8 +52,8 @@
*/
public class NacosNamingServiceUtils {
- private static final Logger logger = LoggerFactory.getLogger(NacosNamingServiceUtils.class);
- private static String NACOS_GROUP_KEY = "nacos.group";
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NacosNamingServiceUtils.class);
+ private static final String NACOS_GROUP_KEY = "nacos.group";
/**
* Convert the {@link ServiceInstance} to {@link Instance}
@@ -91,6 +93,19 @@
}
/**
+ * The group of {@link NamingService} to register
+ *
+ * @param connectionURL {@link URL connection url}
+ * @return non-null, "default" as default
+ * @since 2.7.5
+ */
+ public static String getGroup(URL connectionURL) {
+ // Compatible with nacos grouping via group.
+ String group = connectionURL.getParameter(GROUP_KEY, DEFAULT_GROUP);
+ return connectionURL.getParameter(NACOS_GROUP_KEY, group);
+ }
+
+ /**
* Create an instance of {@link NamingService} from specified {@link URL connection url}
*
* @param connectionURL {@link URL connection url}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java
index 77cd1f6..87f8cd1 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryTest.java
@@ -29,7 +29,9 @@
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -41,11 +43,12 @@
import java.util.List;
import java.util.Set;
+import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -60,22 +63,59 @@
private static final String LOCALHOST = "127.0.0.1";
- private URL registryUrl;
+ protected URL registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort());
private NacosServiceDiscovery nacosServiceDiscovery;
private NacosNamingServiceWrapper namingServiceWrapper;
+ protected String group = DEFAULT_GROUP;
+
private DefaultServiceInstance createServiceInstance(String serviceName, String host, int port) {
return new DefaultServiceInstance(serviceName, host, port, ScopeModelUtil.getApplicationModel(registryUrl.getScopeModel()));
}
+ public static class NacosServiceDiscoveryGroupTest1 extends NacosServiceDiscoveryTest {
+ public NacosServiceDiscoveryGroupTest1() {
+ super();
+ group = "test-group1";
+ registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", group);
+ }
+ }
+
+ public static class NacosServiceDiscoveryGroupTest2 extends NacosServiceDiscoveryTest {
+ public NacosServiceDiscoveryGroupTest2() {
+ super();
+ group = "test-group2";
+ registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", group);
+ }
+ }
+
+
+
+ public static class NacosServiceDiscoveryGroupTest3 extends NacosServiceDiscoveryTest {
+ public NacosServiceDiscoveryGroupTest3() {
+ super();
+ group = DEFAULT_GROUP;
+ registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort()).addParameter("group", "test-group3");
+ }
+
+ @BeforeAll
+ public static void beforeClass() {
+ System.setProperty("dubbo.nacos-service-discovery.use-default-group", "true");
+ }
+
+ @AfterAll
+ public static void afterClass() {
+ System.clearProperty("dubbo.nacos-service-discovery.use-default-group");
+ }
+ }
+
@BeforeEach
public void init() throws Exception {
ApplicationModel applicationModel = ApplicationModel.defaultModel();
applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig(SERVICE_NAME));
- this.registryUrl = URL.valueOf("nacos://127.0.0.1:" + NetUtils.getAvailablePort());
registryUrl.setScopeModel(applicationModel);
// this.nacosServiceDiscovery = new NacosServiceDiscovery(SERVICE_NAME, registryUrl);
@@ -97,45 +137,32 @@
// register
nacosServiceDiscovery.doRegister(serviceInstance);
- List<Instance> instances = new ArrayList<>();
- Instance instance1 = new Instance();
- Instance instance2 = new Instance();
- Instance instance3 = new Instance();
+ ArgumentCaptor<Instance> instanceCaptor = ArgumentCaptor.forClass(Instance.class);
+ verify(namingServiceWrapper, times(1)).registerInstance(any(), eq(group), instanceCaptor.capture());
- instances.add(instance1);
- instances.add(instance2);
- instances.add(instance3);
-
- ArgumentCaptor<Instance> instance = ArgumentCaptor.forClass(Instance.class);
- verify(namingServiceWrapper, times(1)).registerInstance(any(), any(), instance.capture());
-
- when(namingServiceWrapper.getAllInstances(anyString(), anyString())).thenReturn(instances);
- assertEquals(3, namingServiceWrapper.getAllInstances(anyString(), anyString()).size());
+ Instance capture = instanceCaptor.getValue();
+ assertEquals(SERVICE_NAME, capture.getServiceName());
+ assertEquals(LOCALHOST, capture.getIp());
+ assertEquals(serviceInstance.getPort(), capture.getPort());
}
@Test
public void testDoUnRegister() throws NacosException {
// register
- nacosServiceDiscovery.register(URL.valueOf("dubbo://10.0.2.3:20880/DemoService?interface=DemoService"));
- nacosServiceDiscovery.register();
-
- List<Instance> instances = new ArrayList<>();
- Instance instance1 = new Instance();
- Instance instance2 = new Instance();
- Instance instance3 = new Instance();
-
- instances.add(instance1);
- instances.add(instance2);
- instances.add(instance3);
-
- ArgumentCaptor<Instance> instance = ArgumentCaptor.forClass(Instance.class);
- verify(namingServiceWrapper, times(1)).registerInstance(any(), any(), instance.capture());
-
- when(namingServiceWrapper.getAllInstances(anyString(), anyString())).thenReturn(instances);
- assertEquals(3, namingServiceWrapper.getAllInstances(anyString(), anyString()).size());
+ DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort());
+ // register
+ nacosServiceDiscovery.doRegister(serviceInstance);
// unRegister
- nacosServiceDiscovery.unregister();
+ nacosServiceDiscovery.doUnregister(serviceInstance);
+
+ ArgumentCaptor<Instance> instanceCaptor = ArgumentCaptor.forClass(Instance.class);
+ verify(namingServiceWrapper, times(1)).deregisterInstance(any(), eq(group), instanceCaptor.capture());
+
+ Instance capture = instanceCaptor.getValue();
+ assertEquals(SERVICE_NAME, capture.getServiceName());
+ assertEquals(LOCALHOST, capture.getIp());
+ assertEquals(serviceInstance.getPort(), capture.getPort());
}
@Test
@@ -145,7 +172,7 @@
nacosServiceDiscovery.doRegister(serviceInstance);
ArgumentCaptor<Instance> instance = ArgumentCaptor.forClass(Instance.class);
- verify(namingServiceWrapper, times(1)).registerInstance(any(), any(), instance.capture());
+ verify(namingServiceWrapper, times(1)).registerInstance(any(), eq(group), instance.capture());
String serviceNameWithoutVersion = "providers:org.apache.dubbo.registry.nacos.NacosService:default";
String serviceName = "providers:org.apache.dubbo.registry.nacos.NacosService:1.0.0:default";
@@ -154,7 +181,7 @@
serviceNames.add(serviceName);
ListView<String> result = new ListView<>();
result.setData(serviceNames);
- when(namingServiceWrapper.getServicesOfServer(anyInt(), anyInt(), anyString())).thenReturn(result);
+ when(namingServiceWrapper.getServicesOfServer(anyInt(), anyInt(), eq(group))).thenReturn(result);
Set<String> services = nacosServiceDiscovery.getServices();
assertEquals(2, services.size());
}
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
index 9bba269..d427cea 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -14,10 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.dubbo.registry.zookeeper;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
@@ -32,6 +33,7 @@
import org.apache.dubbo.rpc.RpcException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -56,7 +58,7 @@
*/
public class ZookeeperRegistry extends CacheableFailbackRegistry {
- private static final Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(ZookeeperRegistry.class);
private static final String DEFAULT_ROOT = "dubbo";
@@ -70,23 +72,28 @@
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
+
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
+
String group = url.getGroup(DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
+
this.root = group;
this.zkClient = zookeeperTransporter.connect(url);
+
this.zkClient.addStateListener((state) -> {
if (state == StateListener.RECONNECTED) {
- logger.warn("Trying to fetch the latest urls, in case there're provider changes during connection loss.\n" +
+ logger.warn("Trying to fetch the latest urls, in case there are provider changes during connection loss.\n" +
" Since ephemeral ZNode will not get deleted for a connection lose, " +
"there's no need to re-register url of this instance.");
ZookeeperRegistry.this.fetchLatestAddresses();
} else if (state == StateListener.NEW_SESSION_CREATED) {
logger.warn("Trying to re-register urls and re-subscribe listeners of this instance to registry...");
+
try {
ZookeeperRegistry.this.recover();
} catch (Exception e) {
@@ -111,6 +118,29 @@
@Override
public void destroy() {
super.destroy();
+
+ // remove child listener
+ Set<URL> urls = zkListeners.keySet();
+ for (URL url : urls) {
+ ConcurrentMap<NotifyListener, ChildListener> map = zkListeners.get(url);
+ if (CollectionUtils.isEmptyMap(map)) {
+ continue;
+ }
+ Collection<ChildListener> childListeners = map.values();
+ if (CollectionUtils.isEmpty(childListeners)) {
+ continue;
+ }
+ if (ANY_VALUE.equals(url.getServiceInterface())) {
+ String root = toRootPath();
+ childListeners.stream().forEach(childListener -> zkClient.removeChildListener(root, childListener));
+ } else {
+ for (String path : toCategoriesPath(url)) {
+ childListeners.stream().forEach(childListener -> zkClient.removeChildListener(path, childListener));
+ }
+ }
+ }
+ zkListeners.clear();
+
// Just release zkClient reference, but can not close zk client here for zk client is shared somewhere else.
// See org.apache.dubbo.remoting.zookeeper.AbstractZookeeperTransporter#destroy()
zkClient = null;
@@ -150,6 +180,7 @@
String root = toRootPath();
boolean check = url.getParameter(CHECK_KEY, false);
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
+
ChildListener zkListener = listeners.computeIfAbsent(listener, k -> (parentPath, currentChildren) -> {
for (String child : currentChildren) {
child = URL.decode(child);
@@ -160,7 +191,9 @@
}
}
});
+
zkClient.create(root, false);
+
List<String> services = zkClient.addChildListener(root, zkListener);
if (CollectionUtils.isNotEmpty(services)) {
for (String service : services) {
@@ -172,20 +205,37 @@
}
} else {
CountDownLatch latch = new CountDownLatch(1);
+
try {
List<URL> urls = new ArrayList<>();
+
+ /*
+ Iterate over the category value in URL.
+ With default settings, the path variable can be when url is a consumer URL:
+
+ /dubbo/[service name]/providers,
+ /dubbo/[service name]/configurators
+ /dubbo/[service name]/routers
+ */
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, k, latch));
+
if (zkListener instanceof RegistryChildListenerImpl) {
((RegistryChildListenerImpl) zkListener).setLatch(latch);
}
+
+ // create "directories".
zkClient.create(path, false);
+
+ // Add children (i.e. service items).
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
+ // The invocation point that may cause 1-1.
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
+
notify(url, listener, urls);
} finally {
// tells the listener to run only after the sync notification of main thread finishes.
@@ -199,6 +249,7 @@
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
+ super.doUnsubscribe(url, listener);
checkDestroyed();
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners != null) {
@@ -282,12 +333,12 @@
}
/**
- * When zookeeper connection recovered from a connection loss, it need to fetch the latest provider list.
+ * When zookeeper connection recovered from a connection loss, it needs to fetch the latest provider list.
* re-register watcher is only a side effect and is not mandate.
*/
private void fetchLatestAddresses() {
// subscribe
- Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
+ Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<>(getSubscribed());
if (!recoverSubscribed.isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Fetching the latest urls of " + recoverSubscribed.keySet());
@@ -307,6 +358,11 @@
return UrlUtils.isMatch(subscribeUrl, providerUrl);
}
+ /**
+ * Triggered when children get changed. It will be invoked by implementation of CuratorWatcher.
+ *
+ * 'org.apache.dubbo.remoting.zookeeper.curator5.Curator5ZookeeperClient.CuratorWatcherImpl' (Curator 5)
+ */
private class RegistryChildListenerImpl implements ChildListener {
private final ZookeeperRegistryNotifier notifier;
private volatile CountDownLatch latch;
@@ -322,11 +378,13 @@
@Override
public void childChanged(String path, List<String> children) {
+ // Notify 'notifiers' one by one.
try {
latch.await();
} catch (InterruptedException e) {
logger.warn("Zookeeper children listener thread was interrupted unexpectedly, may cause race condition with the main thread.");
}
+
notifier.notify(path, children);
}
}
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index 11276f4..9e2aea2 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -192,9 +192,9 @@
logger.error("Trying to recover from new zkClient session failed, path is " + path + ", error msg: " + e.getMessage());
}
- List<ServiceInstance> instances = this.getInstances(serviceName);
+ List<ServiceInstance> instances = this.getInstances(watcher.getServiceName());
for (ServiceInstancesChangedListener listener : listeners) {
- listener.onEvent(new ServiceInstancesChangedEvent(serviceName, instances));
+ listener.onEvent(new ServiceInstancesChangedEvent(watcher.getServiceName(), instances));
}
latch.countDown();
});
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
index 465d7dd..681019c 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
@@ -117,4 +117,8 @@
public void setLatch(CountDownLatch latch) {
this.latch = latch;
}
+
+ public String getServiceName() {
+ return serviceName;
+ }
}
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
index 7a36a74..12e6fa0 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/util/CuratorFrameworkUtils.java
@@ -17,8 +17,9 @@
package org.apache.dubbo.registry.zookeeper.util;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.zookeeper.ZookeeperInstance;
@@ -77,7 +78,7 @@
.connectString(connectionURL.getBackupAddress())
.retryPolicy(buildRetryPolicy(connectionURL));
String userInformation = connectionURL.getUserInformation();
- if (userInformation != null && userInformation.length() > 0) {
+ if (StringUtils.isNotEmpty(userInformation)) {
builder = builder.authorization("digest", userInformation.getBytes());
builder.aclProvider(new ACLProvider() {
@Override
@@ -169,7 +170,7 @@
}
private static class CuratorConnectionStateListener implements ConnectionStateListener {
- private static final Logger logger = LoggerFactory.getLogger(CuratorConnectionStateListener.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CuratorConnectionStateListener.class);
private final long UNKNOWN_SESSION_ID = -1L;
protected final int DEFAULT_CONNECTION_TIMEOUT_MS = 30 * 1000;
protected final int DEFAULT_SESSION_TIMEOUT_MS = 60 * 1000;
diff --git a/dubbo-remoting/dubbo-remoting-api/pom.xml b/dubbo-remoting/dubbo-remoting-api/pom.xml
index 56f6b4f..c9b0682 100644
--- a/dubbo-remoting/dubbo-remoting-api/pom.xml
+++ b/dubbo-remoting/dubbo-remoting-api/pom.xml
@@ -50,5 +50,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java
index 5bd80e7..02186be 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Constants.java
@@ -79,7 +79,7 @@
String SERIALIZATION_KEY = "serialization";
- String DEFAULT_REMOTING_SERIALIZATION = "hessian2";
+ String DEFAULT_REMOTING_SERIALIZATION_PROPERTY_KEY = "DUBBO_DEFAULT_SERIALIZATION";
String CODEC_KEY = "codec";
@@ -87,6 +87,8 @@
String SERVER_KEY = "server";
+ String IS_PU_SERVER_KEY = "ispuserver";
+
String CLIENT_KEY = "client";
String DEFAULT_REMOTING_CLIENT = "netty";
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2WireProtocol.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/AbstractWireProtocol.java
similarity index 68%
rename from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2WireProtocol.java
rename to dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/AbstractWireProtocol.java
index 90e617e..278712b 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2WireProtocol.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/AbstractWireProtocol.java
@@ -16,14 +16,18 @@
*/
package org.apache.dubbo.remoting.api;
-import io.netty.handler.codec.http2.Http2FrameLogger;
+import org.apache.dubbo.common.URL;
-import static io.netty.handler.logging.LogLevel.DEBUG;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.ssl.SslContext;
-public abstract class Http2WireProtocol implements WireProtocol {
- public static final Http2FrameLogger CLIENT_LOGGER = new Http2FrameLogger(DEBUG, "H2_CLIENT");
- public static final Http2FrameLogger SERVER_LOGGER = new Http2FrameLogger(DEBUG, "H2_SERVER");
- private final ProtocolDetector detector = new Http2ProtocolDetector();
+public abstract class AbstractWireProtocol implements WireProtocol {
+
+ private final ProtocolDetector detector;
+
+ public AbstractWireProtocol(ProtocolDetector detector) {
+ this.detector = detector;
+ }
@Override
public ProtocolDetector detector() {
@@ -31,6 +35,11 @@
}
@Override
+ public void configClientPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext) {
+
+ }
+
+ @Override
public void close() {
}
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java
deleted file mode 100644
index 634b406..0000000
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java
+++ /dev/null
@@ -1,126 +0,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.remoting.api;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.io.Bytes;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.group.ChannelGroup;
-import io.netty.handler.codec.ByteToMessageDecoder;
-import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslHandler;
-
-import java.util.List;
-import java.util.Set;
-
-public class PortUnificationServerHandler extends ByteToMessageDecoder {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(
- PortUnificationServerHandler.class);
-
- private final ChannelGroup channels;
-
- private final SslContext sslCtx;
- private final URL url;
- private final boolean detectSsl;
- private final List<WireProtocol> protocols;
-
- public PortUnificationServerHandler(URL url, SslContext sslCtx, boolean detectSsl,
- List<WireProtocol> protocols, ChannelGroup channels) {
- this.url = url;
- this.sslCtx = sslCtx;
- this.protocols = protocols;
- this.detectSsl = detectSsl;
- this.channels = channels;
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- LOGGER.error("Unexpected exception from downstream before protocol detected.", cause);
- }
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- super.channelActive(ctx);
- channels.add(ctx.channel());
- }
-
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
- throws Exception {
- // Will use the first five bytes to detect a protocol.
- if (in.readableBytes() < 5) {
- return;
- }
-
- if (isSsl(in)) {
- enableSsl(ctx);
- } else {
- for (final WireProtocol protocol : protocols) {
- in.markReaderIndex();
- final ProtocolDetector.Result result = protocol.detector().detect(ctx, in);
- in.resetReaderIndex();
- switch (result) {
- case UNRECOGNIZED:
- continue;
- case RECOGNIZED:
- protocol.configServerPipeline(url, ctx.pipeline(), sslCtx);
- ctx.pipeline().remove(this);
- case NEED_MORE_DATA:
- return;
- default:
- return;
- }
- }
- byte[] preface = new byte[in.readableBytes()];
- in.readBytes(preface);
- Set<String> supported = url.getApplicationModel()
- .getExtensionLoader(WireProtocol.class)
- .getSupportedExtensions();
- LOGGER.error(String.format("Can not recognize protocol from downstream=%s . "
- + "preface=%s protocols=%s", ctx.channel().remoteAddress(),
- Bytes.bytes2hex(preface),
- supported));
-
- // Unknown protocol; discard everything and close the connection.
- in.clear();
- ctx.close();
- }
- }
-
- private void enableSsl(ChannelHandlerContext ctx) {
- ChannelPipeline p = ctx.pipeline();
- p.addLast("ssl", sslCtx.newHandler(ctx.alloc()));
- p.addLast("unificationA",
- new PortUnificationServerHandler(url, sslCtx, false, protocols, channels));
- p.remove(this);
- }
-
- private boolean isSsl(ByteBuf buf) {
- if (detectSsl) {
- return SslHandler.isEncrypted(buf);
- }
- return false;
- }
-
-
-}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ProtocolDetector.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ProtocolDetector.java
index b868b7a..856d0f1 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ProtocolDetector.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/ProtocolDetector.java
@@ -17,8 +17,8 @@
package org.apache.dubbo.remoting.api;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+
/**
* Determine incoming bytes belong to the specific protocol.
@@ -26,7 +26,7 @@
*/
public interface ProtocolDetector {
- Result detect(final ChannelHandlerContext ctx, final ByteBuf in);
+ Result detect(ChannelBuffer in);
enum Result {
RECOGNIZED, UNRECOGNIZED, NEED_MORE_DATA
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/WireProtocol.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/WireProtocol.java
index 96f7459..fe615f8 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/WireProtocol.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/WireProtocol.java
@@ -19,6 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslContext;
@@ -28,7 +29,7 @@
ProtocolDetector detector();
- void configServerPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext);
+ void configServerProtocolHandler(URL url, ChannelOperator operator);
void configClientPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java
new file mode 100644
index 0000000..9a6ac62
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java
@@ -0,0 +1,73 @@
+/*
+ * 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.remoting.api.pu;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.api.WireProtocol;
+import org.apache.dubbo.remoting.transport.AbstractServer;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class AbstractPortUnificationServer extends AbstractServer {
+ private final List<WireProtocol> protocols;
+
+ /*
+ protocol name --> URL object
+ wire protocol will get url object to config server pipeline for channel
+ */
+ private final Map<String, URL> supportedUrls = new ConcurrentHashMap<>();
+
+ /*
+ protocol name --> ChannelHandler object
+ wire protocol will get handler to config server pipeline for channel
+ (for triple protocol, it's a default handler that do nothing)
+ */
+ private final Map<String, ChannelHandler> supportedHandlers = new ConcurrentHashMap<>();
+
+ public AbstractPortUnificationServer(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+ this.protocols = url.getOrDefaultFrameworkModel().getExtensionLoader(WireProtocol.class).getActivateExtension(url, new String[0]);
+ }
+
+ public List<WireProtocol> getProtocols() {
+ return protocols;
+ }
+
+ /*
+ This method registers URL object and corresponding channel handler to pu server.
+ In PuServerExchanger.bind, this method is called with ConcurrentHashMap.computeIfPresent to register messages to
+ this supportedUrls and supportedHandlers
+ */
+ public void addSupportedProtocol(URL url, ChannelHandler handler) {
+ this.supportedUrls.put(url.getProtocol(), url);
+ this.supportedHandlers.put(url.getProtocol(), handler);
+ }
+
+ protected Map<String, URL> getSupportedUrls() {
+ // this getter is just used by implementation of this class
+ return supportedUrls;
+ }
+
+ public Map<String, ChannelHandler> getSupportedHandlers() {
+ // this getter is just used by implementation of this class
+ return supportedHandlers;
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelHandlerPretender.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelHandlerPretender.java
new file mode 100644
index 0000000..b17dd7c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelHandlerPretender.java
@@ -0,0 +1,32 @@
+/*
+ * 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.remoting.api.pu;
+
+import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+public class ChannelHandlerPretender extends ChannelHandlerAdapter {
+ private final Object realHandler;
+
+ public ChannelHandlerPretender(Object realHandler) {
+ this.realHandler = realHandler;
+ }
+
+ public Object getRealHandler() {
+ return realHandler;
+ }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelOperator.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelOperator.java
new file mode 100644
index 0000000..82a3bde
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/ChannelOperator.java
@@ -0,0 +1,25 @@
+/*
+ * 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.remoting.api.pu;
+
+import org.apache.dubbo.remoting.ChannelHandler;
+
+import java.util.List;
+
+public interface ChannelOperator {
+ void configChannelHandler(List<ChannelHandler> handlerList);
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultCodec.java
new file mode 100644
index 0000000..4756dd3
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultCodec.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.api.pu;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.Codec2;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+
+import java.io.IOException;
+
+public class DefaultCodec implements Codec2 {
+ @Override
+ public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
+
+ }
+
+ @Override
+ public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
+ return null;
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultPuHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultPuHandler.java
new file mode 100644
index 0000000..0e021f9
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/DefaultPuHandler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.remoting.api.pu;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+
+public class DefaultPuHandler implements ChannelHandler {
+ @Override
+ public void connected(Channel channel) throws RemotingException {
+
+ }
+
+ @Override
+ public void disconnected(Channel channel) throws RemotingException {
+
+ }
+
+ @Override
+ public void sent(Channel channel, Object message) throws RemotingException {
+
+ }
+
+ @Override
+ public void received(Channel channel, Object message) throws RemotingException {
+
+ }
+
+ @Override
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/PortUnificationTransporter.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/PortUnificationTransporter.java
new file mode 100644
index 0000000..655e91f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/PortUnificationTransporter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.api.pu;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.Client;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.RemotingException;
+
+@SPI(value = "netty", scope = ExtensionScope.FRAMEWORK)
+public interface PortUnificationTransporter {
+
+ @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
+ AbstractPortUnificationServer bind(URL url, ChannelHandler handler) throws RemotingException;
+
+ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
+ Client connect(URL url, ChannelHandler handler) throws RemotingException;
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffers.java
index 1c0d970..cc62925 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffers.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/buffer/ChannelBuffers.java
@@ -114,6 +114,28 @@
return true;
}
+ // prefix
+ public static boolean prefixEquals(ChannelBuffer bufferA, ChannelBuffer bufferB, int count) {
+ final int aLen = bufferA.readableBytes();
+ final int bLen = bufferB.readableBytes();
+ if (aLen < count || bLen < count) {
+ return false;
+ }
+
+ int aIndex = bufferA.readerIndex();
+ int bIndex = bufferB.readerIndex();
+
+ for (int i = count; i > 0; i--) {
+ if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) {
+ return false;
+ }
+ aIndex++;
+ bIndex++;
+ }
+
+ return true;
+ }
+
public static int hasCode(ChannelBuffer buffer) {
final int aLen = buffer.readableBytes();
final int byteCount = aLen & 7;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
index fc00d4c..10fec10 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
@@ -19,7 +19,11 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.remoting.api.PortUnificationServer;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.RemotingServer;
+import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer;
+import org.apache.dubbo.remoting.api.pu.PortUnificationTransporter;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
@@ -28,20 +32,31 @@
public class PortUnificationExchanger {
private static final Logger log = LoggerFactory.getLogger(PortUnificationExchanger.class);
- private static final ConcurrentMap<String, PortUnificationServer> servers = new ConcurrentHashMap<>();
+ private static final ConcurrentMap<String, RemotingServer> servers = new ConcurrentHashMap<>();
- public static void bind(URL url) {
+ public static RemotingServer bind(URL url, ChannelHandler handler) {
servers.computeIfAbsent(url.getAddress(), addr -> {
- final PortUnificationServer server = new PortUnificationServer(url);
- server.bind();
+ final AbstractPortUnificationServer server;
+ try {
+ server = getTransporter(url).bind(url, handler);
+ } catch (RemotingException e) {
+ throw new RuntimeException(e);
+ }
+ // server.bind();
return server;
});
+
+ servers.computeIfPresent(url.getAddress(), (addr, server) -> {
+ ((AbstractPortUnificationServer) server).addSupportedProtocol(url, handler);
+ return server;
+ });
+ return servers.get(url.getAddress());
}
public static void close() {
- final ArrayList<PortUnificationServer> toClose = new ArrayList<>(servers.values());
+ final ArrayList<RemotingServer> toClose = new ArrayList<>(servers.values());
servers.clear();
- for (PortUnificationServer server : toClose) {
+ for (RemotingServer server : toClose) {
try {
server.close();
} catch (Throwable throwable) {
@@ -51,7 +66,13 @@
}
// for test
- public static ConcurrentMap<String, PortUnificationServer> getServers() {
+ public static ConcurrentMap<String, RemotingServer> getServers() {
return servers;
}
+
+ public static PortUnificationTransporter getTransporter(URL url) {
+ return url.getOrDefaultFrameworkModel().getExtensionLoader(PortUnificationTransporter.class)
+ .getAdaptiveExtension();
+ }
+
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchanger.java
index 90f7c98..67f2440 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchanger.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchanger.java
@@ -23,8 +23,11 @@
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import org.apache.dubbo.remoting.exchange.ExchangeServer;
import org.apache.dubbo.remoting.exchange.Exchanger;
+import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.apache.dubbo.remoting.transport.DecodeHandler;
+import static org.apache.dubbo.remoting.Constants.IS_PU_SERVER_KEY;
+
/**
* DefaultMessenger
*
@@ -41,7 +44,14 @@
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
- return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
+ ExchangeServer server;
+ boolean isPuServerKey = url.getParameter(IS_PU_SERVER_KEY, false);
+ if(isPuServerKey) {
+ server = new HeaderExchangeServer(PortUnificationExchanger.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
+ }else {
+ server = new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
+ }
+ return server;
}
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
index 8d46767..946362e 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
@@ -27,6 +27,8 @@
import org.apache.dubbo.remoting.telnet.TelnetHandler;
import org.apache.dubbo.remoting.telnet.support.Help;
import org.apache.dubbo.remoting.telnet.support.TelnetUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
import java.util.ArrayList;
import java.util.HashMap;
@@ -43,10 +45,10 @@
@Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.")
public class StatusTelnetHandler implements TelnetHandler {
- private final ExtensionLoader<StatusChecker> extensionLoader = ExtensionLoader.getExtensionLoader(StatusChecker.class);
-
@Override
public String telnet(Channel channel, String message) {
+ ApplicationModel applicationModel = ScopeModelUtil.getApplicationModel(channel.getUrl().getScopeModel());
+ ExtensionLoader<StatusChecker> extensionLoader = applicationModel.getExtensionLoader(StatusChecker.class);
if ("-l".equals(message)) {
List<StatusChecker> checkers = extensionLoader.getActivateExtension(channel.getUrl(), STATUS_KEY);
String[] header = new String[]{"resource", "status", "message"};
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannel.java
index dee60da..2835326 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannel.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractChannel.java
@@ -44,4 +44,9 @@
public String toString() {
return getLocalAddress() + " -> " + getRemoteAddress();
}
+
+ @Override
+ protected void setUrl(URL url) {
+ super.setUrl(url);
+ }
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
index fbba378..4e0af74 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
@@ -56,9 +56,11 @@
FrameworkModel frameworkModel = getFrameworkModel(url.getScopeModel());
if (frameworkModel.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
return frameworkModel.getExtensionLoader(Codec2.class).getExtension(codecName);
- } else {
+ } else if(frameworkModel.getExtensionLoader(Codec.class).hasExtension(codecName)){
return new CodecAdapter(frameworkModel.getExtensionLoader(Codec.class)
.getExtension(codecName));
+ }else {
+ return frameworkModel.getExtensionLoader(Codec2.class).getExtension("default");
}
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java
index a963f2c..5f27886 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java
@@ -24,6 +24,7 @@
import org.apache.dubbo.common.serialize.ObjectInput;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -83,7 +84,7 @@
public static Serialization getSerialization(URL url) {
return url.getOrDefaultFrameworkModel().getExtensionLoader(Serialization.class).getExtension(
- url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+ url.getParameter(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()));
}
public static Serialization getSerialization(URL url, Byte id) throws IOException {
@@ -168,7 +169,7 @@
} else {
boolean match = false;
for (URL url : urls) {
- String serializationName = url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ String serializationName = url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
Byte localId = SERIALIZATIONNAME_ID_MAP.get(serializationName);
if (localId != null && localId.equals(id)) {
match = true;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
index 4f0936d..a82a634 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
@@ -75,15 +75,19 @@
}
protected void sendFeedback(Channel channel, Request request, Throwable t) throws RemotingException {
- if (request.isTwoWay()) {
- String msg = "Server side(" + url.getIp() + "," + url.getPort()
- + ") thread pool is exhausted, detail msg:" + t.getMessage();
- Response response = new Response(request.getId(), request.getVersion());
- response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
- response.setErrorMessage(msg);
- channel.send(response);
+
+ if (!request.isTwoWay()) {
return;
}
+
+ String msg = "Server side(" + url.getIp() + "," + url.getPort()
+ + ") thread pool is exhausted, detail msg:" + t.getMessage();
+
+ Response response = new Response(request.getId(), request.getVersion());
+ response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
+ response.setErrorMessage(msg);
+
+ channel.send(response);
}
@Override
@@ -140,12 +144,16 @@
// note: url.getOrDefaultApplicationModel() may create new application model
ApplicationModel applicationModel = url.getOrDefaultApplicationModel();
+
ExecutorRepository executorRepository =
applicationModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+
ExecutorService executor = executorRepository.getExecutor(url);
+
if (executor == null) {
executor = executorRepository.createExecutorIfAbsent(url);
}
+
return executor;
}
@@ -154,5 +162,4 @@
return getSharedExecutorService();
}
-
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java
index fdb6d88..bb001da 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/zookeeper/ZookeeperClient.java
@@ -22,10 +22,24 @@
import java.util.List;
import java.util.concurrent.Executor;
+/**
+ * Common abstraction of Zookeeper client.
+ */
public interface ZookeeperClient {
+ /**
+ * Create ZNode in Zookeeper.
+ *
+ * @param path path to ZNode
+ * @param ephemeral specify create mode of ZNode creation. true - EPHEMERAL, false - PERSISTENT.
+ */
void create(String path, boolean ephemeral);
+ /**
+ * Delete ZNode.
+ *
+ * @param path path to ZNode
+ */
void delete(String path);
List<String> getChildren(String path);
@@ -33,18 +47,28 @@
List<String> addChildListener(String path, ChildListener listener);
/**
- * @param path: directory. All child of path will be listened.
- * @param listener
+ * Attach data listener to current Zookeeper client.
+ *
+ * @param path directory. All children of path will be listened.
+ * @param listener The data listener object.
*/
void addDataListener(String path, DataListener listener);
/**
- * @param path: directory. All child of path will be listened.
- * @param listener
- * @param executor another thread
+ * Attach data listener to current Zookeeper client. The listener will be executed using the given executor.
+ *
+ * @param path directory. All children of path will be listened.
+ * @param listener The data listener object.
+ * @param executor the executor that will execute the listener.
*/
void addDataListener(String path, DataListener listener, Executor executor);
+ /**
+ * Detach data listener.
+ *
+ * @param path directory. All listener of children of the path will be detached.
+ * @param listener The data listener object.
+ */
void removeDataListener(String path, DataListener listener);
void removeChildListener(String path, ChildListener listener);
@@ -53,16 +77,37 @@
void removeStateListener(StateListener listener);
+ /**
+ * Check the Zookeeper client whether connected to server or not.
+ *
+ * @return true if connected
+ */
boolean isConnected();
+ /**
+ * Close connection to Zookeeper server (cluster).
+ */
void close();
URL getUrl();
+ /**
+ * Create ZNode in Zookeeper with content specified.
+ *
+ * @param path path to ZNode
+ * @param content the content of ZNode
+ * @param ephemeral specify create mode of ZNode creation. true - EPHEMERAL, false - PERSISTENT.
+ */
void create(String path, String content, boolean ephemeral);
void createOrUpdate(String path, String content, boolean ephemeral, int ticket);
+ /**
+ * Obtain the content of a ZNode.
+ *
+ * @param path path to ZNode
+ * @return content of ZNode
+ */
String getContent(String path);
ConfigItem getConfigItem(String path);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2
index b4ba960..98046f8 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2
@@ -1,3 +1,4 @@
transport=org.apache.dubbo.remoting.transport.codec.TransportCodec
telnet=org.apache.dubbo.remoting.telnet.codec.TelnetCodec
-exchange=org.apache.dubbo.remoting.exchange.codec.ExchangeCodec
\ No newline at end of file
+exchange=org.apache.dubbo.remoting.exchange.codec.ExchangeCodec
+default=org.apache.dubbo.remoting.api.pu.DefaultCodec
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/ChanelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/ChanelHandlerTest.java
index 4d78c7d..0045415 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/ChanelHandlerTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/ChanelHandlerTest.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
@@ -81,7 +82,7 @@
}
final String server = System.getProperty("server", "127.0.0.1:9911");
final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
- final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT);
int sleep = PerformanceUtils.getIntProperty("sleep", 60 * 1000 * 60);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientCloseTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientCloseTest.java
index e7f53f6..f81272a 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientCloseTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientCloseTest.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.remoting.exchange.Exchangers;
@@ -45,7 +46,7 @@
}
final String server = System.getProperty("server", "127.0.0.1:9911");
final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
- final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT);
final int concurrent = PerformanceUtils.getIntProperty("concurrent", 1);
final int runs = PerformanceUtils.getIntProperty("runs", Integer.MAX_VALUE);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientFixedTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientFixedTest.java
index 0b1a04e..82f0140 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientFixedTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientFixedTest.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.remoting.exchange.Exchangers;
@@ -43,7 +44,7 @@
}
final String server = System.getProperty("server", "127.0.0.1:9911");
final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
- final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT);
//final int length = PerformanceUtils.getIntProperty("length", 1024);
final int connectionCount = PerformanceUtils.getIntProperty(CONNECTIONS_KEY, 1);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientTest.java
index 67a1676..d5cffb2 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceClientTest.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.exchange.ExchangeClient;
import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
@@ -55,7 +56,7 @@
}
final String server = System.getProperty("server", "127.0.0.1:9911");
final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
- final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
final int timeout = PerformanceUtils.getIntProperty(TIMEOUT_KEY, DEFAULT_TIMEOUT);
final int length = PerformanceUtils.getIntProperty("length", 1024);
final int connections = PerformanceUtils.getIntProperty(CONNECTIONS_KEY, 1);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java
index 08e26c9..11e378d 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/PerformanceServerTest.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.exchange.ExchangeChannel;
import org.apache.dubbo.remoting.exchange.ExchangeServer;
import org.apache.dubbo.remoting.exchange.Exchangers;
@@ -68,7 +69,7 @@
private static ExchangeServer statServer() throws Exception {
final int port = PerformanceUtils.getIntProperty("port", 9911);
final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
- final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization());
final String threadpool = PerformanceUtils.getProperty(THREADPOOL_KEY, DEFAULT_THREADPOOL);
final int threads = PerformanceUtils.getIntProperty(THREADS_KEY, DEFAULT_THREADS);
final int iothreads = PerformanceUtils.getIntProperty(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/ConnectionTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/ConnectionTest.java
index 9f46576..5b6bd48 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/ConnectionTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/ConnectionTest.java
@@ -17,14 +17,11 @@
package org.apache.dubbo.remoting.api;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.NetUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
class ConnectionTest {
@@ -59,67 +56,4 @@
Assertions.assertEquals(0, latch.getCount());
}
- @Test
- public void connectSyncTest() throws Throwable {
- int port = NetUtils.getAvailablePort();
- URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
- PortUnificationServer server = null;
- try {
- server = new PortUnificationServer(url);
- server.bind();
-
- Connection connection = new Connection(url);
- Assertions.assertTrue(connection.isAvailable());
-
- server.close();
- Assertions.assertFalse(connection.isAvailable());
-
- server.bind();
- // auto reconnect
- Assertions.assertTrue(connection.isAvailable());
-
- connection.close();
- Assertions.assertFalse(connection.isAvailable());
- } finally {
- try {
- server.close();
- } catch (Throwable e) {
- // ignored
- }
- }
-
-
- }
-
- @Test
- public void testMultiConnect() throws Throwable {
- int port = NetUtils.getAvailablePort();
- URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
- PortUnificationServer server = null;
- try {
- server = new PortUnificationServer(url);
- server.close();
-
- Connection connection = new Connection(url);
- ExecutorService service = Executors.newFixedThreadPool(10);
- final CountDownLatch latch = new CountDownLatch(10);
- for (int i = 0; i < 10; i++) {
- Runnable runnable = () -> {
- try {
- Assertions.assertTrue(connection.isAvailable());
- latch.countDown();
- } catch (Exception e) {
- // ignore
- }
- };
- service.execute(runnable);
- }
- } finally {
- try {
- server.close();
- } catch (Throwable e) {
- // ignored
- }
- }
- }
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/EmptyProtocol.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/EmptyProtocol.java
index 7d3b5cf..5c69d9b 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/EmptyProtocol.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/EmptyProtocol.java
@@ -17,6 +17,7 @@
package org.apache.dubbo.remoting.api;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.SslContext;
@@ -28,7 +29,7 @@
}
@Override
- public void configServerPipeline(URL url, ChannelPipeline pipeline,SslContext sslContext) {
+ public void configServerProtocolHandler(URL url, ChannelOperator operator) {
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBuffersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBuffersTest.java
index e6811f2..9e71910 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBuffersTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/buffer/ChannelBuffersTest.java
@@ -41,6 +41,15 @@
}
@Test
+ public void testPrefixEquals(){
+ ChannelBuffer bufA = ChannelBuffers.wrappedBuffer("abcedfaf".getBytes());
+ ChannelBuffer bufB = ChannelBuffers.wrappedBuffer("abcedfaa".getBytes());
+ Assertions.assertTrue(ChannelBuffers.equals(bufA, bufB));
+ Assertions.assertTrue(ChannelBuffers.prefixEquals(bufA, bufB, 7));
+ Assertions.assertFalse(ChannelBuffers.prefixEquals(bufA, bufB, 8));
+ }
+
+ @Test
public void testBuffer() {
ChannelBuffer channelBuffer = ChannelBuffers.buffer(DEFAULT_CAPACITY);
Assertions.assertTrue(channelBuffer instanceof HeapChannelBuffer);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
index 495092d..fd130bb 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
@@ -23,6 +23,7 @@
import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
@@ -31,6 +32,7 @@
import org.apache.dubbo.remoting.exchange.Response;
import org.apache.dubbo.remoting.exchange.codec.ExchangeCodec;
import org.apache.dubbo.remoting.telnet.codec.TelnetCodec;
+import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -43,6 +45,7 @@
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT;
+import static org.apache.dubbo.common.serialize.Constants.FASTJSON2_SERIALIZATION_ID;
/**
*
@@ -64,7 +67,8 @@
private static final short MAGIC = (short) 0xdabb;
private static final byte MAGIC_HIGH = (byte) Bytes.short2bytes(MAGIC)[0];
private static final byte MAGIC_LOW = (byte) Bytes.short2bytes(MAGIC)[1];
- Serialization serialization = getSerialization(Constants.DEFAULT_REMOTING_SERIALIZATION);
+ Serialization serialization = getSerialization(DefaultSerializationSelector.getDefaultRemotingSerialization());
+ private static final byte SERIALIZATION_BYTE = FrameworkModel.defaultModel().getExtension(Serialization.class, DefaultSerializationSelector.getDefaultRemotingSerialization()).getContentTypeId();
private static Serialization getSerialization(String name) {
Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
@@ -137,7 +141,7 @@
@Test
public void test_Decode_Error_Length() throws IOException {
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 0x02, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
@@ -153,7 +157,7 @@
@Test
public void test_Decode_Error_Response_Object() throws IOException {
//00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 0x02, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
//bad object
@@ -227,7 +231,7 @@
@Test
public void test_Decode_Return_Response_Person() throws IOException {
//00000010-response/oneway/hearbeat=false/hessian |20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
@@ -239,7 +243,7 @@
@Test //The status input has a problem, and the read information is wrong when the serialization is serialized.
public void test_Decode_Return_Response_Error() throws IOException {
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 2, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
String errorString = "encode request data error ";
byte[] request = getRequestBytes(errorString, header);
Response obj = (Response) decode(request);
@@ -250,7 +254,7 @@
@Test
public void test_Decode_Return_Request_Event_Object() throws IOException {
//|10011111|20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
@@ -267,7 +271,7 @@
@Test
public void test_Decode_Return_Request_Event_String() throws IOException {
//|10011111|20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
String event = READONLY_EVENT;
byte[] request = getRequestBytes(event, header);
@@ -282,7 +286,7 @@
@Test
public void test_Decode_Return_Request_Heartbeat_Object() throws IOException {
//|10011111|20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
byte[] request = getRequestBytes(null, header);
Request obj = (Request) decode(request);
Assertions.assertNull(obj.getData());
@@ -295,22 +299,24 @@
@Test
public void test_Decode_Return_Request_Object() throws IOException {
//|10011111|20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xc2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
+ System.setProperty("deserialization.event.size", "100");
Request obj = (Request) decode(request);
Assertions.assertEquals(person, obj.getData());
Assertions.assertTrue(obj.isTwoWay());
Assertions.assertFalse(obj.isHeartbeat());
Assertions.assertEquals(Version.getProtocolVersion(), obj.getVersion());
System.out.println(obj);
+ System.clearProperty("deserialization.event.size");
}
@Test
public void test_Decode_Error_Request_Object() throws IOException {
//00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0xe2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) (SERIALIZATION_BYTE | (byte) 0xe0), 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
//bad object
@@ -325,7 +331,7 @@
@Test
public void test_Header_Response_NoSerializationFlag() throws IOException {
//00000010-response/oneway/hearbeat=false/noset |20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, (byte) 0x02, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
@@ -338,7 +344,7 @@
@Test
public void test_Header_Response_Heartbeat() throws IOException {
//00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
- byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, 0x02, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, SERIALIZATION_BYTE, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Person person = new Person();
byte[] request = getRequestBytes(person, header);
@@ -471,7 +477,8 @@
codec.encode(channel, encodeBuffer, request);
Assertions.fail();
} catch (IOException e) {
- Assertions.assertTrue(e.getMessage().startsWith("Data length too large: " + 6));
+ Assertions.assertTrue(e.getMessage().startsWith("Data length too large: "));
+ Assertions.assertTrue(e.getMessage().contains("max payload: 4, channel: org.apache.dubbo.remoting.codec.AbstractMockChannel"));
}
Response response = new Response(1L);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/CodecSupportTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/CodecSupportTest.java
index f3e86a6..b8ab5e4 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/CodecSupportTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/CodecSupportTest.java
@@ -18,6 +18,8 @@
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
+import org.apache.dubbo.remoting.Constants;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -31,7 +33,7 @@
@Test
public void testHeartbeat() throws Exception {
- Byte proto = CodecSupport.getIDByName("hessian2");
+ Byte proto = CodecSupport.getIDByName(DefaultSerializationSelector.getDefaultRemotingSerialization());
Serialization serialization = CodecSupport.getSerializationById(proto);
byte[] nullBytes = CodecSupport.getNullBytesOf(serialization);
diff --git a/dubbo-remoting/dubbo-remoting-netty/pom.xml b/dubbo-remoting/dubbo-remoting-netty/pom.xml
index d048f5b..5d7cefa 100644
--- a/dubbo-remoting/dubbo-remoting-netty/pom.xml
+++ b/dubbo-remoting/dubbo-remoting-netty/pom.xml
@@ -45,5 +45,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyClient.java b/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyClient.java
index 4a163ae..f5e0dc6 100644
--- a/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyClient.java
+++ b/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyClient.java
@@ -18,7 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.NetUtils;
@@ -39,12 +39,15 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_CLIENT_CONNECT_TIMEOUT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CONNECT_PROVIDER;
+
/**
* NettyClient.
*/
public class NettyClient extends AbstractClient {
- private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NettyClient.class);
// ChannelFactory's closure has a DirectMemory leak, using static to avoid
// https://issues.jboss.org/browse/NETTY-424
@@ -121,13 +124,26 @@
}
}
} else if (future.getCause() != null) {
- throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
- + getRemoteAddress() + ", error message is:" + future.getCause().getMessage(), future.getCause());
+ Throwable cause = future.getCause();
+
+ RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ + getRemoteAddress() + ", error message is:" + cause.getMessage(), cause);
+
+ // 6-1 - Failed to connect to provider server by other reason.
+ logger.error(TRANSPORT_FAILED_CONNECT_PROVIDER, "network disconnected", "", "Failed to connect to provider server by other reason.", cause);
+
+ throw remotingException;
} else {
- throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+
+ RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ getRemoteAddress() + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
+
+ // 6-2 - Client-side timeout.
+ logger.error(TRANSPORT_CLIENT_CONNECT_TIMEOUT, "provider crash", "", "Client-side timeout.", remotingException);
+
+ throw remotingException;
}
} finally {
if (!isConnected()) {
diff --git a/dubbo-remoting/dubbo-remoting-netty4/pom.xml b/dubbo-remoting/dubbo-remoting-netty4/pom.xml
index 99dd144..af48662 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/pom.xml
+++ b/dubbo-remoting/dubbo-remoting-netty4/pom.xml
@@ -50,5 +50,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
index 780ae9d..7022146 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
@@ -94,6 +94,8 @@
if (ret == null) {
ret = nettyChannel;
}
+ } else {
+ ret.markActive(true);
}
return ret;
}
@@ -231,6 +233,7 @@
attributes.remove(key);
}
+
@Override
public int hashCode() {
final int prime = 31;
@@ -240,6 +243,11 @@
}
@Override
+ protected void setUrl(URL url) {
+ super.setUrl(url);
+ }
+
+ @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
@@ -273,4 +281,7 @@
return "NettyChannel [channel=" + channel + "]";
}
+ public Channel getNioChannel() {
+ return channel;
+ }
}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
index 7298c48..f664e8e 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
@@ -16,11 +16,11 @@
*/
package org.apache.dubbo.remoting.transport.netty4;
-import io.netty.util.concurrent.EventExecutorGroup;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.resource.GlobalResourceInitializer;
import org.apache.dubbo.common.utils.NetUtils;
@@ -42,11 +42,14 @@
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.proxy.Socks5ProxyHandler;
import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.concurrent.EventExecutorGroup;
import java.net.InetSocketAddress;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_CLIENT_CONNECT_TIMEOUT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CONNECT_PROVIDER;
import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT;
import static org.apache.dubbo.remoting.api.NettyEventLoopFactory.eventLoopGroup;
import static org.apache.dubbo.remoting.api.NettyEventLoopFactory.socketChannelClass;
@@ -62,7 +65,7 @@
private static final String DEFAULT_SOCKS_PROXY_PORT = "1080";
- private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NettyClient.class);
/**
* netty client bootstrap
@@ -149,8 +152,33 @@
@Override
protected void doConnect() throws Throwable {
+ try {
+ String ipv6Address = NetUtils.getLocalHostV6();
+ InetSocketAddress connectAddress;
+ //first try ipv6 address
+ if (ipv6Address != null && getUrl().getParameter(CommonConstants.IPV6_KEY) != null) {
+ connectAddress = new InetSocketAddress(getUrl().getParameter(CommonConstants.IPV6_KEY), getUrl().getPort());
+ try {
+ doConnect(connectAddress);
+ return;
+ } catch (Throwable throwable) {
+ //ignore
+ }
+ }
+
+ connectAddress = getConnectAddress();
+ doConnect(connectAddress);
+ } finally {
+ // just add new valid channel to NettyChannel's cache
+ if (!isConnected()) {
+ //future.cancel(true);
+ }
+ }
+ }
+
+ private void doConnect(InetSocketAddress serverAddress) throws RemotingException {
long start = System.currentTimeMillis();
- ChannelFuture future = bootstrap.connect(getConnectAddress());
+ ChannelFuture future = bootstrap.connect(serverAddress);
try {
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
@@ -186,13 +214,32 @@
}
}
} else if (future.cause() != null) {
- throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
- + getRemoteAddress() + ", error message is:" + future.cause().getMessage(), future.cause());
+
+ Throwable cause = future.cause();
+
+ // 6-1 Failed to connect to provider server by other reason.
+
+ RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ + serverAddress + ", error message is:" + cause.getMessage(), cause);
+
+ logger.error(TRANSPORT_FAILED_CONNECT_PROVIDER, "network disconnected", "",
+ "Failed to connect to provider server by other reason.", cause);
+
+ throw remotingException;
+
} else {
- throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
- + getRemoteAddress() + " client-side timeout "
+
+ // 6-2 Client-side timeout
+
+ RemotingException remotingException = new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ + serverAddress + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
+
+ logger.error(TRANSPORT_CLIENT_CONNECT_TIMEOUT, "provider crash", "",
+ "Client-side timeout.", remotingException);
+
+ throw remotingException;
}
} finally {
// just add new valid channel to NettyChannel's cache
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConfigOperator.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConfigOperator.java
new file mode 100644
index 0000000..dca821e
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyConfigOperator.java
@@ -0,0 +1,97 @@
+/*
+ * 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.remoting.transport.netty4;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.Codec;
+import org.apache.dubbo.remoting.Codec2;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.api.pu.ChannelHandlerPretender;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
+import org.apache.dubbo.remoting.api.pu.DefaultCodec;
+import org.apache.dubbo.remoting.transport.codec.CodecAdapter;
+
+import java.util.List;
+
+public class NettyConfigOperator implements ChannelOperator {
+
+ private final Channel channel;
+ private ChannelHandler handler;
+
+ public NettyConfigOperator(NettyChannel channel, ChannelHandler handler) {
+ this.channel = channel;
+ this.handler = handler;
+ }
+
+ @Override
+ public void configChannelHandler(List<ChannelHandler> handlerList) {
+ URL url = channel.getUrl();
+ Codec2 codec2;
+ String codecName = url.getParameter(Constants.CODEC_KEY);
+ if (StringUtils.isEmpty(codecName)) {
+ // codec extension name must stay the same with protocol name
+ codecName = url.getProtocol();
+ }
+ if (url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).hasExtension(codecName)) {
+ codec2 = url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).getExtension(codecName);
+ } else if(url.getOrDefaultFrameworkModel().getExtensionLoader(Codec.class).hasExtension(codecName)){
+ codec2 = new CodecAdapter(url.getOrDefaultFrameworkModel().getExtensionLoader(Codec.class)
+ .getExtension(codecName));
+ }else {
+ codec2 = url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).getExtension("default");
+ }
+
+ if (!(codec2 instanceof DefaultCodec)){
+ NettyCodecAdapter codec = new NettyCodecAdapter(codec2, channel.getUrl(), handler);
+ ((NettyChannel) channel).getNioChannel().pipeline().addLast(
+ codec.getDecoder()
+ ).addLast(
+ codec.getEncoder()
+ );
+ }
+
+ for (ChannelHandler handler: handlerList) {
+ if (handler instanceof ChannelHandlerPretender) {
+ Object realHandler = ((ChannelHandlerPretender) handler).getRealHandler();
+ if(realHandler instanceof io.netty.channel.ChannelHandler) {
+ ((NettyChannel) channel).getNioChannel().pipeline().addLast(
+ (io.netty.channel.ChannelHandler) realHandler
+ );
+ }
+ }
+ }
+
+ // todo distinguish between client and server channel
+ if( isClientSide(channel)){
+ //todo config client channel handler
+ }else {
+ NettyServerHandler sh = new NettyServerHandler(channel.getUrl(), handler);
+ ((NettyChannel) channel).getNioChannel().pipeline().addLast(
+ sh
+ );
+ }
+ }
+
+ private boolean isClientSide(Channel channel) {
+ return channel.getUrl().getSide("").equalsIgnoreCase(CommonConstants.CONSUMER);
+ }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java
similarity index 66%
rename from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServer.java
rename to dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java
index 51125bd..6a3b926 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServer.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java
@@ -14,33 +14,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.ExecutorUtil;
+import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.api.NettyEventLoopFactory;
+import org.apache.dubbo.remoting.api.SslContexts;
+import org.apache.dubbo.remoting.api.WireProtocol;
+import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer;
+import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
-import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
-import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetSocketAddress;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY;
@@ -53,14 +60,9 @@
/**
* PortUnificationServer.
*/
-public class PortUnificationServer {
+public class NettyPortUnificationServer extends AbstractPortUnificationServer {
- private static final Logger logger = LoggerFactory.getLogger(PortUnificationServer.class);
- private final List<WireProtocol> protocols;
- private final URL url;
-
- private final DefaultChannelGroup channels = new DefaultChannelGroup(
- GlobalEventExecutor.INSTANCE);
+ private static final Logger logger = LoggerFactory.getLogger(NettyPortUnificationServer.class);
private final int serverShutdownTimeoutMills;
/**
@@ -70,41 +72,42 @@
/**
* the boss channel that receive connections and dispatch these to worker channel.
*/
- private Channel channel;
+ private io.netty.channel.Channel channel;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
+ private final Map<String, Channel> dubboChannels = new ConcurrentHashMap<>();
- public PortUnificationServer(URL url) {
+
+ public NettyPortUnificationServer(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, ChannelHandlers.wrap(handler, url));
+
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
- this.url = ExecutorUtil.setThreadName(url, "DubboPUServerHandler");
- this.protocols = ExtensionLoader.getExtensionLoader(WireProtocol.class)
- .getActivateExtension(url, new String[0]);
// read config before destroy
serverShutdownTimeoutMills = ConfigurationUtils.getServerShutdownTimeout(
getUrl().getOrDefaultModuleModel());
}
- public URL getUrl() {
- return url;
+ @Override
+ public void addSupportedProtocol(URL url, ChannelHandler handler) {
+ super.addSupportedProtocol(url, ChannelHandlers.wrap(handler, url));
}
- public void bind() {
- if (channel == null) {
- doOpen();
- }
- }
-
- public void close() throws Throwable {
+ @Override
+ public void close() {
if (channel != null) {
doClose();
}
}
- /**
- * Init and start netty server
- */
- protected void doOpen() {
+ public void bind(){
+ if(channel == null) {
+ doOpen();
+ }
+ }
+
+ @Override
+ public void doOpen() {
bootstrap = new ServerBootstrap();
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, EVENT_LOOP_BOSS_POOL_NAME);
@@ -115,7 +118,7 @@
final boolean enableSsl = getUrl().getParameter(SSL_ENABLED_KEY, false);
final SslContext sslContext;
if (enableSsl) {
- sslContext = SslContexts.buildServerSslContext(url);
+ sslContext = SslContexts.buildServerSslContext(getUrl());
} else {
sslContext = null;
}
@@ -129,9 +132,10 @@
protected void initChannel(SocketChannel ch) throws Exception {
// Do not add idle state handler here, because it should be added in the protocol handler.
final ChannelPipeline p = ch.pipeline();
- final PortUnificationServerHandler puHandler;
- puHandler = new PortUnificationServerHandler(url, sslContext, true, protocols,
- channels);
+ final NettyPortUnificationServerHandler puHandler;
+ puHandler = new NettyPortUnificationServerHandler(getUrl(), sslContext, true, getProtocols(),
+ NettyPortUnificationServer.this, NettyPortUnificationServer.this.dubboChannels,
+ getSupportedUrls(), getSupportedHandlers());
p.addLast("negotiation-protocol", puHandler);
}
});
@@ -139,7 +143,7 @@
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
- if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
+ if (getUrl().getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
bindIp = ANYHOST_VALUE;
}
InetSocketAddress bindAddress = new InetSocketAddress(bindIp, bindPort);
@@ -148,8 +152,8 @@
channel = channelFuture.channel();
}
- protected void doClose() throws Throwable {
- final long st = System.currentTimeMillis();
+ @Override
+ public void doClose(){
try {
if (channel != null) {
@@ -158,14 +162,26 @@
channel = null;
}
- channels.close().await(serverShutdownTimeoutMills);
- final long cost = System.currentTimeMillis() - st;
- logger.info("Port unification server closed. cost:" + cost);
- } catch (InterruptedException e) {
+ } catch (Throwable e) {
logger.warn("Interrupted while shutting down", e);
}
- for (WireProtocol protocol : protocols) {
+ try {
+ Collection<Channel> channels = getChannels();
+ if (CollectionUtils.isNotEmpty(channels)) {
+ for (Channel channel : channels) {
+ try {
+ channel.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+
+ for (WireProtocol protocol : getProtocols()) {
protocol.close();
}
@@ -189,8 +205,25 @@
return channel.isActive();
}
+ @Override
+ public Collection<Channel> getChannels() {
+ Collection<Channel> chs = new ArrayList<>(this.dubboChannels.size());
+ chs.addAll(this.dubboChannels.values());
+ return chs;
+ }
+
+ @Override
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return dubboChannels.get(NetUtils.toAddressString(remoteAddress));
+ }
+
public InetSocketAddress getLocalAddress() {
return (InetSocketAddress) channel.localAddress();
}
+ @Override
+ public boolean canHandleIdle() {
+ return true;
+ }
+
}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java
new file mode 100644
index 0000000..e6fcb1a
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.io.Bytes;
+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.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.api.WireProtocol;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandler;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class NettyPortUnificationServerHandler extends ByteToMessageDecoder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(
+ NettyPortUnificationServerHandler.class);
+
+ private final SslContext sslCtx;
+ private final URL url;
+ private final ChannelHandler handler;
+ private final boolean detectSsl;
+ private final List<WireProtocol> protocols;
+ private final Map<String, Channel> dubboChannels;
+ private final Map<String, URL> urlMapper;
+ private final Map<String, ChannelHandler> handlerMapper;
+
+
+ public NettyPortUnificationServerHandler(URL url, SslContext sslCtx, boolean detectSsl,
+ List<WireProtocol> protocols, ChannelHandler handler,
+ Map<String, Channel> dubboChannels, Map<String, URL> urlMapper, Map<String, ChannelHandler> handlerMapper) {
+ this.url = url;
+ this.sslCtx = sslCtx;
+ this.protocols = protocols;
+ this.detectSsl = detectSsl;
+ this.handler = handler;
+ this.dubboChannels = dubboChannels;
+ this.urlMapper = urlMapper;
+ this.handlerMapper = handlerMapper;
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ LOGGER.error("Unexpected exception from downstream before protocol detected.", cause);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
+ if (channel != null) {
+ // this is needed by some test cases
+ dubboChannels.put(NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
+ }
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
+ throws Exception {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
+ // Will use the first five bytes to detect a protocol.
+ // size of telnet command ls is 2 bytes
+ if (in.readableBytes() < 2) {
+ return;
+ }
+
+ if (isSsl(in)) {
+ enableSsl(ctx);
+ } else {
+ for (final WireProtocol protocol : protocols) {
+ in.markReaderIndex();
+ ChannelBuffer buf = new NettyBackedChannelBuffer(in);
+ final ProtocolDetector.Result result = protocol.detector().detect(buf);
+ in.resetReaderIndex();
+ switch (result) {
+ case UNRECOGNIZED:
+ continue;
+ case RECOGNIZED:
+ String protocolName = url.getOrDefaultFrameworkModel().getExtensionLoader(WireProtocol.class)
+ .getExtensionName(protocol);
+ ChannelHandler localHandler = this.handlerMapper.getOrDefault(protocolName, handler);
+ URL localURL = this.urlMapper.getOrDefault(protocolName, url);
+ channel.setUrl(localURL);
+ NettyConfigOperator operator = new NettyConfigOperator(channel, localHandler);
+ protocol.configServerProtocolHandler(url, operator);
+ ctx.pipeline().remove(this);
+ case NEED_MORE_DATA:
+ return;
+ default:
+ return;
+ }
+ }
+ byte[] preface = new byte[in.readableBytes()];
+ in.readBytes(preface);
+ Set<String> supported = url.getApplicationModel()
+ .getExtensionLoader(WireProtocol.class)
+ .getSupportedExtensions();
+ LOGGER.error(String.format("Can not recognize protocol from downstream=%s . "
+ + "preface=%s protocols=%s", ctx.channel().remoteAddress(),
+ Bytes.bytes2hex(preface),
+ supported));
+
+ // Unknown protocol; discard everything and close the connection.
+ in.clear();
+ ctx.close();
+ }
+ }
+
+ private void enableSsl(ChannelHandlerContext ctx) {
+ ChannelPipeline p = ctx.pipeline();
+ p.addLast("ssl", sslCtx.newHandler(ctx.alloc()));
+ p.addLast("unificationA",
+ new NettyPortUnificationServerHandler(url, sslCtx, false, protocols,
+ handler, dubboChannels, urlMapper, handlerMapper));
+ p.remove(this);
+ }
+
+ private boolean isSsl(ByteBuf buf) {
+ // at least 5 bytes to determine if data is encrypted
+ if (detectSsl && buf.readableBytes() >= 5) {
+ return SslHandler.isEncrypted(buf);
+ }
+ return false;
+ }
+
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationTransporter.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationTransporter.java
new file mode 100644
index 0000000..b13dc0f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationTransporter.java
@@ -0,0 +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.remoting.transport.netty4;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.Client;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer;
+import org.apache.dubbo.remoting.api.pu.PortUnificationTransporter;
+
+public class NettyPortUnificationTransporter implements PortUnificationTransporter {
+
+ public static final String NAME = "netty";
+
+ @Override
+ public AbstractPortUnificationServer bind(URL url, ChannelHandler handler) throws RemotingException {
+ return new NettyPortUnificationServer(url, handler);
+ }
+
+ @Override
+ public Client connect(URL url, ChannelHandler handler) throws RemotingException {
+ return null;
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
index 8eaeb94..7ea3733 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
@@ -95,6 +95,8 @@
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
handler.received(channel, msg);
+ // trigger qos handler
+ ctx.fireChannelRead(msg);
}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter b/dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter
new file mode 100644
index 0000000..69a9814
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.pu.PortUnificationTransporter
@@ -0,0 +1 @@
+netty=org.apache.dubbo.remoting.transport.netty4.NettyPortUnificationTransporter
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java
new file mode 100644
index 0000000..55aa550
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ConnectionTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.remoting.transport.netty4;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.api.Connection;
+import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class ConnectionTest {
+ @Test
+ public void connectSyncTest() throws Throwable {
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
+ NettyPortUnificationServer server = null;
+ try {
+ server = new NettyPortUnificationServer(url, new DefaultPuHandler());
+ server.bind();
+
+ Connection connection = new Connection(url);
+ Assertions.assertTrue(connection.isAvailable());
+
+ server.close();
+ Assertions.assertFalse(connection.isAvailable());
+
+ server.bind();
+ // auto reconnect
+ Assertions.assertTrue(connection.isAvailable());
+
+ connection.close();
+ Assertions.assertFalse(connection.isAvailable());
+ } finally {
+ try {
+ server.close();
+ } catch (Throwable e) {
+ // ignored
+ }
+ }
+
+
+ }
+
+ @Test
+ public void testMultiConnect() throws Throwable {
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
+ NettyPortUnificationServer server = null;
+ try {
+ server = new NettyPortUnificationServer(url, new DefaultPuHandler());
+ server.close();
+
+ Connection connection = new Connection(url);
+ ExecutorService service = Executors.newFixedThreadPool(10);
+ final CountDownLatch latch = new CountDownLatch(10);
+ for (int i = 0; i < 10; i++) {
+ Runnable runnable = () -> {
+ try {
+ Assertions.assertTrue(connection.isAvailable());
+ latch.countDown();
+ } catch (Exception e) {
+ // ignore
+ }
+ };
+ service.execute(runnable);
+ }
+ } finally {
+ try {
+ server.close();
+ } catch (Throwable e) {
+ // ignored
+ }
+ }
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DefaultCodec.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DefaultCodec.java
new file mode 100644
index 0000000..9f33bfa
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/DefaultCodec.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.Codec2;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+
+import java.io.IOException;
+
+public class DefaultCodec implements Codec2 {
+ @Override
+ public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
+
+ }
+
+ @Override
+ public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
+ return null;
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/EmptyWireProtocol.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/EmptyWireProtocol.java
new file mode 100644
index 0000000..9482287
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/EmptyWireProtocol.java
@@ -0,0 +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.remoting.transport.netty4;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.api.WireProtocol;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
+
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.ssl.SslContext;
+
+public class EmptyWireProtocol implements WireProtocol {
+ @Override
+ public ProtocolDetector detector() {
+ return null;
+ }
+
+ @Override
+ public void configServerProtocolHandler(URL url, ChannelOperator operator) {
+
+ }
+
+ @Override
+ public void configClientPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java
similarity index 71%
rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java
rename to dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java
index a842cbb..2fd8232 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationExchangerTest.java
@@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.exchange;
+package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.url.component.ServiceConfigURL;
-import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
+import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -28,10 +28,10 @@
@Test
public void test() {
- URL url = new ServiceConfigURL(CommonConstants.TRIPLE, "localhost", 9103,
- new String[]{Constants.BIND_PORT_KEY, String.valueOf(9103)});
- PortUnificationExchanger.bind(url);
- PortUnificationExchanger.bind(url);
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
+ PortUnificationExchanger.bind(url, new DefaultPuHandler());
+ PortUnificationExchanger.bind(url, new DefaultPuHandler());
Assertions.assertEquals(PortUnificationExchanger.getServers().size(), 1);
PortUnificationExchanger.close();
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java
similarity index 64%
rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java
rename to dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java
index 92bcfb5..23fdcb9 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/PortUnificationServerTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/PortUnificationServerTest.java
@@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.url.component.ServiceConfigURL;
-import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -27,11 +27,12 @@
public class PortUnificationServerTest {
@Test
- public void testBind() {
- URL url = new ServiceConfigURL(CommonConstants.TRIPLE, "localhost", 8898,
- new String[]{Constants.BIND_PORT_KEY, String.valueOf(8898)});
+ public void testBind() throws RemotingException {
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("empty://127.0.0.1:" + port + "?foo=bar");
- final PortUnificationServer server = new PortUnificationServer(url);
+ // abstract endpoint need to get codec of url(which is in triple package)
+ final NettyPortUnificationServer server = new NettyPortUnificationServer(url, new DefaultPuHandler());
server.bind();
Assertions.assertTrue(server.isBound());
}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2 b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2
new file mode 100644
index 0000000..58df523
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2
@@ -0,0 +1 @@
+empty=org.apache.dubbo.remoting.transport.netty4.DefaultCodec
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
new file mode 100644
index 0000000..74a5075
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
@@ -0,0 +1 @@
+empty=org.apache.dubbo.remoting.transport.netty4.EmptyWireProtocol
diff --git a/dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClient.java
index 52c1d9f..a3c719f 100644
--- a/dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClient.java
+++ b/dubbo-remoting/dubbo-remoting-zookeeper-curator5/src/main/java/org/apache/dubbo/remoting/zookeeper/curator5/Curator5ZookeeperClient.java
@@ -18,7 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigItem;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.remoting.zookeeper.AbstractZookeeperClient;
import org.apache.dubbo.remoting.zookeeper.ChildListener;
@@ -55,11 +55,12 @@
import static org.apache.dubbo.common.constants.CommonConstants.SESSION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY;
public class Curator5ZookeeperClient extends AbstractZookeeperClient<Curator5ZookeeperClient.NodeCacheListenerImpl, Curator5ZookeeperClient.CuratorWatcherImpl> {
- protected static final Logger logger = LoggerFactory.getLogger(Curator5ZookeeperClient.class);
+ protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(Curator5ZookeeperClient.class);
private static final Charset CHARSET = StandardCharsets.UTF_8;
private final CuratorFramework client;
@@ -94,9 +95,17 @@
client.getConnectionStateListenable().addListener(new CuratorConnectionStateListener(url));
client.start();
boolean connected = client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS);
+
if (!connected) {
- throw new IllegalStateException("zookeeper not connected");
+ IllegalStateException illegalStateException = new IllegalStateException("zookeeper not connected");
+
+ // 5-1 Failed to connect to configuration center.
+ logger.error(CONFIG_FAILED_CONNECT_REGISTRY, "Zookeeper server offline", "",
+ "Failed to connect with zookeeper", illegalStateException);
+
+ throw illegalStateException;
}
+
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
diff --git a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java
index 13a0018..7a98cce 100644
--- a/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java
+++ b/dubbo-remoting/dubbo-remoting-zookeeper/src/main/java/org/apache/dubbo/remoting/zookeeper/curator/CuratorZookeeperClient.java
@@ -18,7 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.configcenter.ConfigItem;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.StringUtils;
@@ -59,11 +59,12 @@
import static org.apache.dubbo.common.constants.CommonConstants.SESSION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_FAILED_CONNECT_REGISTRY;
public class CuratorZookeeperClient extends AbstractZookeeperClient<CuratorZookeeperClient.NodeCacheListenerImpl, CuratorZookeeperClient.CuratorWatcherImpl> {
- protected static final Logger logger = LoggerFactory.getLogger(CuratorZookeeperClient.class);
+ protected static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CuratorZookeeperClient.class);
private static final Charset CHARSET = StandardCharsets.UTF_8;
private final CuratorFramework client;
@@ -97,10 +98,18 @@
client = builder.build();
client.getConnectionStateListenable().addListener(new CuratorConnectionStateListener(url));
client.start();
+
boolean connected = client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS);
if (!connected) {
- throw new IllegalStateException("zookeeper not connected");
+ IllegalStateException illegalStateException = new IllegalStateException("zookeeper not connected");
+
+ // 5-1 Failed to connect to configuration center.
+ logger.error(CONFIG_FAILED_CONNECT_REGISTRY, "Zookeeper server offline", "",
+ "Failed to connect with zookeeper", illegalStateException);
+
+ throw illegalStateException;
}
+
CuratorWatcherImpl.closed = false;
} catch (Exception e) {
close();
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Protocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Protocol.java
index 838801d..0bbc24f 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Protocol.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Protocol.java
@@ -25,7 +25,35 @@
import java.util.List;
/**
- * Protocol. (API/SPI, Singleton, ThreadSafe)
+ * RPC Protocol extension interface, which encapsulates the details of remote invocation. <br /><br />
+ *
+ * <p>Conventions:
+ *
+ * <li>
+ * When user invokes the 'invoke()' method in object that the method 'refer()' returns,
+ * the protocol needs to execute the 'invoke()' method of Invoker object that received by 'export()' method,
+ * which should have the same URL.
+ * </li>
+ *
+ * <li>
+ * Invoker that returned by 'refer()' is implemented by the protocol. The remote invocation request should be sent by that Invoker.
+ * </li>
+ *
+ * <li>
+ * The invoker that 'export()' receives will be implemented by framework. Protocol implementation should not care with that.
+ * </li>
+ *
+ * <p>Attentions:
+ *
+ * <li>
+ * The Protocol implementation does not care the transparent proxy. The invoker will be converted to business interface by other layer.
+ * </li>
+ *
+ * <li>
+ * The protocol doesn't need to be backed by TCP connection. It can also be backed by file sharing or inter-process communication.
+ * </li>
+ *
+ * (API/SPI, Singleton, ThreadSafe)
*/
@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
index 93d5f2b..deb7d52 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/ContextFilter.java
@@ -97,7 +97,9 @@
RpcContext context = RpcContext.getServerAttachment();
// .setAttachments(attachments) // merged from dubbox
- context.setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
+ if (context.getLocalAddress() == null) {
+ context.setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
+ }
String remoteApplication = invocation.getAttachment(REMOTE_APPLICATION_KEY);
if (StringUtils.isNotEmpty(remoteApplication)) {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
index a09e285..75dcd5c 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
@@ -97,7 +97,7 @@
String generic = inv.getAttachment(GENERIC_KEY);
if (StringUtils.isBlank(generic)) {
- generic = RpcContext.getClientAttachment().getAttachment(GENERIC_KEY);
+ generic = getGenericValueFromRpcContext();
}
if (StringUtils.isEmpty(generic)
@@ -205,6 +205,14 @@
}).toArray();
}
+ private String getGenericValueFromRpcContext(){
+ String generic = RpcContext.getServerAttachment().getAttachment(GENERIC_KEY);
+ if (StringUtils.isBlank(generic)){
+ generic = RpcContext.getClientAttachment().getAttachment(GENERIC_KEY);
+ }
+ return generic;
+ }
+
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation inv) {
if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
@@ -214,7 +222,7 @@
String generic = inv.getAttachment(GENERIC_KEY);
if (StringUtils.isBlank(generic)) {
- generic = RpcContext.getClientAttachment().getAttachment(GENERIC_KEY);
+ generic = getGenericValueFromRpcContext();
}
if (appResponse.hasException()) {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerExporterWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerExporterWrapper.java
index df3497f..021af67 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerExporterWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerExporterWrapper.java
@@ -24,6 +24,7 @@
import org.apache.dubbo.rpc.Invoker;
import java.util.List;
+import java.util.function.Consumer;
/**
* ListenerExporter
@@ -42,22 +43,7 @@
}
this.exporter = exporter;
this.listeners = listeners;
- if (CollectionUtils.isNotEmpty(listeners)) {
- RuntimeException exception = null;
- for (ExporterListener listener : listeners) {
- if (listener != null) {
- try {
- listener.exported(this);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
- }
+ listenerEvent(listener -> listener.exported(this));
}
@Override
@@ -70,23 +56,26 @@
try {
exporter.unexport();
} finally {
- if (CollectionUtils.isNotEmpty(listeners)) {
- RuntimeException exception = null;
- for (ExporterListener listener : listeners) {
- if (listener != null) {
- try {
- listener.unexported(this);
- } catch (RuntimeException t) {
- logger.error(t.getMessage(), t);
- exception = t;
- }
- }
- }
- if (exception != null) {
- throw exception;
- }
- }
+ listenerEvent(listener -> listener.unexported(this));
}
}
+ private void listenerEvent(Consumer<ExporterListener> consumer) {
+ if (CollectionUtils.isNotEmpty(listeners)) {
+ RuntimeException exception = null;
+ for (ExporterListener listener : listeners) {
+ if (listener != null) {
+ try {
+ consumer.accept(listener);
+ } catch (RuntimeException t) {
+ logger.error(t.getMessage(), t);
+ exception = t;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
index 14254c7..2addb81 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/listener/ListenerInvokerWrapper.java
@@ -27,6 +27,7 @@
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
+import java.util.function.Consumer;
/**
* ListenerInvoker
@@ -45,19 +46,10 @@
}
this.invoker = invoker;
this.listeners = listeners;
- if (CollectionUtils.isNotEmpty(listeners)) {
- for (InvokerListener listener : listeners) {
- if (listener != null) {
- try {
- listener.referred(invoker);
- } catch (Throwable t) {
- logger.error(t.getMessage(), t);
- }
- }
- }
- }
+ listenerEvent(listener -> listener.referred(invoker));
}
+
@Override
public Class<T> getInterface() {
return invoker.getInterface();
@@ -88,17 +80,7 @@
try {
invoker.destroy();
} finally {
- if (CollectionUtils.isNotEmpty(listeners)) {
- for (InvokerListener listener : listeners) {
- if (listener != null) {
- try {
- listener.destroyed(invoker);
- } catch (Throwable t) {
- logger.error(t.getMessage(), t);
- }
- }
- }
- }
+ listenerEvent(listener -> listener.destroyed(invoker));
}
}
@@ -109,4 +91,23 @@
public List<InvokerListener> getListeners() {
return listeners;
}
+
+ private void listenerEvent(Consumer<InvokerListener> consumer) {
+ if (CollectionUtils.isNotEmpty(listeners)) {
+ RuntimeException exception = null;
+ for (InvokerListener listener : listeners) {
+ if (listener != null) {
+ try {
+ consumer.accept(listener);
+ } catch (RuntimeException t) {
+ logger.error(t.getMessage(), t);
+ exception = t;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index e1582fe..be56015 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -22,6 +22,7 @@
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.common.threadpool.ThreadlessExecutor;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.common.utils.ArrayUtils;
@@ -50,7 +51,6 @@
import java.util.concurrent.TimeUnit;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
-import static org.apache.dubbo.remoting.Constants.DEFAULT_REMOTING_SERIALIZATION;
import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY;
import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
@@ -196,7 +196,7 @@
RpcUtils.attachInvocationIdIfAsync(getUrl(), inv);
- Byte serializationId = CodecSupport.getIDByName(getUrl().getParameter(SERIALIZATION_KEY, DEFAULT_REMOTING_SERIALIZATION));
+ Byte serializationId = CodecSupport.getIDByName(getUrl().getParameter(SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()));
if (serializationId != null) {
inv.put(SERIALIZATION_ID_KEY, serializationId);
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
index 9088cce..12d48f5 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
@@ -19,7 +19,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.bytecode.Proxy;
import org.apache.dubbo.common.bytecode.Wrapper;
-import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;
@@ -33,7 +33,7 @@
* JavassistRpcProxyFactory
*/
public class JavassistProxyFactory extends AbstractProxyFactory {
- private final static Logger logger = LoggerFactory.getLogger(JavassistProxyFactory.class);
+ private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(JavassistProxyFactory.class);
private final JdkProxyFactory jdkProxyFactory = new JdkProxyFactory();
@Override
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubInvocationUtil.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubInvocationUtil.java
index 4e88fb7..cb52327 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubInvocationUtil.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubInvocationUtil.java
@@ -33,7 +33,13 @@
public static <T, R> void unaryCall(Invoker<?> invoker, MethodDescriptor method, T request,
StreamObserver<R> responseObserver) {
- call(invoker, method, new Object[]{request, responseObserver});
+ try {
+ Object res = unaryCall(invoker, method, request);
+ responseObserver.onNext((R) res);
+ } catch (Exception e) {
+ responseObserver.onError(e);
+ }
+ responseObserver.onCompleted();
}
public static <T, R> StreamObserver<T> biOrClientStreamCall(Invoker<?> invoker,
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java
index 7549692..aee142a 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/AppResponseTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.rpc;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -39,9 +40,7 @@
@Test
public void testAppResponseWithEmptyStackTraceException() {
Throwable throwable = buildEmptyStackTraceException();
- if (throwable == null) {
- return;
- }
+ assumeFalse(throwable == null);
AppResponse appResponse = new AppResponse(throwable);
StackTraceElement[] stackTrace = appResponse.getException().getStackTrace();
@@ -66,9 +65,7 @@
@Test
public void testSetExceptionWithEmptyStackTraceException() {
Throwable throwable = buildEmptyStackTraceException();
- if (throwable == null) {
- return;
- }
+ assumeFalse(throwable == null);
AppResponse appResponse = new AppResponse();
appResponse.setException(throwable);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubInvocationUtilTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubInvocationUtilTest.java
index 476911f..e5196f3 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubInvocationUtilTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/stub/StubInvocationUtilTest.java
@@ -136,13 +136,8 @@
Result result = Mockito.mock(Result.class);
String response = "response";
when(invoker.invoke(any(Invocation.class)))
- .then(invocationOnMock -> {
- Invocation invocation = (Invocation) invocationOnMock.getArguments()[0];
- StreamObserver<Object> observer = (StreamObserver<Object>) invocation.getArguments()[1];
- observer.onNext(response);
- observer.onCompleted();
- return result;
- });
+ .then(invocationOnMock -> result);
+ when(result.recreate()).thenReturn(response);
MethodDescriptor method = Mockito.mock(MethodDescriptor.class);
when(method.getParameterClasses())
.thenReturn(new Class[]{String.class});
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/pom.xml b/dubbo-rpc/dubbo-rpc-dubbo/pom.xml
index 42a0196..0570e62 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-dubbo/pom.xml
@@ -64,6 +64,12 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java
index e422685..d0914c0 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodecSupport.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.transport.CodecSupport;
import org.apache.dubbo.rpc.AppResponse;
@@ -34,7 +35,7 @@
return CodecSupport.getSerializationById((byte) serializationTypeObj);
}
return url.getOrDefaultFrameworkModel().getExtensionLoader(Serialization.class).getExtension(
- url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+ url.getParameter(org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()));
}
public static Serialization getResponseSerialization(URL url, AppResponse appResponse) {
@@ -47,6 +48,6 @@
}
}
return url.getOrDefaultFrameworkModel().getExtensionLoader(Serialization.class).getExtension(
- url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+ url.getParameter(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()));
}
}
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index b2ccfbe..c4e238c 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -33,6 +33,7 @@
import org.apache.dubbo.remoting.exchange.ExchangeHandler;
import org.apache.dubbo.remoting.exchange.ExchangeServer;
import org.apache.dubbo.remoting.exchange.Exchangers;
+import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invocation;
@@ -334,6 +335,7 @@
String key = url.getAddress();
// client can export a service which only for server to invoke
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
+
if (isServer) {
ProtocolServer server = serverMap.get(key);
if (server == null) {
@@ -653,6 +655,7 @@
}
}
}
+ PortUnificationExchanger.close();
referenceClientMap.clear();
super.destroy();
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboDetector.java
similarity index 60%
copy from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java
copy to dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboDetector.java
index ae5e4c6..88d146c 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboDetector.java
@@ -14,32 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+package org.apache.dubbo.rpc.protocol.dubbo.pu;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.http2.Http2CodecUtil;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ByteBufferBackedChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffers;
+
+import java.nio.ByteBuffer;
import static java.lang.Math.min;
-public class Http2ProtocolDetector implements ProtocolDetector {
- private final ByteBuf clientPrefaceString = Http2CodecUtil.connectionPrefaceBuf();
+public class DubboDetector implements ProtocolDetector {
+ private final ChannelBuffer Preface = new ByteBufferBackedChannelBuffer(
+ ByteBuffer.wrap(new byte[]{(byte)0xda, (byte)0xbb})
+ );
@Override
- public Result detect(ChannelHandlerContext ctx, ByteBuf in) {
- int prefaceLen = clientPrefaceString.readableBytes();
+ public Result detect(ChannelBuffer in) {
+ int prefaceLen = Preface.readableBytes();
int bytesRead = min(in.readableBytes(), prefaceLen);
- // If the input so far doesn't match the preface, break the connection.
- if (bytesRead == 0 || !ByteBufUtil.equals(in, 0,
- clientPrefaceString, 0, bytesRead)) {
-
+ if (bytesRead ==0 || !ChannelBuffers.prefixEquals(in, Preface, bytesRead)) {
return Result.UNRECOGNIZED;
}
if (bytesRead == prefaceLen) {
return Result.RECOGNIZED;
}
+
return Result.NEED_MORE_DATA;
}
}
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboWireProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboWireProtocol.java
new file mode 100644
index 0000000..ba83394
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/pu/DubboWireProtocol.java
@@ -0,0 +1,46 @@
+/*
+ * 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.protocol.dubbo.pu;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.api.AbstractWireProtocol;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Activate
+public class DubboWireProtocol extends AbstractWireProtocol {
+ public DubboWireProtocol() {
+ super(new DubboDetector());
+ }
+
+
+ @Override
+ public void configServerProtocolHandler(URL url, ChannelOperator operator) {
+ List<ChannelHandler> handlers = new ArrayList<>();
+ // operator(for now nettyOperator)'s duties
+ // 1. config codec2 for the protocol(load by extension loader)
+ // 2. config handlers passed by wire protocol
+ // ( for triple, some h2 netty handler and logic handler to handle connection;
+ // for dubbo, nothing, an empty handlers is used to trigger operator logic)
+ // 3. config Dubbo Inner handler(for dubbo protocol, this handler handles connection)
+ operator.configChannelHandler(handlers);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
new file mode 100644
index 0000000..06f029b
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.api.WireProtocol
@@ -0,0 +1 @@
+dubbo=org.apache.dubbo.rpc.protocol.dubbo.pu.DubboWireProtocol
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java
index 0bc522a..d3b0316 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocationTest.java
@@ -19,9 +19,11 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import org.apache.dubbo.remoting.buffer.ChannelBufferInputStream;
import org.apache.dubbo.remoting.buffer.ChannelBufferOutputStream;
@@ -61,7 +63,7 @@
inv.setObjectAttachment("k2", "v2");
inv.setTargetServiceUniqueName(url.getServiceKey());
// Write the data of inv to the buffer
- Byte proto = CodecSupport.getIDByName("hessian2");
+ Byte proto = CodecSupport.getIDByName(DefaultSerializationSelector.getDefaultRemotingSerialization());
ChannelBuffer buffer = writeBuffer(url, inv, proto);
FrameworkModel frameworkModel = new FrameworkModel();
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResultTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResultTest.java
index 3c7ea54..04dc51f 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResultTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResultTest.java
@@ -20,9 +20,11 @@
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.common.url.component.ServiceConfigURL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import org.apache.dubbo.remoting.buffer.ChannelBufferInputStream;
import org.apache.dubbo.remoting.buffer.ChannelBufferOutputStream;
@@ -76,7 +78,7 @@
@Test
public void test() throws Exception {
// Mock a rpcInvocation, this rpcInvocation is usually generated by the client request, and stored in Request#data
- Byte proto = CodecSupport.getIDByName("hessian2");
+ Byte proto = CodecSupport.getIDByName(DefaultSerializationSelector.getDefaultRemotingSerialization());
URL url = new ServiceConfigURL("dubbo", "127.0.0.1", 9103, DemoService.class.getName(), VERSION_KEY, "1.0.0");
ServiceDescriptor serviceDescriptor = repository.registerService(DemoService.class);
ProviderModel providerModel = new ProviderModel(url.getServiceKey(), new DemoServiceImpl(), serviceDescriptor, null, null);
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
index b531bb1..a069227 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCountCodecTest.java
@@ -42,7 +42,7 @@
@Test
public void test() throws Exception {
DubboCountCodec dubboCountCodec = new DubboCountCodec(FrameworkModel.defaultModel());
- ChannelBuffer buffer = ChannelBuffers.buffer(1024);
+ ChannelBuffer buffer = ChannelBuffers.buffer(2048);
Channel channel = new MockChannel();
Assertions.assertEquals(Codec2.DecodeResult.NEED_MORE_INPUT, dubboCountCodec.decode(channel, buffer));
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
index d9760e4..8097285 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocolTest.java
@@ -42,6 +42,7 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -199,6 +200,7 @@
}
@Test
+ @Disabled
public void testNonSerializedParameter() throws Exception {
DemoService service = new DemoServiceImpl();
int port = NetUtils.getAvailablePort();
@@ -214,6 +216,7 @@
}
@Test
+ @Disabled
public void testReturnNonSerialized() throws Exception {
DemoService service = new DemoServiceImpl();
int port = NetUtils.getAvailablePort();
diff --git a/dubbo-rpc/dubbo-rpc-injvm/pom.xml b/dubbo-rpc/dubbo-rpc-injvm/pom.xml
index 3346ed0..4f5f9ad 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-injvm/pom.xml
@@ -46,5 +46,11 @@
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java
index 8e3f6ca..232b586 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/DefaultParamDeepCopyUtil.java
@@ -22,6 +22,7 @@
import org.apache.dubbo.common.serialize.ObjectInput;
import org.apache.dubbo.common.serialize.ObjectOutput;
import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.remoting.Constants;
import java.io.ByteArrayInputStream;
@@ -37,7 +38,7 @@
@SuppressWarnings({"unchecked"})
public <T> T copy(URL url, Object src, Class<T> targetClass) {
Serialization serialization = url.getOrDefaultFrameworkModel().getExtensionLoader(Serialization.class).getExtension(
- url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+ url.getParameter(Constants.SERIALIZATION_KEY, DefaultSerializationSelector.getDefaultRemotingSerialization()));
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ObjectOutput objectOutput = serialization.serialize(url, outputStream);
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
index 18c08e8..972efa5 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/main/java/org/apache/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -221,7 +221,7 @@
if (pts != null && args != null && pts.length == args.length) {
realArgument = new Object[pts.length];
for (int i = 0; i < pts.length; i++) {
- realArgument[i] = paramDeepCopyUtil.copy(getUrl(), args[i], pts[i]);
+ realArgument[i] = paramDeepCopyUtil.copy(invoker.getUrl(), args[i], pts[i]);
}
}
if (realArgument == null) {
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/Empty.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/Empty.java
new file mode 100644
index 0000000..6a9678c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/demo/Empty.java
@@ -0,0 +1,20 @@
+/*
+ * 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 demo;
+
+public class Empty {
+}
diff --git a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java
index c91f505..765f689 100644
--- a/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java
+++ b/dubbo-rpc/dubbo-rpc-injvm/src/test/java/org/apache/dubbo/rpc/protocol/injvm/InjvmClassLoaderTest.java
@@ -32,11 +32,13 @@
import org.apache.dubbo.rpc.model.ProviderModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import demo.Empty;
import demo.MultiClassLoaderService;
import demo.MultiClassLoaderServiceImpl;
import demo.MultiClassLoaderServiceRequest;
import demo.MultiClassLoaderServiceResult;
import javassist.CannotCompileException;
+import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.junit.jupiter.api.Assertions;
@@ -128,20 +130,28 @@
applicationModel.destroy();
}
- private Class<?> compileCustomRequest(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
+ private Class<?> compileCustomRequest(ClassLoader classLoader) throws NotFoundException, CannotCompileException, ClassNotFoundException {
CtClassBuilder builder = new CtClassBuilder();
builder.setClassName(MultiClassLoaderServiceRequest.class.getName() + "A");
builder.setSuperClassName(MultiClassLoaderServiceRequest.class.getName());
CtClass cls = builder.build(classLoader);
- return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
+ ClassPool cp = cls.getClassPool();
+ if (classLoader == null) {
+ classLoader = cp.getClassLoader();
+ }
+ return cp.toClass(cls, classLoader.loadClass(Empty.class.getName()), classLoader, JavassistCompiler.class.getProtectionDomain());
}
- private Class<?> compileCustomResult(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
+ private Class<?> compileCustomResult(ClassLoader classLoader) throws NotFoundException, CannotCompileException, ClassNotFoundException {
CtClassBuilder builder = new CtClassBuilder();
builder.setClassName(MultiClassLoaderServiceResult.class.getName() + "A");
builder.setSuperClassName(MultiClassLoaderServiceResult.class.getName());
CtClass cls = builder.build(classLoader);
- return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
+ ClassPool cp = cls.getClassPool();
+ if (classLoader == null) {
+ classLoader = cp.getClassLoader();
+ }
+ return cp.toClass(cls, classLoader.loadClass(Empty.class.getName()), classLoader, JavassistCompiler.class.getProtectionDomain());
}
private static class TestClassLoader1 extends ClassLoader {
@@ -169,6 +179,9 @@
return loadedClass.get(name);
}
if (name.startsWith("demo")) {
+ if (name.equals(MultiClassLoaderServiceRequest.class.getName()) || name.equals(MultiClassLoaderServiceResult.class.getName())) {
+ return super.loadClass(name, resolve);
+ }
Class<?> aClass = this.findClass(name);
this.loadedClass.put(name, aClass);
if (resolve) {
@@ -217,7 +230,7 @@
byte[] bytes = loadClassData(name);
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
- throw new ClassNotFoundException();
+ return testClassLoader.loadClass(name, false);
}
}
@@ -226,7 +239,7 @@
if (loadedClass.containsKey(name)) {
return loadedClass.get(name);
}
- if (name.startsWith("demo.MultiClassLoaderServiceRe")) {
+ if (name.startsWith("demo.MultiClassLoaderServiceRe") || name.startsWith("demo.Empty")) {
Class<?> aClass = this.findClass(name);
this.loadedClass.put(name, aClass);
if (resolve) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml
index f2b08cd..17f141a 100644
--- a/dubbo-rpc/dubbo-rpc-triple/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml
@@ -30,7 +30,8 @@
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
<dubbo.compiler.version>0.0.4.1-SNAPSHOT</dubbo.compiler.version>
-
+ <reactive.version>1.0.4</reactive.version>
+ <reactor.version>3.4.19</reactor.version>
</properties>
<dependencies>
<dependency>
@@ -39,6 +40,12 @@
<version>${project.parent.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-remoting-netty4</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
@@ -54,9 +61,9 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>io.projectreactor</groupId>
- <artifactId>reactor-core</artifactId>
- <version>3.4.16</version>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -73,6 +80,12 @@
<groupId>org.xerial.snappy</groupId>
<artifactId>snappy-java</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-core</artifactId>
+ <version>${reactor.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserver.java
index 3a059bc..76ae697 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/CancelableStreamObserver.java
@@ -19,6 +19,7 @@
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.rpc.CancellationContext;
+import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter;
public abstract class CancelableStreamObserver<T> implements StreamObserver<T> {
@@ -28,8 +29,19 @@
this.cancellationContext = cancellationContext;
}
+ public CancellationContext getCancellationContext() {
+ return cancellationContext;
+ }
+
public void cancel(Throwable throwable) {
cancellationContext.cancel(throwable);
}
+ public void beforeStart(final ClientCallToObserverAdapter<T> clientCallToObserverAdapter) {
+ // do nothing
+ }
+
+ public void startRequest() {
+ // do nothing
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClientStreamObserver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClientStreamObserver.java
index b28499a..d20d2f5 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClientStreamObserver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ClientStreamObserver.java
@@ -29,6 +29,8 @@
* request()} may not be called before the call is started, a number of initial requests may be
* specified.
*/
- void disableAutoRequest();
+ default void disableAutoRequest() {
+ disableAutoFlowControl();
+ }
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetector.java
similarity index 70%
rename from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java
rename to dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetector.java
index ae5e4c6..9071ca4 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/Http2ProtocolDetector.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetector.java
@@ -14,27 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+package org.apache.dubbo.rpc.protocol.tri;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.ChannelHandlerContext;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ByteBufferBackedChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffers;
+
import io.netty.handler.codec.http2.Http2CodecUtil;
import static java.lang.Math.min;
public class Http2ProtocolDetector implements ProtocolDetector {
- private final ByteBuf clientPrefaceString = Http2CodecUtil.connectionPrefaceBuf();
+ private final ChannelBuffer clientPrefaceString = new ByteBufferBackedChannelBuffer(
+ Http2CodecUtil.connectionPrefaceBuf().nioBuffer());
@Override
- public Result detect(ChannelHandlerContext ctx, ByteBuf in) {
+ public Result detect(ChannelBuffer in) {
int prefaceLen = clientPrefaceString.readableBytes();
int bytesRead = min(in.readableBytes(), prefaceLen);
// If the input so far doesn't match the preface, break the connection.
- if (bytesRead == 0 || !ByteBufUtil.equals(in, 0,
- clientPrefaceString, 0, bytesRead)) {
-
+ if (bytesRead == 0 || !ChannelBuffers.prefixEquals(in, clientPrefaceString, bytesRead)) {
return Result.UNRECOGNIZED;
}
if (bytesRead == prefaceLen) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java
index 57b2edd..7ee9e0e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java
@@ -20,6 +20,7 @@
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.serialize.MultipleSerialization;
+import org.apache.dubbo.common.serialize.support.DefaultSerializationSelector;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.config.Constants;
import org.apache.dubbo.rpc.model.MethodDescriptor;
@@ -38,7 +39,6 @@
import static org.apache.dubbo.common.constants.CommonConstants.$ECHO;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOBUF_MESSAGE_CLASS_NAME;
-import static org.apache.dubbo.remoting.Constants.DEFAULT_REMOTING_SERIALIZATION;
import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY;
import static org.apache.dubbo.rpc.protocol.tri.TripleProtocol.METHOD_ATTR_PACK;
@@ -105,7 +105,7 @@
public static ReflectionPackableMethod init(MethodDescriptor methodDescriptor, URL url) {
final String serializeName = url.getParameter(SERIALIZATION_KEY,
- DEFAULT_REMOTING_SERIALIZATION);
+ DefaultSerializationSelector.getDefaultRemotingSerialization());
Object stored = methodDescriptor.getAttribute(METHOD_ATTR_PACK);
if (stored != null) {
return (ReflectionPackableMethod) stored;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServerStreamObserver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServerStreamObserver.java
index 3ca3f1d..9d3a9b2 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServerStreamObserver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ServerStreamObserver.java
@@ -21,7 +21,8 @@
public interface ServerStreamObserver<T> extends CallStreamObserver<T> {
-
- void disableAutoInboundFlowControl();
+ default void disableAutoInboundFlowControl() {
+ disableAutoFlowControl();
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
index 881303f..6fba538 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
@@ -23,7 +23,10 @@
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
-import org.apache.dubbo.remoting.api.Http2WireProtocol;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.api.AbstractWireProtocol;
+import org.apache.dubbo.remoting.api.pu.ChannelHandlerPretender;
+import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import org.apache.dubbo.rpc.HeaderFilter;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -39,10 +42,13 @@
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http2.Http2FrameCodec;
import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
+import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.codec.http2.Http2MultiplexHandler;
import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.SslContext;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -56,7 +62,7 @@
import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_MAX_HEADER_LIST_SIZE_KEY;
@Activate
-public class TripleHttp2Protocol extends Http2WireProtocol implements ScopeModelAware {
+public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeModelAware {
// 1 MiB
private static final int MIB_1 = 1 << 20;
@@ -67,11 +73,19 @@
private static final int DEFAULT_MAX_FRAME_SIZE = MIB_8;
private static final int DEFAULT_WINDOW_INIT_SIZE = MIB_8;
+ public static final Http2FrameLogger CLIENT_LOGGER = new Http2FrameLogger(LogLevel.DEBUG, "H2_CLIENT");
+
+ public static final Http2FrameLogger SERVER_LOGGER = new Http2FrameLogger(LogLevel.DEBUG, "H2_SERVER");
+
private ExtensionLoader<HeaderFilter> filtersLoader;
private FrameworkModel frameworkModel;
private Configuration config = ConfigurationUtils.getGlobalConfiguration(
ApplicationModel.defaultModel());
+ public TripleHttp2Protocol() {
+ super(new Http2ProtocolDetector());
+ }
+
@Override
public void setFrameworkModel(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
@@ -89,7 +103,8 @@
}
@Override
- public void configServerPipeline(URL url, ChannelPipeline pipeline, SslContext sslContext) {
+ public void configServerProtocolHandler(URL url, ChannelOperator operator) {
+
final List<HeaderFilter> headFilters;
if (filtersLoader != null) {
headFilters = filtersLoader.getActivateExtension(url,
@@ -120,8 +135,14 @@
headFilters));
}
});
- pipeline.addLast(codec, new TripleServerConnectionHandler(), handler,
- new TripleTailHandler());
+ List<ChannelHandler> handlers = new ArrayList<>();
+ handlers.add(new ChannelHandlerPretender(codec));
+ handlers.add(new ChannelHandlerPretender(new TripleServerConnectionHandler()));
+ handlers.add(new ChannelHandlerPretender(handler));
+ handlers.add(new ChannelHandlerPretender(new TripleTailHandler()));
+ operator.configChannelHandler(handlers);
+
+
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
index 2e9146c..16b4373 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
@@ -46,6 +46,7 @@
import org.apache.dubbo.rpc.protocol.tri.call.TripleClientCall;
import org.apache.dubbo.rpc.protocol.tri.call.UnaryClientCallListener;
import org.apache.dubbo.rpc.protocol.tri.compressor.Compressor;
+import org.apache.dubbo.rpc.protocol.tri.observer.ClientCallToObserverAdapter;
import org.apache.dubbo.rpc.support.RpcUtils;
import io.netty.util.AsciiString;
@@ -174,14 +175,18 @@
StreamObserver<Object> streamCall(ClientCall call,
RequestMetadata metadata,
StreamObserver<Object> responseObserver) {
- if (responseObserver instanceof CancelableStreamObserver) {
- final CancellationContext context = new CancellationContext();
- ((CancelableStreamObserver<Object>) responseObserver).setCancellationContext(context);
- context.addListener(context1 -> call.cancelByLocal(new IllegalStateException("Canceled by app")));
- }
ObserverToClientCallListenerAdapter listener = new ObserverToClientCallListenerAdapter(
responseObserver);
- return call.start(metadata, listener);
+ StreamObserver<Object> streamObserver = call.start(metadata, listener);
+ if (responseObserver instanceof CancelableStreamObserver) {
+ final CancellationContext context = new CancellationContext();
+ CancelableStreamObserver<Object> cancelableStreamObserver = (CancelableStreamObserver<Object>) responseObserver;
+ cancelableStreamObserver.setCancellationContext(context);
+ context.addListener(context1 -> call.cancelByLocal(new IllegalStateException("Canceled by app")));
+ listener.setOnStartConsumer(dummy -> cancelableStreamObserver.startRequest());
+ cancelableStreamObserver.beforeStart((ClientCallToObserverAdapter<Object>) streamObserver);
+ }
+ return streamObserver;
}
AsyncRpcResult invokeUnary(MethodDescriptor methodDescriptor, Invocation invocation,
@@ -196,35 +201,16 @@
RequestMetadata request = createRequest(methodDescriptor, invocation, timeout);
final Object pureArgument;
- if (methodDescriptor.getParameterClasses().length == 2
- && methodDescriptor.getParameterClasses()[1].isAssignableFrom(
- StreamObserver.class)) {
- StreamObserver<Object> observer = (StreamObserver<Object>) invocation.getArguments()[1];
- future.whenComplete((r, t) -> {
- if (t != null) {
- observer.onError(t);
- return;
- }
- if (r.hasException()) {
- observer.onError(r.getException());
- return;
- }
- observer.onNext(r.getValue());
- observer.onCompleted();
- });
+
+ if (methodDescriptor instanceof StubMethodDescriptor) {
pureArgument = invocation.getArguments()[0];
- result = new AsyncRpcResult(CompletableFuture.completedFuture(new AppResponse()),
- invocation);
} else {
- if (methodDescriptor instanceof StubMethodDescriptor) {
- pureArgument = invocation.getArguments()[0];
- } else {
- pureArgument = invocation.getArguments();
- }
- result = new AsyncRpcResult(future, invocation);
- result.setExecutor(callbackExecutor);
- FutureContext.getContext().setCompatibleFuture(future);
+ pureArgument = invocation.getArguments();
}
+ result = new AsyncRpcResult(future, invocation);
+ FutureContext.getContext().setCompatibleFuture(future);
+
+ result.setExecutor(callbackExecutor);
ClientCall.Listener callListener = new UnaryClientCallListener(future);
final StreamObserver<Object> requestObserver = call.start(request, callListener);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
index 7e5a5b6..a7b0683 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
@@ -18,11 +18,11 @@
package org.apache.dubbo.rpc.protocol.tri;
import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
import org.apache.dubbo.remoting.api.ConnectionManager;
+import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
@@ -48,20 +48,10 @@
import java.util.Set;
import java.util.concurrent.ExecutorService;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLIENT_THREADPOOL;
-import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY;
-
public class TripleProtocol extends AbstractProtocol {
public static final String METHOD_ATTR_PACK = "pack";
- private static final String CLIENT_THREAD_POOL_NAME = "DubboTriClientHandler";
- private static final URL THREAD_POOL_URL = new URL(CommonConstants.TRIPLE,
- CommonConstants.LOCALHOST_VALUE, 50051)
- .addParameter(THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME)
- .addParameterIfAbsent(THREADPOOL_KEY, DEFAULT_CLIENT_THREADPOOL);
-
private static final Logger logger = LoggerFactory.getLogger(TripleProtocol.class);
private final PathResolver pathResolver;
private final TriBuiltinService triBuiltinService;
@@ -117,12 +107,12 @@
.setStatus(url.getServiceKey(), HealthCheckResponse.ServingStatus.SERVING);
triBuiltinService.getHealthStatusManager()
.setStatus(url.getServiceInterface(), HealthCheckResponse.ServingStatus.SERVING);
-
// init
url.getOrDefaultApplicationModel().getExtensionLoader(ExecutorRepository.class)
.getDefaultExtension()
.createExecutorIfAbsent(url);
- PortUnificationExchanger.bind(url);
+
+ PortUnificationExchanger.bind(url, new DefaultPuHandler());
optimizeSerialization(url);
return exporter;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java
index 5fac300..1c934ce 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ObserverToClientCallListenerAdapter.java
@@ -21,16 +21,22 @@
import org.apache.dubbo.rpc.TriRpcStatus;
import java.util.Map;
+import java.util.function.Consumer;
public class ObserverToClientCallListenerAdapter implements ClientCall.Listener {
private final StreamObserver<Object> delegate;
private ClientCall call;
+ private Consumer<ClientCall> onStartConsumer = clientCall -> { };
public ObserverToClientCallListenerAdapter(StreamObserver<Object> delegate) {
this.delegate = delegate;
}
+ public void setOnStartConsumer(Consumer<ClientCall> onStartConsumer) {
+ this.onStartConsumer = onStartConsumer;
+ }
+
@Override
public void onMessage(Object message) {
delegate.onNext(message);
@@ -54,5 +60,7 @@
if (call.isAutoRequest()) {
call.request(1);
}
+
+ onStartConsumer.accept(call);
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/CallStreamObserver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/CallStreamObserver.java
index 8f0037f..362ab9e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/CallStreamObserver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/CallStreamObserver.java
@@ -43,5 +43,12 @@
*/
void setCompression(String compression);
+ /**
+ * Swaps to manual flow control where no message will be delivered to {@link
+ * StreamObserver#onNext(Object)} unless it is {@link #request request()}ed. Since {@code
+ * request()} may not be called before the call is started, a number of initial requests may be
+ * specified.
+ */
+ void disableAutoFlowControl();
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ClientCallToObserverAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ClientCallToObserverAdapter.java
index 8002d2e..37f1769 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ClientCallToObserverAdapter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ClientCallToObserverAdapter.java
@@ -76,7 +76,7 @@
}
@Override
- public void disableAutoRequest() {
+ public void disableAutoFlowControl() {
call.setAutoRequest(false);
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java
index 8329dda..18bb578 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java
@@ -47,7 +47,7 @@
}
- private boolean isTerminated() {
+ public boolean isTerminated() {
return terminated;
}
@@ -105,7 +105,7 @@
}
@Override
- public void disableAutoInboundFlowControl() {
+ public void disableAutoFlowControl() {
call.disableAutoRequestN();
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/Http2ProtocolDetectorTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetectorTest.java
similarity index 74%
rename from dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/Http2ProtocolDetectorTest.java
rename to dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetectorTest.java
index 702bf10..e336db6 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/api/Http2ProtocolDetectorTest.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/Http2ProtocolDetectorTest.java
@@ -14,7 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.remoting.api;
+package org.apache.dubbo.rpc.protocol.tri;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.api.ProtocolDetector;
+import org.apache.dubbo.remoting.buffer.ByteBufferBackedChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.apache.dubbo.remoting.buffer.ChannelBuffers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
@@ -36,16 +42,17 @@
ByteBuf connectionPrefaceBuf = Http2CodecUtil.connectionPrefaceBuf();
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
- ProtocolDetector.Result result = detector.detect(ctx, byteBuf);
+ ChannelBuffer in = new ByteBufferBackedChannelBuffer(byteBuf.nioBuffer());
+ ProtocolDetector.Result result = detector.detect(in);
Assertions.assertEquals(result, ProtocolDetector.Result.UNRECOGNIZED);
byteBuf.writeBytes(connectionPrefaceBuf);
- result = detector.detect(ctx, byteBuf);
+ result = detector.detect(new ByteBufferBackedChannelBuffer(byteBuf.nioBuffer()));
Assertions.assertEquals(result, ProtocolDetector.Result.RECOGNIZED);
byteBuf.clear();
byteBuf.writeBytes(connectionPrefaceBuf, 0, 1);
- result = detector.detect(ctx, byteBuf);
+ result = detector.detect(new ByteBufferBackedChannelBuffer(byteBuf.nioBuffer()));
Assertions.assertEquals(result, ProtocolDetector.Result.NEED_MORE_DATA);
}
diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Constants.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Constants.java
index 3715437..9dda745 100644
--- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Constants.java
+++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Constants.java
@@ -32,6 +32,7 @@
byte PROTOBUF_JSON_SERIALIZATION_ID = 21;
byte PROTOBUF_SERIALIZATION_ID = 22;
+ byte FASTJSON2_SERIALIZATION_ID = 23;
byte KRYO_SERIALIZATION2_ID = 25;
byte CUSTOM_MESSAGE_PACK_ID = 31;
}
diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Serialization.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Serialization.java
index bb67639..d0314c5 100644
--- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Serialization.java
+++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/Serialization.java
@@ -33,7 +33,7 @@
* e.g. <dubbo:protocol serialization="xxx" />
* </pre>
*/
-@SPI(value = "hessian2", scope = ExtensionScope.FRAMEWORK)
+@SPI(scope = ExtensionScope.FRAMEWORK)
public interface Serialization {
/**
diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/DefaultSerializationSelector.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/DefaultSerializationSelector.java
new file mode 100644
index 0000000..86c0825
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/support/DefaultSerializationSelector.java
@@ -0,0 +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.common.serialize.support;
+
+
+public class DefaultSerializationSelector {
+
+ private final static String DEFAULT_REMOTING_SERIALIZATION_PROPERTY_KEY = "DUBBO_DEFAULT_SERIALIZATION";
+
+ private final static String DEFAULT_REMOTING_SERIALIZATION_PROPERTY = "hessian2";
+
+ private final static String DEFAULT_REMOTING_SERIALIZATION;
+
+ static {
+ String fromProperty = System.getProperty(DEFAULT_REMOTING_SERIALIZATION_PROPERTY_KEY);
+ if (fromProperty != null) {
+ DEFAULT_REMOTING_SERIALIZATION = fromProperty;
+ } else {
+ String fromEnv = System.getenv(DEFAULT_REMOTING_SERIALIZATION_PROPERTY_KEY);
+ if (fromEnv != null) {
+ DEFAULT_REMOTING_SERIALIZATION = fromEnv;
+ } else {
+ DEFAULT_REMOTING_SERIALIZATION = DEFAULT_REMOTING_SERIALIZATION_PROPERTY;
+ }
+ }
+ }
+
+ public static String getDefaultRemotingSerialization() {
+ return DEFAULT_REMOTING_SERIALIZATION;
+ }
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml b/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml
new file mode 100644
index 0000000..683e099
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/pom.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The fastjson2 serialization module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba.fastjson2</groupId>
+ <artifactId>fastjson2</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java
new file mode 100644
index 0000000..10b14dd
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectInput.java
@@ -0,0 +1,152 @@
+/*
+ * 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.serialize.fastjson2;
+
+import org.apache.dubbo.common.serialize.ObjectInput;
+
+import com.alibaba.fastjson2.JSONB;
+import com.alibaba.fastjson2.JSONReader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+
+/**
+ * FastJson object input implementation
+ */
+public class FastJson2ObjectInput implements ObjectInput {
+
+ private final Fastjson2CreatorManager fastjson2CreatorManager;
+
+ private volatile ClassLoader classLoader;
+ private final InputStream is;
+
+ public FastJson2ObjectInput(Fastjson2CreatorManager fastjson2CreatorManager, InputStream in) {
+ this.fastjson2CreatorManager = fastjson2CreatorManager;
+ this.classLoader = Thread.currentThread().getContextClassLoader();
+ this.is = in;
+ fastjson2CreatorManager.setCreator(classLoader);
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ return readObject(boolean.class);
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ return readObject(byte.class);
+ }
+
+ @Override
+ public short readShort() throws IOException {
+ return readObject(short.class);
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ return readObject(int.class);
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ return readObject(long.class);
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return readObject(float.class);
+ }
+
+ @Override
+ public double readDouble() throws IOException {
+ return readObject(double.class);
+ }
+
+ @Override
+ public String readUTF() throws IOException {
+ return readObject(String.class);
+ }
+
+ @Override
+ public byte[] readBytes() throws IOException {
+ int length = is.read();
+ byte[] bytes = new byte[length];
+ int read = is.read(bytes, 0, length);
+ if (read != length) {
+ throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read);
+ }
+ return bytes;
+ }
+
+ @Override
+ public Object readObject() throws IOException, ClassNotFoundException {
+ return readObject(Object.class);
+ }
+
+ @Override
+ public <T> T readObject(Class<T> cls) throws IOException {
+ updateClassLoaderIfNeed();
+ int length = readLength();
+ byte[] bytes = new byte[length];
+ int read = is.read(bytes, 0, length);
+ if (read != length) {
+ throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read);
+ }
+ return (T) JSONB.parseObject(bytes, Object.class, JSONReader.Feature.SupportAutoType,
+ JSONReader.Feature.UseDefaultConstructorAsPossible,
+ JSONReader.Feature.UseNativeObject,
+ JSONReader.Feature.FieldBased);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(Class<T> cls, Type type) throws IOException, ClassNotFoundException {
+ updateClassLoaderIfNeed();
+ int length = readLength();
+ byte[] bytes = new byte[length];
+ int read = is.read(bytes, 0, length);
+ if (read != length) {
+ throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read);
+ }
+ return (T) JSONB.parseObject(bytes, Object.class, JSONReader.Feature.SupportAutoType,
+ JSONReader.Feature.UseDefaultConstructorAsPossible,
+ JSONReader.Feature.UseNativeObject,
+ JSONReader.Feature.FieldBased);
+ }
+
+ private void updateClassLoaderIfNeed() {
+ ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+ if (currentClassLoader != classLoader) {
+ fastjson2CreatorManager.setCreator(currentClassLoader);
+ classLoader = currentClassLoader;
+ }
+ }
+
+ private int readLength() throws IOException {
+ byte[] bytes = new byte[Integer.BYTES];
+ int read = is.read(bytes, 0, Integer.BYTES);
+ if (read != Integer.BYTES) {
+ throw new IllegalArgumentException("deserialize failed. expected read length: " + Integer.BYTES + " but actual read: " + read);
+ }
+ int value = 0;
+ for (byte b : bytes) {
+ value = (value << 8) + (b & 0xFF);
+ }
+ return value;
+ }
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java
new file mode 100644
index 0000000..fdf15ae
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2ObjectOutput.java
@@ -0,0 +1,134 @@
+/*
+ * 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.serialize.fastjson2;
+
+import org.apache.dubbo.common.serialize.ObjectOutput;
+
+import com.alibaba.fastjson2.JSONB;
+import com.alibaba.fastjson2.JSONWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * FastJson object output implementation
+ */
+public class FastJson2ObjectOutput implements ObjectOutput {
+
+ private final Fastjson2CreatorManager fastjson2CreatorManager;
+
+ private volatile ClassLoader classLoader;
+ private final OutputStream os;
+
+ public FastJson2ObjectOutput(Fastjson2CreatorManager fastjson2CreatorManager, OutputStream out) {
+ this.fastjson2CreatorManager = fastjson2CreatorManager;
+ this.classLoader = Thread.currentThread().getContextClassLoader();
+ this.os = out;
+ fastjson2CreatorManager.setCreator(classLoader);
+ }
+
+ @Override
+ public void writeBool(boolean v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeByte(byte v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeShort(short v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeInt(int v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeLong(long v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeFloat(float v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeDouble(double v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeUTF(String v) throws IOException {
+ writeObject(v);
+ }
+
+ @Override
+ public void writeBytes(byte[] b) throws IOException {
+ os.write(b.length);
+ os.write(b);
+ }
+
+ @Override
+ public void writeBytes(byte[] b, int off, int len) throws IOException {
+ os.write(len);
+ os.write(b, off, len);
+ }
+
+ @Override
+ public void writeObject(Object obj) throws IOException {
+ updateClassLoaderIfNeed();
+ byte[] bytes = JSONB.toBytes(obj, JSONWriter.Feature.WriteClassName,
+ JSONWriter.Feature.FieldBased,
+ JSONWriter.Feature.ReferenceDetection,
+ JSONWriter.Feature.WriteNulls,
+ JSONWriter.Feature.NotWriteDefaultValue,
+ JSONWriter.Feature.NotWriteHashMapArrayListClassName,
+ JSONWriter.Feature.WriteNameAsSymbol);
+ writeLength(bytes.length);
+ os.write(bytes);
+ os.flush();
+ }
+
+ private void updateClassLoaderIfNeed() {
+ ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+ if (currentClassLoader != classLoader) {
+ fastjson2CreatorManager.setCreator(currentClassLoader);
+ classLoader = currentClassLoader;
+ }
+ }
+
+ private void writeLength(int value) throws IOException {
+ byte[] bytes = new byte[Integer.BYTES];
+ int length = bytes.length;
+ for (int i = 0; i < length; i++) {
+ bytes[length - i - 1] = (byte) (value & 0xFF);
+ value >>= 8;
+ }
+ os.write(bytes);
+ }
+
+ @Override
+ public void flushBuffer() throws IOException {
+ os.flush();
+ }
+
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java
new file mode 100644
index 0000000..4c20abc
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/FastJson2Serialization.java
@@ -0,0 +1,66 @@
+/*
+ * 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.serialize.fastjson2;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.apache.dubbo.common.serialize.Constants.FASTJSON2_SERIALIZATION_ID;
+
+/**
+ * FastJson serialization implementation
+ *
+ * <pre>
+ * e.g. <dubbo:protocol serialization="fastjson" />
+ * </pre>
+ */
+public class FastJson2Serialization implements Serialization {
+
+ private final Fastjson2CreatorManager fastjson2CreatorManager;
+
+ public FastJson2Serialization(FrameworkModel frameworkModel) {
+ this.fastjson2CreatorManager = frameworkModel.getBeanFactory().getBean(Fastjson2CreatorManager.class);
+ }
+
+ @Override
+ public byte getContentTypeId() {
+ return FASTJSON2_SERIALIZATION_ID;
+ }
+
+ @Override
+ public String getContentType() {
+ return "text/jsonb";
+ }
+
+ @Override
+ public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
+ return new FastJson2ObjectOutput(fastjson2CreatorManager, output);
+ }
+
+ @Override
+ public ObjectInput deserialize(URL url, InputStream input) throws IOException {
+ return new FastJson2ObjectInput(fastjson2CreatorManager, input);
+ }
+
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2CreatorManager.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2CreatorManager.java
new file mode 100644
index 0000000..8168aee
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2CreatorManager.java
@@ -0,0 +1,61 @@
+/*
+ * 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.serialize.fastjson2;
+
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ScopeClassLoaderListener;
+
+import com.alibaba.fastjson2.JSONFactory;
+import com.alibaba.fastjson2.reader.ObjectReaderCreatorASM;
+import com.alibaba.fastjson2.writer.ObjectWriterCreatorASM;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Fastjson2CreatorManager implements ScopeClassLoaderListener<FrameworkModel> {
+
+ /**
+ * An empty classLoader used when classLoader is system classLoader. Prevent the NPE.
+ */
+ private static final ClassLoader SYSTEM_CLASSLOADER_KEY = new ClassLoader() {};
+
+ private final Map<ClassLoader, ObjectReaderCreatorASM> readerMap = new ConcurrentHashMap<>();
+ private final Map<ClassLoader, ObjectWriterCreatorASM> writerMap = new ConcurrentHashMap<>();
+
+ public Fastjson2CreatorManager(FrameworkModel frameworkModel) {
+ frameworkModel.addClassLoaderListener(this);
+ }
+
+ public void setCreator(ClassLoader classLoader) {
+ if (classLoader == null) {
+ classLoader = SYSTEM_CLASSLOADER_KEY;
+ }
+ JSONFactory.setContextReaderCreator(readerMap.computeIfAbsent(classLoader, ObjectReaderCreatorASM::new));
+ JSONFactory.setContextWriterCreator(writerMap.computeIfAbsent(classLoader, ObjectWriterCreatorASM::new));
+ }
+
+ @Override
+ public void onAddClassLoader(FrameworkModel scopeModel, ClassLoader classLoader) {
+ // nop
+ }
+
+ @Override
+ public void onRemoveClassLoader(FrameworkModel scopeModel, ClassLoader classLoader) {
+ readerMap.remove(classLoader);
+ writerMap.remove(classLoader);
+ }
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java
new file mode 100644
index 0000000..d9c9d25
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/java/org/apache/dubbo/common/serialize/fastjson2/Fastjson2ScopeModelInitializer.java
@@ -0,0 +1,41 @@
+/*
+ * 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.serialize.fastjson2;
+
+import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.apache.dubbo.rpc.model.ScopeModelInitializer;
+
+public class Fastjson2ScopeModelInitializer implements ScopeModelInitializer {
+ @Override
+ public void initializeFrameworkModel(FrameworkModel frameworkModel) {
+ ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
+ beanFactory.registerBean(Fastjson2CreatorManager.class);
+ }
+
+ @Override
+ public void initializeApplicationModel(ApplicationModel applicationModel) {
+
+ }
+
+ @Override
+ public void initializeModuleModel(ModuleModel moduleModel) {
+
+ }
+}
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
new file mode 100644
index 0000000..6cda42c
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
@@ -0,0 +1 @@
+fastjson2=org.apache.dubbo.common.serialize.fastjson2.FastJson2Serialization
diff --git a/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer
new file mode 100644
index 0000000..186e56b
--- /dev/null
+++ b/dubbo-serialization/dubbo-serialization-fastjson2/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer
@@ -0,0 +1 @@
+fastjson2=org.apache.dubbo.common.serialize.fastjson2.Fastjson2ScopeModelInitializer
diff --git a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaObjectInput.java b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaObjectInput.java
index 936ab26..b7a3294 100644
--- a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaObjectInput.java
+++ b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaObjectInput.java
@@ -27,7 +27,7 @@
* Java object input implementation
*/
public class JavaObjectInput extends NativeJavaObjectInput {
- public final static int MAX_BYTE_ARRAY_LENGTH = 8 * 1024 * 1024;
+ public static final int MAX_BYTE_ARRAY_LENGTH = 8 * 1024 * 1024;
public JavaObjectInput(InputStream is) throws IOException {
super(new ObjectInputStream(is));
diff --git a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java
index 996fe6e..e166f33 100644
--- a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java
+++ b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/java/JavaSerialization.java
@@ -39,7 +39,7 @@
*/
public class JavaSerialization implements Serialization {
private static final Logger logger = LoggerFactory.getLogger(JavaSerialization.class);
- private final static AtomicBoolean warn = new AtomicBoolean(false);
+ private static final AtomicBoolean warn = new AtomicBoolean(false);
@Override
public byte getContentTypeId() {
diff --git a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/nativejava/NativeJavaSerialization.java b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/nativejava/NativeJavaSerialization.java
index 20d9d0a..854fb0e 100644
--- a/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/nativejava/NativeJavaSerialization.java
+++ b/dubbo-serialization/dubbo-serialization-jdk/src/main/java/org/apache/dubbo/common/serialize/nativejava/NativeJavaSerialization.java
@@ -41,7 +41,7 @@
*/
public class NativeJavaSerialization implements Serialization {
private static final Logger logger = LoggerFactory.getLogger(JavaSerialization.class);
- private final static AtomicBoolean warn = new AtomicBoolean(false);
+ private static final AtomicBoolean warn = new AtomicBoolean(false);
@Override
public byte getContentTypeId() {
diff --git a/dubbo-serialization/pom.xml b/dubbo-serialization/pom.xml
index 6f75995..dbfa18c 100644
--- a/dubbo-serialization/pom.xml
+++ b/dubbo-serialization/pom.xml
@@ -33,6 +33,7 @@
<module>dubbo-serialization-api</module>
<module>dubbo-serialization-hessian2</module>
<module>dubbo-serialization-jdk</module>
+ <module>dubbo-serialization-fastjson2</module>
</modules>
<dependencies>
diff --git a/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml
index 355b032..9246831 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml
+++ b/dubbo-spring-boot/dubbo-spring-boot-actuator/pom.xml
@@ -114,6 +114,13 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ <version>${revision}</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${project.version}</version>
<optional>true</optional>
diff --git a/dubbo-test/dubbo-test-spring/pom.xml b/dubbo-test/dubbo-test-spring/pom.xml
index c496d16..6cbf254 100644
--- a/dubbo-test/dubbo-test-spring/pom.xml
+++ b/dubbo-test/dubbo-test-spring/pom.xml
@@ -111,6 +111,10 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-fastjson2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-jdk</artifactId>
</dependency>
<dependency>
diff --git a/dubbo-xds/pom.xml b/dubbo-xds/pom.xml
new file mode 100644
index 0000000..d162c2e
--- /dev/null
+++ b/dubbo-xds/pom.xml
@@ -0,0 +1,120 @@
+<?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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-xds</artifactId>
+ <name>${project.artifactId}</name>
+ <description>The xDS Integration</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-protobuf</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-stub</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-netty-shaded</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.envoyproxy.controlplane</groupId>
+ <artifactId>api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-ext-jdk15on</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>kr.motd.maven</groupId>
+ <artifactId>os-maven-plugin</artifactId>
+ <version>1.6.2</version>
+ </extension>
+ </extensions>
+ <plugins>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <version>0.6.1</version>
+ <configuration>
+ <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
+ <pluginId>grpc-java</pluginId>
+ <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.31.1:exe:${os.detected.classifier}</pluginArtifact>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ <goal>compile-custom</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
new file mode 100644
index 0000000..1ef1a8b
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsCertificateSigner.java
@@ -0,0 +1,58 @@
+/*
+ * 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.registry.xds;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI
+public interface XdsCertificateSigner {
+
+ @Adaptive(value = "signer")
+ CertPair GenerateCert(URL url);
+
+ class CertPair {
+ private final String privateKey;
+ private final String publicKey;
+ private final long createTime;
+ private final long expireTime;
+
+ public CertPair(String privateKey, String publicKey, long createTime, long expireTime) {
+ this.privateKey = privateKey;
+ this.publicKey = publicKey;
+ this.createTime = createTime;
+ this.expireTime = expireTime;
+ }
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public long getCreateTime() {
+ return createTime;
+ }
+
+ public boolean isExpire() {
+ return System.currentTimeMillis() < expireTime;
+ }
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
new file mode 100644
index 0000000..c09ea80
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsEnv.java
@@ -0,0 +1,22 @@
+/*
+ * 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.registry.xds;
+
+public interface XdsEnv {
+
+ String getCluster();
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsInitializationException.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsInitializationException.java
new file mode 100644
index 0000000..b79f55c
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsInitializationException.java
@@ -0,0 +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.registry.xds;
+
+public final class XdsInitializationException extends Exception {
+
+ public XdsInitializationException(String message) {
+ super(message);
+ }
+
+ public XdsInitializationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
new file mode 100644
index 0000000..a4bd51e
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.registry.xds;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+/**
+ * Empty implements for xDS <br/>
+ * xDS only support `Service Discovery` mode register <br/>
+ * Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
+ * {@link XdsServiceDiscovery} is the real implementation of xDS
+ */
+public class XdsRegistry extends FailbackRegistry {
+ public XdsRegistry(URL url) {
+ super(url);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void doRegister(URL url) {
+
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+
+ }
+
+ @Override
+ public void doSubscribe(URL url, NotifyListener listener) {
+
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
new file mode 100644
index 0000000..8fa130b
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsRegistryFactory.java
@@ -0,0 +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.registry.xds;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+
+public class XdsRegistryFactory extends AbstractRegistryFactory {
+
+ @Override
+ protected String createRegistryCacheKey(URL url) {
+ return url.toFullString();
+ }
+
+ @Override
+ protected Registry createRegistry(URL url) {
+ return new XdsRegistry(url);
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
new file mode 100644
index 0000000..28945f8
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscovery.java
@@ -0,0 +1,97 @@
+/*
+ * 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.registry.xds;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ReflectionBasedServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.xds.util.PilotExchanger;
+import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ScopeModelUtil;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class XdsServiceDiscovery extends ReflectionBasedServiceDiscovery {
+
+ private static final Logger logger = LoggerFactory.getLogger(XdsServiceDiscovery.class);
+
+ private PilotExchanger exchanger;
+
+ public XdsServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+ super(applicationModel, registryURL);
+ }
+
+ @Override
+ public void doInitialize(URL registryURL) {
+ try {
+ exchanger = PilotExchanger.initialize(registryURL);
+ } catch (Throwable t) {
+ logger.error(t);
+ }
+ }
+
+ @Override
+ public void doDestroy() {
+ try {
+ exchanger.destroy();
+ } catch (Throwable t) {
+ logger.error(t);
+ }
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return exchanger.getServices();
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+ Set<Endpoint> endpoints = exchanger.getEndpoints(serviceName);
+ return changedToInstances(serviceName, endpoints);
+ }
+
+ @Override
+ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+ listener.getServiceNames().forEach(serviceName -> exchanger.observeEndpoints(serviceName,
+ (endpoints -> notifyListener(serviceName, listener, changedToInstances(serviceName, endpoints)))));
+ }
+
+ private List<ServiceInstance> changedToInstances(String serviceName, Collection<Endpoint> endpoints) {
+ List<ServiceInstance> instances = new LinkedList<>();
+ endpoints.forEach(endpoint -> {
+ try {
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, endpoint.getAddress(), endpoint.getPortValue(), ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
+ // fill metadata by SelfHostMetaServiceDiscovery, will be fetched by RPC request
+ fillServiceInstance(serviceInstance);
+ instances.add(serviceInstance);
+ } catch (Throwable t) {
+ logger.error("Error occurred when parsing endpoints. Endpoints List:" + endpoints, t);
+ }
+ });
+ instances.sort(Comparator.comparingInt(ServiceInstance::hashCode));
+ return instances;
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
new file mode 100644
index 0000000..41eba21
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/XdsServiceDiscoveryFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.xds;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+public class XdsServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+
+ private static final Logger logger = LoggerFactory.getLogger(XdsServiceDiscoveryFactory.class);
+
+ @Override
+ protected ServiceDiscovery createDiscovery(URL registryURL) {
+ XdsServiceDiscovery xdsServiceDiscovery = new XdsServiceDiscovery(ApplicationModel.defaultModel() ,registryURL);
+ try {
+ xdsServiceDiscovery.doInitialize(registryURL);
+ } catch (Exception e) {
+ logger.error("Error occurred when initialize xDS service discovery impl.", e);
+ }
+ return xdsServiceDiscovery;
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
new file mode 100644
index 0000000..cf91f1e
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioCitadelCertificateSigner.java
@@ -0,0 +1,251 @@
+/*
+ * 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.registry.xds.istio;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.XdsCertificateSigner;
+import org.apache.dubbo.rpc.RpcException;
+
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
+import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.grpc.stub.MetadataUtils;
+import io.grpc.stub.StreamObserver;
+import istio.v1.auth.IstioCertificateRequest;
+import istio.v1.auth.IstioCertificateResponse;
+import istio.v1.auth.IstioCertificateServiceGrpc;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+import org.bouncycastle.util.io.pem.PemObject;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.ECGenParameterSpec;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class IstioCitadelCertificateSigner implements XdsCertificateSigner {
+
+ private static final Logger logger = LoggerFactory.getLogger(IstioCitadelCertificateSigner.class);
+
+ private final org.apache.dubbo.registry.xds.istio.IstioEnv istioEnv;
+
+ private CertPair certPair;
+
+ public IstioCitadelCertificateSigner() {
+ // watch cert, Refresh every 30s
+ ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
+ scheduledThreadPool.scheduleAtFixedRate(new GenerateCertTask(), 0, 30, TimeUnit.SECONDS);
+ istioEnv = IstioEnv.getInstance();
+ }
+
+ @Override
+ public CertPair GenerateCert(URL url) {
+
+ if (certPair != null && !certPair.isExpire()) {
+ return certPair;
+ }
+ return doGenerateCert();
+ }
+
+ private class GenerateCertTask implements Runnable {
+ @Override
+ public void run() {
+ doGenerateCert();
+ }
+ }
+
+ private CertPair doGenerateCert() {
+ synchronized (this) {
+ if (certPair == null || certPair.isExpire()) {
+ try {
+ certPair = createCert();
+ } catch (IOException e) {
+ logger.error("Generate Cert from Istio failed.", e);
+ throw new RpcException("Generate Cert from Istio failed.", e);
+ }
+ }
+ }
+ return certPair;
+ }
+
+ public CertPair createCert() throws IOException {
+ PublicKey publicKey = null;
+ PrivateKey privateKey = null;
+ ContentSigner signer = null;
+
+ if (istioEnv.isECCFirst()) {
+ try {
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
+ KeyPairGenerator g = KeyPairGenerator.getInstance("EC");
+ g.initialize(ecSpec, new SecureRandom());
+ KeyPair keypair = g.generateKeyPair();
+ publicKey = keypair.getPublic();
+ privateKey = keypair.getPrivate();
+ signer = new JcaContentSignerBuilder("SHA256withECDSA").build(keypair.getPrivate());
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | OperatorCreationException e) {
+ logger.error("Generate Key with secp256r1 algorithm failed. Please check if your system support. "
+ + "Will attempt to generate with RSA2048.", e);
+ }
+ }
+
+ if (publicKey == null) {
+ try {
+ KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA");
+ kpGenerator.initialize(istioEnv.getRasKeySize());
+ KeyPair keypair = kpGenerator.generateKeyPair();
+ publicKey = keypair.getPublic();
+ privateKey = keypair.getPrivate();
+ signer = new JcaContentSignerBuilder("SHA256WithRSA").build(keypair.getPrivate());
+ } catch (NoSuchAlgorithmException | OperatorCreationException e) {
+ logger.error("Generate Key with SHA256WithRSA algorithm failed. Please check if your system support.", e);
+ throw new RpcException(e);
+ }
+ }
+
+ String csr = generateCsr(publicKey, signer);
+ ManagedChannel channel = NettyChannelBuilder.forTarget(istioEnv.getCaAddr())
+ .sslContext(GrpcSslContexts.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build()).build();
+
+ Metadata header = new Metadata();
+ Metadata.Key<String> key = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
+ header.put(key, "Bearer " + istioEnv.getServiceAccount());
+
+ key = Metadata.Key.of("ClusterID", Metadata.ASCII_STRING_MARSHALLER);
+ header.put(key, istioEnv.getIstioMetaClusterId());
+
+ IstioCertificateServiceGrpc.IstioCertificateServiceStub stub = IstioCertificateServiceGrpc.newStub(channel);
+
+ stub = MetadataUtils.attachHeaders(stub, header);
+
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ StringBuffer publicKeyBuilder = new StringBuffer();
+ AtomicBoolean failed = new AtomicBoolean(false);
+ stub.createCertificate(generateRequest(csr), generateResponseObserver(countDownLatch, publicKeyBuilder, failed));
+
+ long expireTime = System.currentTimeMillis() + (long) (istioEnv.getSecretTTL() * istioEnv.getSecretGracePeriodRatio());
+
+ try {
+ countDownLatch.await();
+ } catch (InterruptedException e) {
+ throw new RpcException("Generate Cert Failed. Wait for cert failed.", e);
+ }
+
+ if (failed.get()) {
+ throw new RpcException("Generate Cert Failed. Send csr request failed. Please check log above.");
+ }
+
+ String privateKeyPem = generatePrivatePemKey(privateKey);
+ CertPair certPair = new CertPair(privateKeyPem, publicKeyBuilder.toString(), System.currentTimeMillis(), expireTime);
+
+ channel.shutdown();
+ return certPair;
+ }
+
+ private IstioCertificateRequest generateRequest(String csr) {
+ return IstioCertificateRequest.newBuilder().setCsr(csr).setValidityDuration(istioEnv.getSecretTTL()).build();
+ }
+
+ private StreamObserver<IstioCertificateResponse> generateResponseObserver(CountDownLatch countDownLatch,
+ StringBuffer publicKeyBuilder,
+ AtomicBoolean failed) {
+ return new StreamObserver<IstioCertificateResponse>() {
+ @Override
+ public void onNext(IstioCertificateResponse istioCertificateResponse) {
+ for (int i = 0; i < istioCertificateResponse.getCertChainCount(); i++) {
+ publicKeyBuilder.append(istioCertificateResponse.getCertChainBytes(i).toStringUtf8());
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Receive Cert chain from Istio Citadel. \n" + publicKeyBuilder);
+ }
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ failed.set(true);
+ logger.error("Receive error message from Istio Citadel grpc stub.", throwable);
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onCompleted() {
+ countDownLatch.countDown();
+ }
+ };
+ }
+
+ private String generatePrivatePemKey(PrivateKey privateKey) throws IOException {
+ String key = generatePemKey("RSA PRIVATE KEY", privateKey.getEncoded());
+ if (logger.isDebugEnabled()) {
+ logger.debug("Generated Private Key. \n" + key);
+ }
+ return key;
+ }
+
+ private String generatePemKey(String type, byte[] content) throws IOException {
+ PemObject pemObject = new PemObject(type, content);
+ StringWriter str = new StringWriter();
+ JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(str);
+ jcaPEMWriter.writeObject(pemObject);
+ jcaPEMWriter.close();
+ str.close();
+ return str.toString();
+ }
+
+ private String generateCsr(PublicKey publicKey, ContentSigner signer) throws IOException {
+ GeneralNames subjectAltNames = new GeneralNames(new GeneralName[]{new GeneralName(6, istioEnv.getCsrHost())});
+
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+ extGen.addExtension(Extension.subjectAlternativeName, true, subjectAltNames);
+
+ PKCS10CertificationRequest request = new JcaPKCS10CertificationRequestBuilder(
+ new X500Name("O=" + istioEnv.getTrustDomain()), publicKey).addAttribute(
+ PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate()).build(signer);
+
+ String csr = generatePemKey("CERTIFICATE REQUEST", request.getEncoded());
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("CSR Request to Istio Citadel. \n" + csr);
+ }
+ return csr;
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
new file mode 100644
index 0000000..d14aca0
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioConstant.java
@@ -0,0 +1,93 @@
+/*
+ * 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.registry.xds.istio;
+
+public class IstioConstant {
+ /**
+ * Address of the spiffe certificate provider. Defaults to discoveryAddress
+ */
+ public final static String CA_ADDR_KEY = "CA_ADDR";
+
+ /**
+ * CA and xDS services
+ */
+ public final static String DEFAULT_CA_ADDR = "istiod.istio-system.svc:15012";
+
+ /**
+ * The trust domain for spiffe certificates
+ */
+ public final static String TRUST_DOMAIN_KEY = "TRUST_DOMAIN";
+
+ /**
+ * The trust domain for spiffe certificates default value
+ */
+ public final static String DEFAULT_TRUST_DOMAIN = "cluster.local";
+
+ public final static String WORKLOAD_NAMESPACE_KEY = "WORKLOAD_NAMESPACE";
+
+ public final static String DEFAULT_WORKLOAD_NAMESPACE = "default";
+
+ /**
+ * k8s jwt token
+ */
+ public final static String KUBERNETES_SA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
+
+ public final static String KUBERNETES_CA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
+
+ public final static String KUBERNETES_NAMESPACE_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
+
+ public final static String RSA_KEY_SIZE_KEY = "RSA_KEY_SIZE";
+
+ public final static String DEFAULT_RSA_KEY_SIZE = "2048";
+
+ /**
+ * The type of ECC signature algorithm to use when generating private keys
+ */
+ public final static String ECC_SIG_ALG_KEY = "ECC_SIGNATURE_ALGORITHM";
+
+ public final static String DEFAULT_ECC_SIG_ALG = "ECDSA";
+
+ /**
+ * The cert lifetime requested by istio agent
+ */
+ public final static String SECRET_TTL_KEY = "SECRET_TTL";
+
+ /**
+ * The cert lifetime default value 24h0m0s
+ */
+ public final static String DEFAULT_SECRET_TTL = "86400"; //24 * 60 * 60
+
+ /**
+ * The grace period ratio for the cert rotation
+ */
+ public final static String SECRET_GRACE_PERIOD_RATIO_KEY = "SECRET_GRACE_PERIOD_RATIO";
+
+ /**
+ * The grace period ratio for the cert rotation, by default 0.5
+ */
+ public final static String DEFAULT_SECRET_GRACE_PERIOD_RATIO = "0.5";
+
+ public final static String ISTIO_META_CLUSTER_ID_KEY = "ISTIO_META_CLUSTER_ID";
+
+ public final static String DEFAULT_ISTIO_META_CLUSTER_ID = "Kubernetes";
+
+ public final static String SPIFFE = "spiffe://";
+
+ public final static String NS = "/ns/";
+
+ public final static String SA = "/sa/";
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
new file mode 100644
index 0000000..0bb0e06
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/istio/IstioEnv.java
@@ -0,0 +1,165 @@
+/*
+ * 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.registry.xds.istio;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.XdsEnv;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+import static org.apache.dubbo.registry.xds.istio.IstioConstant.NS;
+import static org.apache.dubbo.registry.xds.istio.IstioConstant.SA;
+import static org.apache.dubbo.registry.xds.istio.IstioConstant.SPIFFE;
+
+public class IstioEnv implements XdsEnv {
+ private static final Logger logger = LoggerFactory.getLogger(IstioEnv.class);
+
+ private static final IstioEnv INSTANCE = new IstioEnv();
+
+ private String podName;
+
+ private String caAddr;
+
+ private String serviceAccount = null;
+
+ private String csrHost;
+
+ private String trustDomain;
+
+ private String workloadNameSpace;
+
+ private int rasKeySize;
+
+ private String eccSigAlg;
+
+ private int secretTTL;
+
+ private float secretGracePeriodRatio;
+
+ private String istioMetaClusterId;
+
+ private String caCert;
+
+ private IstioEnv() {
+ // read k8s jwt token
+ File saFile = new File(IstioConstant.KUBERNETES_SA_PATH);
+ if (saFile.canRead()) {
+ try {
+ podName = System.getenv("HOSTNAME");
+ serviceAccount = FileUtils.readFileToString(saFile, StandardCharsets.UTF_8);
+ trustDomain = Optional.ofNullable(System.getenv(IstioConstant.TRUST_DOMAIN_KEY)).orElse(IstioConstant.DEFAULT_TRUST_DOMAIN);
+ workloadNameSpace = Optional.ofNullable(System.getenv(IstioConstant.WORKLOAD_NAMESPACE_KEY))
+ .orElseGet(()->{
+ File namespaceFile = new File(IstioConstant.KUBERNETES_NAMESPACE_PATH);
+ if (namespaceFile.canRead()) {
+ try {
+ return FileUtils.readFileToString(namespaceFile, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ logger.error("read namespace file error", e);
+ }
+ }
+ return IstioConstant.DEFAULT_WORKLOAD_NAMESPACE;
+ });
+ // spiffe://<trust_domain>/ns/<namespace>/sa/<service_account>
+ csrHost = SPIFFE + trustDomain + NS + workloadNameSpace + SA + serviceAccount;
+ caAddr = Optional.ofNullable(System.getenv(IstioConstant.CA_ADDR_KEY)).orElse(IstioConstant.DEFAULT_CA_ADDR);
+ rasKeySize = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.RSA_KEY_SIZE_KEY)).orElse(IstioConstant.DEFAULT_RSA_KEY_SIZE));
+ eccSigAlg = Optional.ofNullable(System.getenv(IstioConstant.ECC_SIG_ALG_KEY)).orElse(IstioConstant.DEFAULT_ECC_SIG_ALG);
+ secretTTL = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.SECRET_TTL_KEY)).orElse(IstioConstant.DEFAULT_SECRET_TTL));
+ secretGracePeriodRatio = Float.parseFloat(Optional.ofNullable(System.getenv(IstioConstant.SECRET_GRACE_PERIOD_RATIO_KEY)).orElse(IstioConstant.DEFAULT_SECRET_GRACE_PERIOD_RATIO));
+ istioMetaClusterId = Optional.ofNullable(System.getenv(IstioConstant.ISTIO_META_CLUSTER_ID_KEY)).orElse(IstioConstant.DEFAULT_ISTIO_META_CLUSTER_ID);
+ File caFile = new File(IstioConstant.KUBERNETES_CA_PATH);
+ if (caFile.canRead()) {
+ try {
+ caCert = FileUtils.readFileToString(caFile, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ logger.error("read ca file error", e);
+ }
+ }
+ } catch (IOException e) {
+ logger.error("Unable to read token file.", e);
+ }
+ }
+ if (serviceAccount == null) {
+ throw new UnsupportedOperationException("Unable to found kubernetes service account token file. " +
+ "Please check if work in Kubernetes and mount service account token file correctly.");
+ }
+ }
+
+ public static IstioEnv getInstance() {
+ return INSTANCE;
+ }
+
+ public String getPodName() {
+ return podName;
+ }
+
+ public String getCaAddr() {
+ return caAddr;
+ }
+
+ public String getServiceAccount() {
+ return serviceAccount;
+ }
+
+ public String getCsrHost() {
+ return csrHost;
+ }
+
+ public String getTrustDomain() {
+ return trustDomain;
+ }
+
+ public String getWorkloadNameSpace() {
+ return workloadNameSpace;
+ }
+
+ @Override
+ public String getCluster() {
+ return null;
+ }
+
+ public int getRasKeySize() {
+ return rasKeySize;
+ }
+
+ public boolean isECCFirst() {
+ return IstioConstant.DEFAULT_ECC_SIG_ALG.equals(eccSigAlg);
+ }
+
+ public int getSecretTTL() {
+ return secretTTL;
+ }
+
+ public float getSecretGracePeriodRatio() {
+ return secretGracePeriodRatio;
+ }
+
+ public String getIstioMetaClusterId() {
+ return istioMetaClusterId;
+ }
+
+ public String getCaCert() {
+ return caCert;
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
new file mode 100644
index 0000000..e353bec
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/NodeBuilder.java
@@ -0,0 +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.registry.xds.util;
+
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.registry.xds.istio.IstioEnv;
+
+import io.envoyproxy.envoy.config.core.v3.Node;
+
+public class NodeBuilder {
+
+ private final static String SVC_CLUSTER_LOCAL = ".svc.cluster.local";
+
+ public static Node build() {
+// String podName = System.getenv("metadata.name");
+// String podNamespace = System.getenv("metadata.namespace");
+
+ String podName = IstioEnv.getInstance().getPodName();
+ String podNamespace = IstioEnv.getInstance().getWorkloadNameSpace();
+ String svcName = IstioEnv.getInstance().getIstioMetaClusterId();
+
+ // id -> sidecar~ip~{POD_NAME}~{NAMESPACE_NAME}.svc.cluster.local
+ // cluster -> {SVC_NAME}
+ return Node.newBuilder()
+ .setId("sidecar~" + NetUtils.getLocalHost() + "~" +podName + "~" + podNamespace + SVC_CLUSTER_LOCAL)
+ .setCluster(svcName)
+ .build();
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
new file mode 100644
index 0000000..dbb30e7
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.java
@@ -0,0 +1,162 @@
+/*
+ * 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.registry.xds.util;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+import org.apache.dubbo.registry.xds.util.protocol.impl.EdsProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.impl.LdsProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.impl.RdsProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
+import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
+import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
+import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+public class PilotExchanger {
+
+ private final XdsChannel xdsChannel;
+
+ private final RdsProtocol rdsProtocol;
+
+ private final EdsProtocol edsProtocol;
+
+ private ListenerResult listenerResult;
+
+ private RouteResult routeResult;
+
+ private final AtomicLong observeRouteRequest = new AtomicLong(-1);
+
+ private final Map<String, Long> domainObserveRequest = new ConcurrentHashMap<>();
+
+ private final Map<String, Set<Consumer<Set<Endpoint>>>> domainObserveConsumer = new ConcurrentHashMap<>();
+
+ private PilotExchanger(URL url) {
+ xdsChannel = new XdsChannel(url);
+ int pollingPoolSize = url.getParameter("pollingPoolSize", 10);
+ int pollingTimeout = url.getParameter("pollingTimeout", 10);
+ LdsProtocol ldsProtocol = new LdsProtocol(xdsChannel, NodeBuilder.build(), pollingPoolSize, pollingTimeout);
+ this.rdsProtocol = new RdsProtocol(xdsChannel, NodeBuilder.build(), pollingPoolSize, pollingTimeout);
+ this.edsProtocol = new EdsProtocol(xdsChannel, NodeBuilder.build(), pollingPoolSize, pollingTimeout);
+
+ this.listenerResult = ldsProtocol.getListeners();
+ this.routeResult = rdsProtocol.getResource(listenerResult.getRouteConfigNames());
+
+ // Observer RDS update
+ if (CollectionUtils.isNotEmpty(listenerResult.getRouteConfigNames())) {
+ this.observeRouteRequest.set(createRouteObserve());
+ }
+ // Observe LDS updated
+ ldsProtocol.observeListeners((newListener) -> {
+ // update local cache
+ if (!newListener.equals(listenerResult)) {
+ this.listenerResult = newListener;
+ // update RDS observation
+ synchronized (observeRouteRequest) {
+ if (observeRouteRequest.get() == -1) {
+ this.observeRouteRequest.set(createRouteObserve());
+ } else {
+ rdsProtocol.updateObserve(observeRouteRequest.get(), newListener.getRouteConfigNames());
+ }
+ }
+ }
+ });
+ }
+
+ private long createRouteObserve() {
+ return rdsProtocol.observeResource(listenerResult.getRouteConfigNames(), (newResult) -> {
+ // check if observed domain update ( will update endpoint observation )
+ domainObserveConsumer.forEach((domain, consumer) -> {
+ Set<String> newRoute = newResult.searchDomain(domain);
+ if (!routeResult.searchDomain(domain).equals(newRoute)) {
+ // routers in observed domain has been updated
+ Long domainRequest = domainObserveRequest.get(domain);
+ if (domainRequest == null) {
+ // router list is empty when observeEndpoints() called and domainRequest has not been created yet
+ // create new observation
+ doObserveEndpoints(domain);
+ } else {
+ // update observation by domainRequest
+ edsProtocol.updateObserve(domainRequest, newRoute);
+ }
+ }
+ });
+ // update local cache
+ routeResult = newResult;
+ });
+ }
+
+ public static PilotExchanger initialize(URL url) {
+ return new PilotExchanger(url);
+ }
+
+ public void destroy() {
+ xdsChannel.destroy();
+ }
+
+ public Set<String> getServices() {
+ return routeResult.getDomains();
+ }
+
+ public Set<Endpoint> getEndpoints(String domain) {
+ Set<String> cluster = routeResult.searchDomain(domain);
+ if (CollectionUtils.isNotEmpty(cluster)) {
+ EndpointResult endpoint = edsProtocol.getResource(cluster);
+ return endpoint.getEndpoints();
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ public void observeEndpoints(String domain, Consumer<Set<Endpoint>> consumer) {
+ // store Consumer
+ domainObserveConsumer.compute(domain, (k, v) -> {
+ if (v == null) {
+ v = new ConcurrentHashSet<>();
+ }
+ // support multi-consumer
+ v.add(consumer);
+ return v;
+ });
+ if (!domainObserveRequest.containsKey(domain)) {
+ doObserveEndpoints(domain);
+ }
+ }
+
+ private void doObserveEndpoints(String domain) {
+ Set<String> router = routeResult.searchDomain(domain);
+ // if router is empty, do nothing
+ // observation will be created when RDS updates
+ if (CollectionUtils.isNotEmpty(router)) {
+ long endpointRequest =
+ edsProtocol.observeResource(
+ router,
+ endpointResult ->
+ // notify consumers
+ domainObserveConsumer.get(domain).forEach(
+ consumer1 -> consumer1.accept(endpointResult.getEndpoints())));
+ domainObserveRequest.put(domain, endpointRequest);
+ }
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
new file mode 100644
index 0000000..9019d38
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
@@ -0,0 +1,102 @@
+/*
+ * 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.registry.xds.util;
+
+import io.grpc.ManagedChannel;
+import io.grpc.netty.shaded.io.netty.channel.epoll.EpollDomainSocketChannel;
+import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup;
+import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
+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.url.component.URLAddress;
+import org.apache.dubbo.registry.xds.XdsCertificateSigner;
+import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
+import io.envoyproxy.envoy.service.discovery.v3.DeltaDiscoveryRequest;
+import io.envoyproxy.envoy.service.discovery.v3.DeltaDiscoveryResponse;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
+import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
+import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
+import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.grpc.stub.StreamObserver;
+import org.apache.dubbo.registry.xds.util.bootstrap.Bootstrapper;
+import org.apache.dubbo.registry.xds.util.bootstrap.BootstrapperImpl;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+public class XdsChannel {
+
+ private static final Logger logger = LoggerFactory.getLogger(XdsChannel.class);
+
+ private static final String USE_AGENT = "use-agent";
+
+ private final ManagedChannel channel;
+
+ protected XdsChannel(URL url) {
+ ManagedChannel managedChannel = null;
+ try {
+ if(!url.getParameter(USE_AGENT,false)) {
+ XdsCertificateSigner signer = url.getOrDefaultApplicationModel().getExtensionLoader(XdsCertificateSigner.class)
+ .getExtension(url.getParameter("signer", "istio"));
+ XdsCertificateSigner.CertPair certPair = signer.GenerateCert(url);
+ SslContext context = GrpcSslContexts.forClient()
+ .trustManager(InsecureTrustManagerFactory.INSTANCE)
+ .keyManager(new ByteArrayInputStream(certPair.getPublicKey().getBytes(StandardCharsets.UTF_8)),
+ new ByteArrayInputStream(certPair.getPrivateKey().getBytes(StandardCharsets.UTF_8)))
+ .build();
+ managedChannel = NettyChannelBuilder.forAddress(url.getHost(), url.getPort()).sslContext(context)
+ .build();
+ }
+ else {
+ BootstrapperImpl bootstrapper = new BootstrapperImpl();
+ Bootstrapper.BootstrapInfo bootstrapInfo = bootstrapper.bootstrap();
+ URLAddress address =URLAddress.parse(bootstrapInfo.servers().get(0).target(),null, false);
+ EpollEventLoopGroup elg = new EpollEventLoopGroup();
+ managedChannel = NettyChannelBuilder.forAddress(new DomainSocketAddress("/" + address.getPath()))
+ .eventLoopGroup(elg)
+ .channelType(EpollDomainSocketChannel.class)
+ .usePlaintext()
+ .build();
+ }
+ } catch (Exception e) {
+ logger.error("Error occurred when creating gRPC channel to control panel.", e);
+ }
+ channel = managedChannel;
+ }
+
+ public StreamObserver<DeltaDiscoveryRequest> observeDeltaDiscoveryRequest(StreamObserver<DeltaDiscoveryResponse> observer) {
+ return AggregatedDiscoveryServiceGrpc.newStub(channel).deltaAggregatedResources(observer);
+ }
+
+ public StreamObserver<DiscoveryRequest> createDeltaDiscoveryRequest(StreamObserver<DiscoveryResponse> observer) {
+ return AggregatedDiscoveryServiceGrpc.newStub(channel).streamAggregatedResources(observer);
+ }
+
+ public StreamObserver<io.envoyproxy.envoy.api.v2.DeltaDiscoveryRequest> observeDeltaDiscoveryRequestV2(StreamObserver<io.envoyproxy.envoy.api.v2.DeltaDiscoveryResponse> observer) {
+ return io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel).deltaAggregatedResources(observer);
+ }
+
+ public StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryRequest> createDeltaDiscoveryRequestV2(StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryResponse> observer) {
+ return io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel).streamAggregatedResources(observer);
+ }
+
+ public void destroy() {
+ channel.shutdown();
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapInfoImpl.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapInfoImpl.java
new file mode 100644
index 0000000..a4e4451
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapInfoImpl.java
@@ -0,0 +1,129 @@
+/*
+ * 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.registry.xds.util.bootstrap;
+
+import io.envoyproxy.envoy.config.core.v3.Node;
+
+import javax.annotation.Nullable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public final class BootstrapInfoImpl extends Bootstrapper.BootstrapInfo {
+
+ private final List<Bootstrapper.ServerInfo> servers;
+
+ private final String serverListenerResourceNameTemplate;
+
+ private final Map<String, Bootstrapper.CertificateProviderInfo> certProviders;
+
+ private final Node node;
+
+ BootstrapInfoImpl(List<Bootstrapper.ServerInfo> servers, String serverListenerResourceNameTemplate, Map<String, Bootstrapper.CertificateProviderInfo> certProviders, Node node) {
+ this.servers = servers;
+ this.serverListenerResourceNameTemplate = serverListenerResourceNameTemplate;
+ this.certProviders = certProviders;
+ this.node = node;
+ }
+
+ @Override
+ public List<Bootstrapper.ServerInfo> servers() {
+ return servers;
+ }
+
+ public Map<String, Bootstrapper.CertificateProviderInfo> certProviders() {
+ return certProviders;
+ }
+
+ @Override
+ public Node node() {
+ return node;
+ }
+
+ @Override
+ public String serverListenerResourceNameTemplate() {
+ return serverListenerResourceNameTemplate;
+ }
+
+ @Override
+ public String toString() {
+ return "BootstrapInfo{"
+ + "servers=" + servers + ", "
+ + "serverListenerResourceNameTemplate=" + serverListenerResourceNameTemplate + ", "
+ + "node=" + node + ", "
+ + "}";
+ }
+
+ public static final class Builder extends Bootstrapper.BootstrapInfo.Builder {
+ private List<Bootstrapper.ServerInfo> servers;
+ private Node node;
+
+ private Map<String, Bootstrapper.CertificateProviderInfo> certProviders;
+
+ private String serverListenerResourceNameTemplate;
+ Builder() {
+ }
+ @Override
+ Bootstrapper.BootstrapInfo.Builder servers(List<Bootstrapper.ServerInfo> servers) {
+ this.servers = new LinkedList<>(servers);
+ return this;
+ }
+
+ @Override
+ Bootstrapper.BootstrapInfo.Builder node(Node node) {
+ if (node == null) {
+ throw new NullPointerException("Null node");
+ }
+ this.node = node;
+ return this;
+ }
+
+ @Override
+ Bootstrapper.BootstrapInfo.Builder certProviders(@Nullable Map<String, Bootstrapper.CertificateProviderInfo> certProviders) {
+ this.certProviders = certProviders;
+ return this;
+ }
+
+ @Override
+ Bootstrapper.BootstrapInfo.Builder serverListenerResourceNameTemplate(@Nullable String serverListenerResourceNameTemplate) {
+ this.serverListenerResourceNameTemplate = serverListenerResourceNameTemplate;
+ return this;
+ }
+
+ @Override
+ Bootstrapper.BootstrapInfo build() {
+ if (this.servers == null
+ || this.node == null) {
+ StringBuilder missing = new StringBuilder();
+ if (this.servers == null) {
+ missing.append(" servers");
+ }
+ if (this.node == null) {
+ missing.append(" node");
+ }
+ throw new IllegalStateException("Missing required properties:" + missing);
+ }
+ return new BootstrapInfoImpl(
+ this.servers,
+ this.serverListenerResourceNameTemplate,
+ this.certProviders,
+ this.node);
+ }
+ }
+
+}
+
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/Bootstrapper.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/Bootstrapper.java
new file mode 100644
index 0000000..5e018c4
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/Bootstrapper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.registry.xds.util.bootstrap;
+
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.grpc.ChannelCredentials;
+import org.apache.dubbo.registry.xds.XdsInitializationException;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+
+public abstract class Bootstrapper {
+
+ public abstract BootstrapInfo bootstrap() throws XdsInitializationException;
+
+ BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public abstract static class ServerInfo {
+ public abstract String target();
+
+ abstract ChannelCredentials channelCredentials();
+
+ abstract boolean useProtocolV3();
+
+ abstract boolean ignoreResourceDeletion();
+
+ }
+
+ public abstract static class CertificateProviderInfo {
+ public abstract String pluginName();
+
+ public abstract Map<String, ?> config();
+ }
+
+ public abstract static class BootstrapInfo {
+ public abstract List<ServerInfo> servers();
+
+ public abstract Map<String, CertificateProviderInfo> certProviders();
+
+ public abstract Node node();
+
+ public abstract String serverListenerResourceNameTemplate();
+
+ abstract static class Builder {
+
+ abstract Builder servers(List<ServerInfo> servers);
+
+ abstract Builder node(Node node);
+
+ abstract Builder certProviders(@Nullable Map<String, CertificateProviderInfo> certProviders);
+
+ abstract Builder serverListenerResourceNameTemplate(
+ @Nullable String serverListenerResourceNameTemplate);
+
+ abstract BootstrapInfo build();
+ }
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperImpl.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperImpl.java
new file mode 100644
index 0000000..d9e2997
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperImpl.java
@@ -0,0 +1,180 @@
+/*
+ * 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.registry.xds.util.bootstrap;
+
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.grpc.ChannelCredentials;
+import io.grpc.internal.JsonParser;
+import io.grpc.internal.JsonUtil;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.XdsInitializationException;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class BootstrapperImpl extends Bootstrapper {
+
+ static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP";
+ static String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
+
+ private static final Logger logger = LoggerFactory.getLogger(BootstrapperImpl.class);
+ private FileReader reader = LocalFileReader.INSTANCE;
+
+ private static final String SERVER_FEATURE_XDS_V3 = "xds_v3";
+ private static final String SERVER_FEATURE_IGNORE_RESOURCE_DELETION = "ignore_resource_deletion";
+
+ public BootstrapInfo bootstrap() throws XdsInitializationException {
+ String filePath = bootstrapPathFromEnvVar;
+ String fileContent = null;
+ if (filePath != null) {
+ try {
+ fileContent = reader.readFile(filePath);
+ } catch (IOException e) {
+ throw new XdsInitializationException("Fail to read bootstrap file", e);
+ }
+ }
+ if (fileContent == null) throw new XdsInitializationException("Cannot find bootstrap configuration");
+
+ Map<String, ?> rawBootstrap;
+ try {
+ rawBootstrap = (Map<String, ?>) JsonParser.parse(fileContent);
+ } catch (IOException e) {
+ throw new XdsInitializationException("Failed to parse JSON", e);
+ }
+ return bootstrap(rawBootstrap);
+ }
+
+ @Override
+ BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
+ BootstrapInfo.Builder builder = new BootstrapInfoImpl.Builder();
+
+ List<?> rawServerConfigs = JsonUtil.getList(rawData, "xds_servers");
+ if (rawServerConfigs == null) {
+ throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' does not exist.");
+ }
+ List<ServerInfo> servers = parseServerInfos(rawServerConfigs);
+ builder.servers(servers);
+
+ Node.Builder nodeBuilder = Node.newBuilder();
+ Map<String, ?> rawNode = JsonUtil.getObject(rawData, "node");
+ if (rawNode != null) {
+ String id = JsonUtil.getString(rawNode, "id");
+ if (id != null) {
+ nodeBuilder.setId(id);
+ }
+ String cluster = JsonUtil.getString(rawNode, "cluster");
+ if (cluster != null) {
+ nodeBuilder.setCluster(cluster);
+ }
+ Map<String, ?> metadata = JsonUtil.getObject(rawNode, "metadata");
+ Map<String, ?> rawLocality = JsonUtil.getObject(rawNode, "locality");
+ }
+ builder.node(nodeBuilder.build());
+
+ Map<String, ?> certProvidersBlob = JsonUtil.getObject(rawData, "certificate_providers");
+ if (certProvidersBlob != null) {
+ Map<String, CertificateProviderInfo> certProviders = new HashMap<>(certProvidersBlob.size());
+ for (String name : certProvidersBlob.keySet()) {
+ Map<String, ?> valueMap = JsonUtil.getObject(certProvidersBlob, name);
+ String pluginName =
+ checkForNull(JsonUtil.getString(valueMap, "plugin_name"), "plugin_name");
+ Map<String, ?> config = checkForNull(JsonUtil.getObject(valueMap, "config"), "config");
+ CertificateProviderInfoImpl certificateProviderInfo =
+ new CertificateProviderInfoImpl(pluginName, config);
+ certProviders.put(name, certificateProviderInfo);
+ }
+ builder.certProviders(certProviders);
+ }
+
+ return builder.build();
+ }
+
+ private static List<ServerInfo> parseServerInfos(List<?> rawServerConfigs)
+ throws XdsInitializationException {
+ List<ServerInfo> servers = new LinkedList<>();
+ List<Map<String, ?>> serverConfigList = JsonUtil.checkObjectList(rawServerConfigs);
+ for (Map<String, ?> serverConfig : serverConfigList) {
+ String serverUri = JsonUtil.getString(serverConfig, "server_uri");
+ if (serverUri == null) {
+ throw new XdsInitializationException("Invalid bootstrap: missing 'server_uri'");
+ }
+ List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
+ if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
+ throw new XdsInitializationException(
+ "Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
+ }
+ ChannelCredentials channelCredentials =
+ parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
+// if (channelCredentials == null) {
+// throw new XdsInitializationException(
+// "Server " + serverUri + ": no supported channel credentials found");
+// }
+
+ boolean useProtocolV3 = false;
+ boolean ignoreResourceDeletion = false;
+ List<String> serverFeatures = JsonUtil.getListOfStrings(serverConfig, "server_features");
+ if (serverFeatures != null) {
+ useProtocolV3 = serverFeatures.contains(SERVER_FEATURE_XDS_V3);
+ ignoreResourceDeletion = serverFeatures.contains(SERVER_FEATURE_IGNORE_RESOURCE_DELETION);
+ }
+ servers.add(
+ new ServerInfoImpl(serverUri, channelCredentials, useProtocolV3, ignoreResourceDeletion));
+ }
+ return servers;
+ }
+
+ void setFileReader(FileReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Reads the content of the file with the given path in the file system.
+ */
+ interface FileReader {
+ String readFile(String path) throws IOException;
+ }
+
+ private enum LocalFileReader implements FileReader {
+ INSTANCE;
+
+ @Override
+ public String readFile(String path) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
+ }
+ }
+
+ private static <T> T checkForNull(T value, String fieldName) throws XdsInitializationException {
+ if (value == null) {
+ throw new XdsInitializationException(
+ "Invalid bootstrap: '" + fieldName + "' does not exist.");
+ }
+ return value;
+ }
+
+ @Nullable
+ private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList, String serverUri) throws XdsInitializationException {
+ return null;
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/CertificateProviderInfoImpl.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/CertificateProviderInfoImpl.java
new file mode 100644
index 0000000..2c3da74
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/CertificateProviderInfoImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.registry.xds.util.bootstrap;
+
+import java.util.Map;
+
+final class CertificateProviderInfoImpl extends Bootstrapper.CertificateProviderInfo {
+
+ private final String pluginName;
+ private final Map<String, ?> config;
+
+ CertificateProviderInfoImpl(String pluginName, Map<String, ?> config) {
+ this.pluginName = pluginName;
+ this.config = config;
+ }
+
+ @Override
+ public String pluginName() {
+ return pluginName;
+ }
+
+ @Override
+ public Map<String, ?> config() {
+ return config;
+ }
+
+ @Override
+ public String toString() {
+ return "CertificateProviderInfo{"
+ + "pluginName=" + pluginName + ", "
+ + "config=" + config
+ + "}";
+ }
+
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/ServerInfoImpl.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/ServerInfoImpl.java
new file mode 100644
index 0000000..001008a
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/bootstrap/ServerInfoImpl.java
@@ -0,0 +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.registry.xds.util.bootstrap;
+
+import io.grpc.ChannelCredentials;
+
+final class ServerInfoImpl extends Bootstrapper.ServerInfo {
+
+ private final String target;
+
+ private final ChannelCredentials channelCredentials;
+
+ private final boolean useProtocolV3;
+
+ private final boolean ignoreResourceDeletion;
+
+ ServerInfoImpl(String target, ChannelCredentials channelCredentials, boolean useProtocolV3, boolean ignoreResourceDeletion) {
+ this.target = target;
+ this.channelCredentials = channelCredentials;
+ this.useProtocolV3 = useProtocolV3;
+ this.ignoreResourceDeletion = ignoreResourceDeletion;
+ }
+
+ @Override
+ public String target() {
+ return target;
+ }
+
+ @Override
+ ChannelCredentials channelCredentials() {
+ return channelCredentials;
+ }
+
+ @Override
+ boolean useProtocolV3() {
+ return useProtocolV3;
+ }
+
+ @Override
+ boolean ignoreResourceDeletion() {
+ return ignoreResourceDeletion;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerInfo{"
+ + "target=" + target + ", "
+ + "channelCredentials=" + channelCredentials + ", "
+ + "useProtocolV3=" + useProtocolV3 + ", "
+ + "ignoreResourceDeletion=" + ignoreResourceDeletion
+ + "}";
+ }
+
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
new file mode 100644
index 0000000..9b6687f
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/AbstractProtocol.java
@@ -0,0 +1,242 @@
+/*
+ * 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.registry.xds.util.protocol;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.registry.xds.util.XdsChannel;
+
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
+import io.grpc.stub.StreamObserver;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+public abstract class AbstractProtocol<T, S extends DeltaResource<T>> implements XdsProtocol<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractProtocol.class);
+
+ protected final XdsChannel xdsChannel;
+
+ protected final Node node;
+
+ /**
+ * Store Request Parameter ( resourceNames )
+ * K - requestId, V - resourceNames
+ */
+ protected final Map<Long, Set<String>> requestParam = new ConcurrentHashMap<>();
+
+ /**
+ * Store ADS Request Observer ( StreamObserver in Streaming Request )
+ * K - requestId, V - StreamObserver
+ */
+ private final Map<Long, StreamObserver<DiscoveryRequest>> requestObserverMap = new ConcurrentHashMap<>();
+
+ /**
+ * Store Delta-ADS Request Observer ( StreamObserver in Streaming Request )
+ * K - requestId, V - StreamObserver
+ */
+ private final Map<Long, ScheduledFuture<?>> observeScheduledMap = new ConcurrentHashMap<>();
+
+ /**
+ * Store CompletableFuture for Request ( used to fetch async result in ResponseObserver )
+ * K - requestId, V - CompletableFuture
+ */
+ private final Map<Long, CompletableFuture<T>> streamResult = new ConcurrentHashMap<>();
+
+ private final ScheduledExecutorService pollingExecutor;
+
+ private final int pollingTimeout;
+
+ protected final static AtomicLong requestId = new AtomicLong(0);
+
+ public AbstractProtocol(XdsChannel xdsChannel, Node node, int pollingPoolSize, int pollingTimeout) {
+ this.xdsChannel = xdsChannel;
+ this.node = node;
+ this.pollingExecutor = new ScheduledThreadPoolExecutor(pollingPoolSize, new NamedThreadFactory("Dubbo-registry-xds"));
+ this.pollingTimeout = pollingTimeout;
+ }
+
+ /**
+ * Abstract method to obtain Type-URL from sub-class
+ *
+ * @return Type-URL of xDS
+ */
+ public abstract String getTypeUrl();
+
+ @Override
+ public T getResource(Set<String> resourceNames) {
+ long request = requestId.getAndIncrement();
+ resourceNames = resourceNames == null ? Collections.emptySet() : resourceNames;
+
+ // Store Request Parameter, which will be used for ACK
+ requestParam.put(request, resourceNames);
+
+ // create observer
+ StreamObserver<DiscoveryRequest> requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(request));
+
+ // use future to get async result
+ CompletableFuture<T> future = new CompletableFuture<>();
+ requestObserverMap.put(request, requestObserver);
+ streamResult.put(request, future);
+
+ // send request to control panel
+ requestObserver.onNext(buildDiscoveryRequest(resourceNames));
+
+ try {
+ // get result
+ return future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ logger.error("Error occur when request control panel.");
+ return null;
+ } finally {
+ // close observer
+ //requestObserver.onCompleted();
+
+ // remove temp
+ streamResult.remove(request);
+ requestObserverMap.remove(request);
+ requestParam.remove(request);
+ }
+ }
+
+ @Override
+ public long observeResource(Set<String> resourceNames, Consumer<T> consumer) {
+ long request = requestId.getAndIncrement();
+ resourceNames = resourceNames == null ? Collections.emptySet() : resourceNames;
+
+ // Store Request Parameter, which will be used for ACK
+ requestParam.put(request, resourceNames);
+
+ // call once for full data
+ consumer.accept(getResource(resourceNames));
+
+ // channel reused
+ StreamObserver<DiscoveryRequest> requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(request));
+ requestObserverMap.put(request, requestObserver);
+
+ ScheduledFuture<?> scheduledFuture = pollingExecutor.scheduleAtFixedRate(() -> {
+ try {
+ // origin request, may changed by updateObserve
+ Set<String> names = requestParam.get(request);
+
+ // use future to get async result
+ CompletableFuture<T> future = new CompletableFuture<>();
+ streamResult.put(request, future);
+
+ // observer reused
+ StreamObserver<DiscoveryRequest> observer = requestObserverMap.get(request);
+
+ // send request to control panel
+ observer.onNext(buildDiscoveryRequest(names));
+
+ try {
+ // get result
+ consumer.accept(future.get());
+ } catch (InterruptedException | ExecutionException e) {
+ logger.error("Error occur when request control panel.");
+ } finally {
+ // close observer
+ //requestObserver.onCompleted();
+
+ // remove temp
+ streamResult.remove(request);
+ }
+ } catch (Throwable t) {
+ logger.error("Error when requesting observe data. Type: " + getTypeUrl(), t);
+ }
+ }, pollingTimeout, pollingTimeout, TimeUnit.SECONDS);
+
+ observeScheduledMap.put(request, scheduledFuture);
+
+ return request;
+ }
+
+ @Override
+ public void updateObserve(long request, Set<String> resourceNames) {
+ // send difference in resourceNames
+ requestParam.put(request, resourceNames);
+ }
+
+ protected DiscoveryRequest buildDiscoveryRequest(Set<String> resourceNames) {
+ return DiscoveryRequest.newBuilder()
+ .setNode(node)
+ .setTypeUrl(getTypeUrl())
+ .addAllResourceNames(resourceNames)
+ .build();
+ }
+
+ protected DiscoveryRequest buildDiscoveryRequest(Set<String> resourceNames, DiscoveryResponse response) {
+ // for ACK
+ return DiscoveryRequest.newBuilder()
+ .setNode(node)
+ .setTypeUrl(response.getTypeUrl())
+ .setVersionInfo(response.getVersionInfo())
+ .setResponseNonce(response.getNonce())
+ .build();
+ }
+
+ protected abstract T decodeDiscoveryResponse(DiscoveryResponse response);
+
+ private class ResponseObserver implements StreamObserver<DiscoveryResponse> {
+ private final long requestId;
+
+ public ResponseObserver(long requestId) {
+ this.requestId = requestId;
+ }
+
+ @Override
+ public void onNext(DiscoveryResponse value) {
+ logger.info("receive notification from xds server, type: " + getTypeUrl() + " requestId: " + requestId);
+ T result = decodeDiscoveryResponse(value);
+ StreamObserver<DiscoveryRequest> observer = requestObserverMap.get(requestId);
+ if (observer == null) {
+ return;
+ }
+ observer.onNext(buildDiscoveryRequest(Collections.emptySet(), value));
+ CompletableFuture<T> future = streamResult.get(requestId);
+ if (future == null) {
+ return;
+ }
+ future.complete(result);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ logger.error("xDS Client received error message! detail:", t);
+ }
+
+ @Override
+ public void onCompleted() {
+ // ignore
+ }
+ }
+
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
new file mode 100644
index 0000000..bcae39c
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/DeltaResource.java
@@ -0,0 +1,32 @@
+/*
+ * 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.registry.xds.util.protocol;
+
+/**
+ * A interface for resources in xDS, which can be updated by ADS delta stream
+ * <br/>
+ * This interface is design to unify the way of fetching data in delta stream
+ * in {@link org.apache.dubbo.registry.xds.util.PilotExchanger}
+ */
+public interface DeltaResource<T> {
+ /**
+ * Get resource from delta stream
+ *
+ * @return the newest resource from stream
+ */
+ T getResource();
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
new file mode 100644
index 0000000..847ec04
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/XdsProtocol.java
@@ -0,0 +1,48 @@
+/*
+ * 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.registry.xds.util.protocol;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+public interface XdsProtocol<T> {
+ /**
+ * Gets all {@link T resource} by the specified resource name.
+ * For LDS, the {@param resourceNames} is ignored
+ *
+ * @param resourceNames specified resource name
+ * @return resources, null if request failed
+ */
+ T getResource(Set<String> resourceNames);
+
+ /**
+ * Add a observer resource with {@link Consumer}
+ *
+ * @param resourceNames specified resource name
+ * @param consumer resource notifier, will be called when resource updated
+ * @return requestId, used when resourceNames update with {@link XdsProtocol#updateObserve(long, Set)}
+ */
+ long observeResource(Set<String> resourceNames, Consumer<T> consumer);
+
+ /**
+ * Update observed resource list in {@link XdsProtocol#observeResource(Set, Consumer)}
+ *
+ * @param request requestId returned by {@link XdsProtocol#observeResource(Set, Consumer)}
+ * @param resourceNames new resource name list to observe
+ */
+ void updateObserve(long request, Set<String> resourceNames);
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
new file mode 100644
index 0000000..9e192cf
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaEndpoint.java
@@ -0,0 +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.registry.xds.util.protocol.delta;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
+import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
+import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+public class DeltaEndpoint implements DeltaResource<EndpointResult> {
+ private final Map<String, Set<Endpoint>> data = new ConcurrentHashMap<>();
+
+ public void addResource(String resourceName, Set<Endpoint> endpoints) {
+ data.put(resourceName, endpoints);
+ }
+
+ public void removeResource(Collection<String> resourceName) {
+ if (CollectionUtils.isNotEmpty(resourceName)) {
+ resourceName.forEach(data::remove);
+ }
+ }
+
+ @Override
+ public EndpointResult getResource() {
+ Set<Endpoint> set = data.values().stream()
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet());
+ return new EndpointResult(set);
+ }
+
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
new file mode 100644
index 0000000..73b2c9a
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaListener.java
@@ -0,0 +1,49 @@
+/*
+ * 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.registry.xds.util.protocol.delta;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
+import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+public class DeltaListener implements DeltaResource<ListenerResult> {
+ private final Map<String, Set<String>> data = new ConcurrentHashMap<>();
+
+ public void addResource(String resourceName, Set<String> listeners) {
+ data.put(resourceName, listeners);
+ }
+
+ public void removeResource(Collection<String> resourceName) {
+ if (CollectionUtils.isNotEmpty(resourceName)) {
+ resourceName.forEach(data::remove);
+ }
+ }
+
+ @Override
+ public ListenerResult getResource() {
+ Set<String> set = data.values().stream()
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet());
+ return new ListenerResult(set);
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
new file mode 100644
index 0000000..71fdb47
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/delta/DeltaRoute.java
@@ -0,0 +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.registry.xds.util.protocol.delta;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
+import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DeltaRoute implements DeltaResource<RouteResult> {
+ private final Map<String, Map<String, Set<String>>> data = new ConcurrentHashMap<>();
+
+ public void addResource(String resourceName, Map<String, Set<String>> route) {
+ data.put(resourceName, route);
+ }
+
+ public void removeResource(Collection<String> resourceName) {
+ if (CollectionUtils.isNotEmpty(resourceName)) {
+ resourceName.forEach(data::remove);
+ }
+ }
+
+ @Override
+ public RouteResult getResource() {
+ Map<String, Set<String>> result = new ConcurrentHashMap<>();
+ data.values().forEach(result::putAll);
+ return new RouteResult(result);
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
new file mode 100644
index 0000000..510370a
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/EdsProtocol.java
@@ -0,0 +1,93 @@
+/*
+ * 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.registry.xds.util.protocol.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.util.XdsChannel;
+import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaEndpoint;
+import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
+import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.InvalidProtocolBufferException;
+import io.envoyproxy.envoy.config.core.v3.HealthStatus;
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.envoyproxy.envoy.config.core.v3.SocketAddress;
+import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
+import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class EdsProtocol extends AbstractProtocol<EndpointResult, DeltaEndpoint> {
+
+ private static final Logger logger = LoggerFactory.getLogger(EdsProtocol.class);
+
+ public EdsProtocol(XdsChannel xdsChannel, Node node, int pollingPoolSize, int pollingTimeout) {
+ super(xdsChannel, node, pollingPoolSize, pollingTimeout);
+ }
+
+ @Override
+ public String getTypeUrl() {
+ return "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
+ }
+
+ @Override
+ protected EndpointResult decodeDiscoveryResponse(DiscoveryResponse response) {
+ if (getTypeUrl().equals(response.getTypeUrl())) {
+ Set<Endpoint> set = response.getResourcesList().stream()
+ .map(EdsProtocol::unpackClusterLoadAssignment)
+ .filter(Objects::nonNull)
+ .flatMap((e) -> decodeResourceToEndpoint(e).stream())
+ .collect(Collectors.toSet());
+ return new EndpointResult(set);
+ }
+ return new EndpointResult();
+ }
+
+ private static Set<Endpoint> decodeResourceToEndpoint(ClusterLoadAssignment resource) {
+ return resource.getEndpointsList().stream()
+ .flatMap((e) -> e.getLbEndpointsList().stream())
+ .map(EdsProtocol::decodeLbEndpointToEndpoint)
+ .collect(Collectors.toSet());
+ }
+
+ private static Endpoint decodeLbEndpointToEndpoint(LbEndpoint lbEndpoint) {
+ Endpoint endpoint = new Endpoint();
+ SocketAddress address = lbEndpoint.getEndpoint().getAddress().getSocketAddress();
+ endpoint.setAddress(address.getAddress());
+ endpoint.setPortValue(address.getPortValue());
+ boolean healthy = HealthStatus.HEALTHY.equals(lbEndpoint.getHealthStatus()) ||
+ HealthStatus.UNKNOWN.equals(lbEndpoint.getHealthStatus());
+ endpoint.setHealthy(healthy);
+ endpoint.setWeight(lbEndpoint.getLoadBalancingWeight().getValue());
+ return endpoint;
+ }
+
+ private static ClusterLoadAssignment unpackClusterLoadAssignment(Any any) {
+ try {
+ return any.unpack(ClusterLoadAssignment.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.error("Error occur when decode xDS response.", e);
+ return null;
+ }
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
new file mode 100644
index 0000000..b9a59a5
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/LdsProtocol.java
@@ -0,0 +1,106 @@
+/*
+ * 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.registry.xds.util.protocol.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.util.XdsChannel;
+import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaListener;
+import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.InvalidProtocolBufferException;
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.envoyproxy.envoy.config.listener.v3.Filter;
+import io.envoyproxy.envoy.config.listener.v3.Listener;
+import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
+import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
+
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class LdsProtocol extends AbstractProtocol<ListenerResult, DeltaListener> {
+
+ private static final Logger logger = LoggerFactory.getLogger(LdsProtocol.class);
+
+ public LdsProtocol(XdsChannel xdsChannel, Node node, int pollingPoolSize, int pollingTimeout) {
+ super(xdsChannel, node, pollingPoolSize, pollingTimeout);
+ }
+
+ @Override
+ public String getTypeUrl() {
+ return "type.googleapis.com/envoy.config.listener.v3.Listener";
+ }
+
+ public ListenerResult getListeners() {
+ return getResource(null);
+ }
+
+ public void observeListeners(Consumer<ListenerResult> consumer) {
+ observeResource(Collections.emptySet(), consumer);
+ }
+
+ @Override
+ protected ListenerResult decodeDiscoveryResponse(DiscoveryResponse response) {
+ if (getTypeUrl().equals(response.getTypeUrl())) {
+ Set<String> set = response.getResourcesList().stream()
+ .map(LdsProtocol::unpackListener)
+ .filter(Objects::nonNull)
+ .flatMap((e) -> decodeResourceToListener(e).stream())
+ .collect(Collectors.toSet());
+ return new ListenerResult(set);
+ }
+ return new ListenerResult();
+ }
+
+ private Set<String> decodeResourceToListener(Listener resource) {
+ return resource.getFilterChainsList().stream()
+ .flatMap((e) -> e.getFiltersList().stream())
+ .map(Filter::getTypedConfig)
+ .map(LdsProtocol::unpackHttpConnectionManager)
+ .filter(Objects::nonNull)
+ .map(HttpConnectionManager::getRds)
+ .map(Rds::getRouteConfigName)
+ .collect(Collectors.toSet());
+ }
+
+ private static Listener unpackListener(Any any) {
+ try {
+ return any.unpack(Listener.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.error("Error occur when decode xDS response.", e);
+ return null;
+ }
+ }
+
+ private static HttpConnectionManager unpackHttpConnectionManager(Any any) {
+ try {
+ if (!any.is(HttpConnectionManager.class)) {
+ return null;
+ }
+ return any.unpack(HttpConnectionManager.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.error("Error occur when decode xDS response.", e);
+ return null;
+ }
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
new file mode 100644
index 0000000..ff9ba67
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/impl/RdsProtocol.java
@@ -0,0 +1,94 @@
+/*
+ * 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.registry.xds.util.protocol.impl;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.registry.xds.util.XdsChannel;
+import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
+import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaRoute;
+import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.InvalidProtocolBufferException;
+import io.envoyproxy.envoy.config.core.v3.Node;
+import io.envoyproxy.envoy.config.route.v3.Route;
+import io.envoyproxy.envoy.config.route.v3.RouteAction;
+import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
+import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class RdsProtocol extends AbstractProtocol<RouteResult, DeltaRoute> {
+
+ private static final Logger logger = LoggerFactory.getLogger(RdsProtocol.class);
+
+ public RdsProtocol(XdsChannel xdsChannel, Node node, int pollingPoolSize, int pollingTimeout) {
+ super(xdsChannel, node, pollingPoolSize, pollingTimeout);
+ }
+
+ @Override
+ public String getTypeUrl() {
+ return "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
+ }
+
+ @Override
+ protected RouteResult decodeDiscoveryResponse(DiscoveryResponse response) {
+ if (getTypeUrl().equals(response.getTypeUrl())) {
+ Map<String, Set<String>> map = response.getResourcesList().stream()
+ .map(RdsProtocol::unpackRouteConfiguration)
+ .filter(Objects::nonNull)
+ .map(RdsProtocol::decodeResourceToListener)
+ .reduce((a, b) -> {
+ a.putAll(b);
+ return a;
+ }).orElse(new HashMap<>());
+ return new RouteResult(map);
+ }
+ return new RouteResult();
+ }
+
+ private static Map<String, Set<String>> decodeResourceToListener(RouteConfiguration resource) {
+ Map<String, Set<String>> map = new HashMap<>();
+ resource.getVirtualHostsList()
+ .forEach(virtualHost -> {
+ Set<String> cluster = virtualHost.getRoutesList().stream()
+ .map(Route::getRoute)
+ .map(RouteAction::getCluster)
+ .collect(Collectors.toSet());
+ for (String domain : virtualHost.getDomainsList()) {
+ map.put(domain, cluster);
+ }
+ });
+ return map;
+ }
+
+ private static RouteConfiguration unpackRouteConfiguration(Any any) {
+ try {
+ return any.unpack(RouteConfiguration.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.error("Error occur when decode xDS response.", e);
+ return null;
+ }
+ }
+
+
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
new file mode 100644
index 0000000..acbe919
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/Endpoint.java
@@ -0,0 +1,88 @@
+/*
+ * 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.registry.xds.util.protocol.message;
+
+import java.util.Objects;
+
+public class Endpoint {
+ private String address;
+ private int portValue;
+ private boolean healthy;
+ private int weight;
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public int getPortValue() {
+ return portValue;
+ }
+
+ public void setPortValue(int portValue) {
+ this.portValue = portValue;
+ }
+
+ public boolean isHealthy() {
+ return healthy;
+ }
+
+ public void setHealthy(boolean healthy) {
+ this.healthy = healthy;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public void setWeight(int weight) {
+ this.weight = weight;
+ }
+
+ @Override
+ public String toString() {
+ return "Endpoint{" +
+ "address='" + address + '\'' +
+ ", portValue='" + portValue + '\'' +
+ ", healthy=" + healthy +
+ ", weight=" + weight +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Endpoint endpoint = (Endpoint) o;
+ return healthy == endpoint.healthy &&
+ weight == endpoint.weight &&
+ Objects.equals(address, endpoint.address) &&
+ Objects.equals(portValue, endpoint.portValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, portValue, healthy, weight);
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
new file mode 100644
index 0000000..4f89078
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/EndpointResult.java
@@ -0,0 +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.registry.xds.util.protocol.message;
+
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+
+import java.util.Objects;
+import java.util.Set;
+
+public class EndpointResult {
+ private Set<Endpoint> endpoints;
+
+ public EndpointResult() {
+ this.endpoints = new ConcurrentHashSet<>();
+ }
+
+ public EndpointResult(Set<Endpoint> endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ public Set<Endpoint> getEndpoints() {
+ return endpoints;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EndpointResult that = (EndpointResult) o;
+ return Objects.equals(endpoints, that.endpoints);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(endpoints);
+ }
+
+ @Override
+ public String toString() {
+ return "EndpointResult{" +
+ "endpoints=" + endpoints +
+ '}';
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
new file mode 100644
index 0000000..61d717e
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/ListenerResult.java
@@ -0,0 +1,70 @@
+/*
+ * 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.registry.xds.util.protocol.message;
+
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+
+import java.util.Objects;
+import java.util.Set;
+
+public class ListenerResult {
+ private Set<String> routeConfigNames;
+
+ public ListenerResult() {
+ this.routeConfigNames = new ConcurrentHashSet<>();
+ }
+
+ public ListenerResult(Set<String> routeConfigNames) {
+ this.routeConfigNames = routeConfigNames;
+ }
+
+ public Set<String> getRouteConfigNames() {
+ return routeConfigNames;
+ }
+
+ public void setRouteConfigNames(Set<String> routeConfigNames) {
+ this.routeConfigNames = routeConfigNames;
+ }
+
+ public void mergeRouteConfigNames(Set<String> names) {
+ this.routeConfigNames.addAll(names);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ListenerResult listenerResult = (ListenerResult) o;
+ return Objects.equals(routeConfigNames, listenerResult.routeConfigNames);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(routeConfigNames);
+ }
+
+ @Override
+ public String toString() {
+ return "ListenerResult{" +
+ "routeConfigNames=" + routeConfigNames +
+ '}';
+ }
+}
diff --git a/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
new file mode 100644
index 0000000..2355b3d
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/protocol/message/RouteResult.java
@@ -0,0 +1,73 @@
+/*
+ * 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.registry.xds.util.protocol.message;
+
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class RouteResult {
+ private final Map<String, Set<String>> domainMap;
+
+ public RouteResult() {
+ this.domainMap = new ConcurrentHashMap<>();
+ }
+
+ public RouteResult(Map<String, Set<String>> domainMap) {
+ this.domainMap = domainMap;
+ }
+
+ public boolean isNotEmpty() {
+ return !domainMap.isEmpty();
+ }
+
+ public Set<String> searchDomain(String domain) {
+ return domainMap.getOrDefault(domain, new ConcurrentHashSet<>());
+ }
+
+ public Set<String> getDomains() {
+ return Collections.unmodifiableSet(domainMap.keySet());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RouteResult that = (RouteResult) o;
+ return Objects.equals(domainMap, that.domainMap);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(domainMap);
+ }
+
+ @Override
+ public String toString() {
+ return "RouteResult{" +
+ "domainMap=" + domainMap +
+ '}';
+ }
+}
diff --git a/dubbo-xds/src/main/proto/ca.proto b/dubbo-xds/src/main/proto/ca.proto
new file mode 100644
index 0000000..41e6add
--- /dev/null
+++ b/dubbo-xds/src/main/proto/ca.proto
@@ -0,0 +1,62 @@
+// Copyright Istio Authors
+//
+// Licensed 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.
+
+// The canonical version of this proto can be found at
+// https://github.com/istio/api/blob/9abf4c87205f6ad04311fa021ce60803d8b95f78/security/v1alpha1/ca.proto
+
+syntax = "proto3";
+
+import "google/protobuf/struct.proto";
+
+// Keep this package for backward compatibility.
+package istio.v1.auth;
+
+option go_package = "istio.io/api/security/v1alpha1";
+option java_generic_services = true;
+option java_multiple_files = true;
+
+// Certificate request message. The authentication should be based on:
+// 1. Bearer tokens carried in the side channel;
+// 2. Client-side certificate via Mutual TLS handshake.
+// Note: the service implementation is REQUIRED to verify the authenticated caller is authorize to
+// all SANs in the CSR. The server side may overwrite any requested certificate field based on its
+// policies.
+message IstioCertificateRequest {
+ // PEM-encoded certificate request.
+ // The public key in the CSR is used to generate the certificate,
+ // and other fields in the generated certificate may be overwritten by the CA.
+ string csr = 1;
+ // Optional: requested certificate validity period, in seconds.
+ int64 validity_duration = 3;
+
+ // $hide_from_docs
+ // Optional: Opaque metadata provided by the XDS node to Istio.
+ // Supported metadata: WorkloadName, WorkloadIP, ClusterID
+ google.protobuf.Struct metadata = 4;
+}
+
+// Certificate response message.
+message IstioCertificateResponse {
+ // PEM-encoded certificate chain.
+ // The leaf cert is the first element, and the root cert is the last element.
+ repeated string cert_chain = 1;
+}
+
+// Service for managing certificates issued by the CA.
+service IstioCertificateService {
+ // Using provided CSR, returns a signed certificate.
+ rpc CreateCertificate(IstioCertificateRequest)
+ returns (IstioCertificateResponse) {
+ }
+}
diff --git a/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..2ee954e
--- /dev/null
+++ b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+xds=org.apache.dubbo.registry.xds.XdsRegistryFactory
diff --git a/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..daf1bb1
--- /dev/null
+++ b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+xds=org.apache.dubbo.registry.xds.XdsServiceDiscovery
diff --git a/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..e6dfce2
--- /dev/null
+++ b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+xds=org.apache.dubbo.registry.xds.XdsServiceDiscoveryFactory
diff --git a/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
new file mode 100644
index 0000000..bbfc0fb
--- /dev/null
+++ b/dubbo-xds/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
@@ -0,0 +1 @@
+istio=org.apache.dubbo.registry.xds.istio.IstioCitadelCertificateSigner
diff --git a/dubbo-xds/src/test/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperTest.java b/dubbo-xds/src/test/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperTest.java
new file mode 100644
index 0000000..180e260
--- /dev/null
+++ b/dubbo-xds/src/test/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.registry.xds.util.bootstrap;
+
+import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.URLAddress;
+import org.apache.dubbo.registry.xds.XdsInitializationException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class BootstrapperTest {
+ @Test
+ public void testParse() throws XdsInitializationException {
+ String rawData = "{\n" +
+ " \"xds_servers\": [\n" +
+ " {\n" +
+ " \"server_uri\": \"unix:///etc/istio/proxy/XDS\",\n" +
+ " \"channel_creds\": [\n" +
+ " {\n" +
+ " \"type\": \"insecure\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"server_features\": [\n" +
+ " \"xds_v3\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"node\": {\n" +
+ " \"id\": \"sidecar~172.17.0.4~dubbo-demo-consumer-deployment-grpc-agent-58585cb9cd-gp79p.dubbo-demo~dubbo-demo.svc.cluster.local\",\n" +
+ " \"metadata\": {\n" +
+ " \"ANNOTATIONS\": {\n" +
+ " \"inject.istio.io/templates\": \"grpc-agent\",\n" +
+ " \"kubernetes.io/config.seen\": \"2022-07-19T12:53:29.742565722Z\",\n" +
+ " \"kubernetes.io/config.source\": \"api\",\n" +
+ " \"prometheus.io/path\": \"/stats/prometheus\",\n" +
+ " \"prometheus.io/port\": \"15020\",\n" +
+ " \"prometheus.io/scrape\": \"true\",\n" +
+ " \"proxy.istio.io/config\": \"{\\\"holdApplicationUntilProxyStarts\\\": true}\",\n" +
+ " \"proxy.istio.io/overrides\": \"{\\\"containers\\\":[{\\\"name\\\":\\\"app\\\",\\\"image\\\":\\\"gcr.io/istio-testing/app:latest\\\",\\\"args\\\":[\\\"--metrics=15014\\\",\\\"--port\\\",\\\"18080\\\",\\\"--tcp\\\",\\\"19090\\\",\\\"--xds-grpc-server=17070\\\",\\\"--grpc\\\",\\\"17070\\\",\\\"--grpc\\\",\\\"17171\\\",\\\"--port\\\",\\\"3333\\\",\\\"--port\\\",\\\"8080\\\",\\\"--version\\\",\\\"v1\\\",\\\"--crt=/cert.crt\\\",\\\"--key=/cert.key\\\"],\\\"ports\\\":[{\\\"containerPort\\\":17070,\\\"protocol\\\":\\\"TCP\\\"},{\\\"containerPort\\\":17171,\\\"protocol\\\":\\\"TCP\\\"},{\\\"containerPort\\\":8080,\\\"protocol\\\":\\\"TCP\\\"},{\\\"name\\\":\\\"tcp-health-port\\\",\\\"containerPort\\\":3333,\\\"protocol\\\":\\\"TCP\\\"}],\\\"env\\\":[{\\\"name\\\":\\\"INSTANCE_IP\\\",\\\"valueFrom\\\":{\\\"fieldRef\\\":{\\\"apiVersion\\\":\\\"v1\\\",\\\"fieldPath\\\":\\\"status.podIP\\\"}}}],\\\"resources\\\":{},\\\"volumeMounts\\\":[{\\\"name\\\":\\\"kube-api-access-2tknx\\\",\\\"readOnly\\\":true,\\\"mountPath\\\":\\\"/var/run/secrets/kubernetes.io/serviceaccount\\\"}],\\\"livenessProbe\\\":{\\\"tcpSocket\\\":{\\\"port\\\":\\\"tcp-health-port\\\"},\\\"initialDelaySeconds\\\":10,\\\"timeoutSeconds\\\":1,\\\"periodSeconds\\\":10,\\\"successThreshold\\\":1,\\\"failureThreshold\\\":10},\\\"readinessProbe\\\":{\\\"httpGet\\\":{\\\"path\\\":\\\"/\\\",\\\"port\\\":8080,\\\"scheme\\\":\\\"HTTP\\\"},\\\"initialDelaySeconds\\\":1,\\\"timeoutSeconds\\\":1,\\\"periodSeconds\\\":2,\\\"successThreshold\\\":1,\\\"failureThreshold\\\":10},\\\"startupProbe\\\":{\\\"tcpSocket\\\":{\\\"port\\\":\\\"tcp-health-port\\\"},\\\"timeoutSeconds\\\":1,\\\"periodSeconds\\\":10,\\\"successThreshold\\\":1,\\\"failureThreshold\\\":10},\\\"terminationMessagePath\\\":\\\"/dev/termination-log\\\",\\\"terminationMessagePolicy\\\":\\\"File\\\",\\\"imagePullPolicy\\\":\\\"Always\\\",\\\"securityContext\\\":{\\\"runAsUser\\\":1338,\\\"runAsGroup\\\":1338}},{\\\"name\\\":\\\"dubbo-demo-consumer\\\",\\\"image\\\":\\\"dockeddocking/dubbo:consumer.v1.0\\\",\\\"command\\\":[\\\"sh\\\",\\\"-c\\\",\\\"java $JAVA_OPTS -jar dubbo-demo-consumer.jar \\\"],\\\"resources\\\":{},\\\"volumeMounts\\\":[{\\\"name\\\":\\\"kube-api-access-2tknx\\\",\\\"readOnly\\\":true,\\\"mountPath\\\":\\\"/var/run/secrets/kubernetes.io/serviceaccount\\\"}],\\\"terminationMessagePath\\\":\\\"/dev/termination-log\\\",\\\"terminationMessagePolicy\\\":\\\"File\\\",\\\"imagePullPolicy\\\":\\\"Always\\\"}]}\",\n" +
+ " \"sidecar.istio.io/rewriteAppHTTPProbers\": \"false\",\n" +
+ " \"sidecar.istio.io/status\": \"{\\\"initContainers\\\":null,\\\"containers\\\":[\\\"app\\\",\\\"dubbo-demo-consumer\\\",\\\"istio-proxy\\\"],\\\"volumes\\\":[\\\"workload-socket\\\",\\\"workload-certs\\\",\\\"istio-xds\\\",\\\"istio-data\\\",\\\"istio-podinfo\\\",\\\"istio-token\\\",\\\"istiod-ca-cert\\\"],\\\"imagePullSecrets\\\":null,\\\"revision\\\":\\\"default\\\"}\"\n" +
+ " },\n" +
+ " \"APP_CONTAINERS\": \"app,dubbo-demo-consumer\",\n" +
+ " \"CLUSTER_ID\": \"Kubernetes\",\n" +
+ " \"ENVOY_PROMETHEUS_PORT\": 15090,\n" +
+ " \"ENVOY_STATUS_PORT\": 15021,\n" +
+ " \"GENERATOR\": \"grpc\",\n" +
+ " \"INSTANCE_IPS\": \"172.17.0.4\",\n" +
+ " \"INTERCEPTION_MODE\": \"REDIRECT\",\n" +
+ " \"ISTIO_PROXY_SHA\": \"2b6009118109b480e1d5abf3188fd7d9c0c0acf0\",\n" +
+ " \"ISTIO_VERSION\": \"1.14.1\",\n" +
+ " \"LABELS\": {\n" +
+ " \"app\": \"dubbo-demo-consumer-dev\",\n" +
+ " \"pod-template-hash\": \"58585cb9cd\",\n" +
+ " \"service.istio.io/canonical-name\": \"dubbo-demo-consumer-dev\",\n" +
+ " \"service.istio.io/canonical-revision\": \"v1\",\n" +
+ " \"version\": \"v1\"\n" +
+ " },\n" +
+ " \"MESH_ID\": \"cluster.local\",\n" +
+ " \"NAME\": \"dubbo-demo-consumer-deployment-grpc-agent-58585cb9cd-gp79p\",\n" +
+ " \"NAMESPACE\": \"dubbo-demo\",\n" +
+ " \"OWNER\": \"kubernetes://apis/apps/v1/namespaces/dubbo-demo/deployments/dubbo-demo-consumer-deployment-grpc-agent\",\n" +
+ " \"PILOT_SAN\": [\n" +
+ " \"istiod.istio-system.svc\"\n" +
+ " ],\n" +
+ " \"POD_PORTS\": \"[{\\\"containerPort\\\":17070,\\\"protocol\\\":\\\"TCP\\\"},{\\\"containerPort\\\":17171,\\\"protocol\\\":\\\"TCP\\\"},{\\\"containerPort\\\":8080,\\\"protocol\\\":\\\"TCP\\\"},{\\\"name\\\":\\\"tcp-health-port\\\",\\\"containerPort\\\":3333,\\\"protocol\\\":\\\"TCP\\\"}]\",\n" +
+ " \"PROV_CERT\": \"var/run/secrets/istio/root-cert.pem\",\n" +
+ " \"PROXY_CONFIG\": {\n" +
+ " \"binaryPath\": \"/usr/local/bin/envoy\",\n" +
+ " \"concurrency\": 2,\n" +
+ " \"configPath\": \"./etc/istio/proxy\",\n" +
+ " \"controlPlaneAuthPolicy\": \"MUTUAL_TLS\",\n" +
+ " \"discoveryAddress\": \"istiod.istio-system.svc:15012\",\n" +
+ " \"drainDuration\": \"45s\",\n" +
+ " \"holdApplicationUntilProxyStarts\": true,\n" +
+ " \"parentShutdownDuration\": \"60s\",\n" +
+ " \"proxyAdminPort\": 15000,\n" +
+ " \"serviceCluster\": \"istio-proxy\",\n" +
+ " \"statNameLength\": 189,\n" +
+ " \"statusPort\": 15020,\n" +
+ " \"terminationDrainDuration\": \"5s\",\n" +
+ " \"tracing\": {\n" +
+ " \"zipkin\": {\n" +
+ " \"address\": \"zipkin.istio-system:9411\"\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"SERVICE_ACCOUNT\": \"default\",\n" +
+ " \"WORKLOAD_NAME\": \"dubbo-demo-consumer-deployment-grpc-agent\"\n" +
+ " },\n" +
+ " \"locality\": {},\n" +
+ " \"UserAgentVersionType\": null\n" +
+ " },\n" +
+ " \"certificate_providers\": {\n" +
+ " \"default\": {\n" +
+ " \"plugin_name\": \"file_watcher\",\n" +
+ " \"config\": {\n" +
+ " \"certificate_file\": \"/var/lib/istio/data/cert-chain.pem\",\n" +
+ " \"private_key_file\": \"/var/lib/istio/data/key.pem\",\n" +
+ " \"ca_certificate_file\": \"/var/lib/istio/data/root-cert.pem\",\n" +
+ " \"refresh_interval\": \"900s\"\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"server_listener_resource_name_template\": \"xds.istio.io/grpc/lds/inbound/%s\"\n" +
+ "}";
+ BootstrapperImpl.bootstrapPathFromEnvVar = "";
+ BootstrapperImpl bootstrapper = new BootstrapperImpl();
+ bootstrapper.setFileReader(createFileReader(rawData));
+ Bootstrapper.BootstrapInfo info = bootstrapper.bootstrap();
+ List<Bootstrapper.ServerInfo> serverInfoList = info.servers();
+ Assertions.assertEquals(serverInfoList.get(0).target(), "unix:///etc/istio/proxy/XDS");
+ URLAddress address =URLAddress.parse(serverInfoList.get(0).target(),null, false);
+ Assertions.assertEquals(new DomainSocketAddress(address.getPath()).path(), "etc/istio/proxy/XDS");
+ }
+
+ @Test
+ public void testUrl() {
+ URL url = URL.valueOf("dubbo://127.0.0.1:23456/TestService?useAgent=true");
+ Assertions.assertTrue(url.getParameter("useAgent", false));
+ }
+
+ private static BootstrapperImpl.FileReader createFileReader(final String rawData) {
+ return new BootstrapperImpl.FileReader() {
+ @Override
+ public String readFile(String path) throws IOException {
+ return rawData;
+ }
+ };
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 6546479..ed0a923 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,7 +129,7 @@
<checkstyle_unix.skip>true</checkstyle_unix.skip>
<rat.skip>true</rat.skip>
<jacoco.skip>true</jacoco.skip>
- <revision>3.0.13-SNAPSHOT</revision>
+ <revision>3.1.2-SNAPSHOT</revision>
</properties>
<modules>
@@ -157,6 +157,8 @@
<module>dubbo-spring-boot</module>
<module>dubbo-native</module>
<module>dubbo-test</module>
+ <module>dubbo-kubernetes</module>
+ <module>dubbo-xds</module>
<module>dubbo-native-plugin</module>
</modules>
@@ -326,10 +328,10 @@
**/org/apache/dubbo/common/serialize/protobuf/support/wrapper/MapValue.java,
**/org/apache/dubbo/common/serialize/protobuf/support/wrapper/ThrowablePB.java,
**/org/apache/dubbo/triple/TripleWrapper.java,
- **/istio/v1/auth/Ca.java,
- **/istio/v1/auth/IstioCertificateServiceGrpc.java,
+ **/istio/v1/auth/**/*,
**/com/google/rpc/*,
**/generated/**/*,
+ **/generated-sources/**/*,
**/grpc/health/**/*,
**/grpc/reflection/**/*,
**/target/**/*,
@@ -512,6 +514,7 @@
<forkMode>once</forkMode>
<argLine>${argline} ${jacocoArgLine}
--add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
</argLine>
<systemProperties>