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/.github/workflows/build-and-test-3.yml b/.github/workflows/build-and-test-3.1.yml
similarity index 84%
rename from .github/workflows/build-and-test-3.yml
rename to .github/workflows/build-and-test-3.1.yml
index 3a9b654..b2e83ad 100644
--- a/.github/workflows/build-and-test-3.yml
+++ b/.github/workflows/build-and-test-3.1.yml
@@ -1,4 +1,4 @@
-name: Build and Test For Dubbo 3
+name: Build and Test For Dubbo 3.1
on: [push, pull_request, workflow_dispatch]
@@ -151,6 +151,43 @@
- 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-18.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
env:
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/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index c190fa5..fd2c467 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;
@@ -71,7 +71,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 +193,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("2-2", "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-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/config/Configuration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Configuration.java
index f7f5bc8..8e7dc07 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,6 +16,9 @@
*/
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;
@@ -24,6 +27,9 @@
* 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 +72,11 @@
try {
return convert(Integer.class, key, defaultValue);
} catch (NumberFormatException e) {
+ // 0-2 Property type mismatch.
+ interfaceLevelLogger.error("0-2", "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/configcenter/AbstractDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
index 0cc6af2..b5feab0 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,7 +75,7 @@
/**
* Logger
*/
- protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
/**
* The thread pool for workers who executes the tasks
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..a238e7d 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
@@ -546,4 +546,26 @@
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";
+
}
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/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/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
index 0942d88..26fef8d 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));
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..bd15785 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;
@@ -47,7 +48,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 +83,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("0-1", "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/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index f0246c0..11b1e3f 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 the Zookeeper registry, I found that the category of the consumer URL is 'consumers'.
+ 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..09bc1a4 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
@@ -1023,7 +1023,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 5e9b8b0..42a785b 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
@@ -240,9 +240,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/annotation/DubboReference.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboReference.java
index 6b5c215..e33797e 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
*/
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/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/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-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..43b715e
--- /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.rpc.protocol.tri.reactive.handler.ManyToManyMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.reactive.handler.ManyToOneMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.reactive.handler.OneToManyMethodHandler;
+import org.apache.dubbo.rpc.protocol.tri.reactive.calls.ReactorClientCalls;
+import org.apache.dubbo.rpc.protocol.tri.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/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 0c57145..8dd76c0 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 java.util.concurrent.Callable;
@@ -67,14 +68,20 @@
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.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;
@@ -92,7 +99,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.
@@ -248,7 +255,7 @@
}
protected synchronized void init() {
- if (initialized && ref !=null ) {
+ if (initialized && ref != null) {
return;
}
try {
@@ -276,7 +283,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);
@@ -314,6 +321,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("2-2", "server crashed", "", "No provider available.", t);
+ }
+
throw t;
}
initialized = true;
@@ -384,7 +399,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);
@@ -411,6 +426,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);
@@ -425,12 +443,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());
@@ -440,6 +458,68 @@
}
/**
+ * 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("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"))) {
+ 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
@@ -503,9 +583,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.");
}
}
@@ -558,7 +638,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 + "/")
@@ -568,6 +650,10 @@
+ invoker.getUrl()
+ " to the consumer "
+ NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
+
+ logger.error("2-2", "provider not started", "", "No provider available.", illegalStateException);
+
+ throw illegalStateException;
}
}
@@ -585,7 +671,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) {
@@ -594,9 +680,9 @@
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], " +
- "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()));
+ "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()));
}
interfaceClass = GenericService.class;
} else {
@@ -605,7 +691,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);
@@ -662,7 +748,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/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index 778b7c3..dac760b 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
@@ -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;
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/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/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/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/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
index b22bd91..ed55e23 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>
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..f2a38c4 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>
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..4141823 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;
@@ -61,7 +61,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 +106,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("5-1", "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..e30c3be 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
@@ -60,7 +60,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("5-1", "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 fd7d0ee..fb3b390 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.7</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>
@@ -175,7 +176,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.12-SNAPSHOT</revision>
+ <revision>3.1.1-SNAPSHOT</revision>
</properties>
<dependencyManagement>
@@ -229,6 +230,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>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml
index bda20ce..e84de5f 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.12-SNAPSHOT</revision>
+ <revision>3.1.1-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 b023a54..cad323e 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.12-SNAPSHOT</revision>
+ <revision>3.1.1-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..81f17eb 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>
@@ -335,6 +342,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>
@@ -429,6 +440,7 @@
<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>
diff --git a/dubbo-distribution/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml
index a477c4dc..d38fb8f 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -198,6 +198,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>
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..1a0c1fa
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesMeshEnvListener.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.google.gson.Gson;
+import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
+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.io.IOException;
+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
+ .customResource(
+ MeshConstant.getVsDefinition())
+ .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+ @Override
+ public void eventReceived(Action action, String resource) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ Map drRuleMap = new Gson().fromJson(resource, Map.class);
+ String vsRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+ 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 {
+ Map<String, Object> vsRule = kubernetesClient
+ .customResource(
+ MeshConstant.getVsDefinition())
+ .get(namespace, appName);
+ vsAppCache.put(appName, new Yaml(new SafeConstructor()).dump(vsRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (IOException 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
+ .customResource(
+ MeshConstant.getDrDefinition())
+ .watch(namespace, appName, null, new ListOptionsBuilder().build(), new Watcher<String>() {
+ @Override
+ public void eventReceived(Action action, String resource) {
+ logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action + " Resource:" + resource);
+
+ if (action == Action.ADDED || action == Action.MODIFIED) {
+ Map drRuleMap = new Gson().fromJson(resource, Map.class);
+ String drRule = new Yaml(new SafeConstructor()).dump(drRuleMap);
+
+ 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 {
+ Map<String, Object> drRule = kubernetesClient
+ .customResource(
+ MeshConstant.getDrDefinition())
+ .get(namespace, appName);
+ drAppCache.put(appName, new Yaml(new SafeConstructor()).dump(drRule));
+ } catch (Throwable ignore) {
+
+ }
+ } catch (IOException 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..e0f47f1
--- /dev/null
+++ b/dubbo-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DefaultKubernetesClient;
+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 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, Watch> SERVICE_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> PODS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> ENDPOINTS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
+
+ public KubernetesServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+ super(applicationModel, registryURL);
+ Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
+ this.kubernetesClient = new DefaultKubernetesClient(config);
+ 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() throws Exception {
+ SERVICE_WATCHER.forEach((k, v) -> v.close());
+ SERVICE_WATCHER.clear();
+
+ PODS_WATCHER.forEach((k, v) -> v.close());
+ PODS_WATCHER.clear();
+
+ ENDPOINTS_WATCHER.forEach((k, v) -> v.close());
+ ENDPOINTS_WATCHER.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 =
+ 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) {
+ Watch watch = kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Endpoints>() {
+ @Override
+ public void eventReceived(Action action, Endpoints resource) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: " + action.name() +
+ ". Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ ENDPOINTS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return;
+ }
+
+ Watch watch = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .watch(new Watcher<Pod>() {
+ @Override
+ public void eventReceived(Action action, Pod resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Update Event. Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ PODS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
+ Watch watch = kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Service>() {
+ @Override
+ public void eventReceived(Action action, Service resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Update Event. Update Pods Watcher. " +
+ "Current pod name: " + currentHostname);
+ }
+
+ if (PODS_WATCHER.containsKey(serviceName)) {
+ PODS_WATCHER.get(serviceName).close();
+ PODS_WATCHER.remove(serviceName);
+ }
+ watchPods(listener, serviceName);
+ }
+ }
+
+ @Override
+ public void onClose(WatcherException cause) {
+ // ignore
+ }
+ });
+
+ SERVICE_WATCHER.put(serviceName, watch);
+ }
+
+ private void notifyServiceChanged(String serviceName, ServiceInstancesChangedListener listener) {
+ long receivedTime = System.nanoTime();
+
+ ServiceInstancesChangedEvent event;
+
+ event = new ServiceInstancesChangedEvent(serviceName, getInstances(serviceName));
+
+ 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..6b1a1b0
--- /dev/null
+++ b/dubbo-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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;
+//
+//@ExtendWith({MockitoExtension.class})
+//public class KubernetesServiceDiscoveryTest {
+// 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;
+//
+// @BeforeEach
+// public void setUp() {
+// mockServer.before();
+// mockClient = mockServer.getClient();
+//
+// serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
+// .setProtocol("kubernetes")
+// .addParameter(KubernetesClientConst.USE_HTTPS, "false")
+// .addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
+// serverUrl.setScopeModel(ApplicationModel.defaultModel());
+//
+// 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("TestServer").withLabels(selector).endMetadata()
+// .build();
+//
+// Service service = new ServiceBuilder()
+// .withNewMetadata().withName("TestService").endMetadata()
+// .withNewSpec().withSelector(selector).endSpec().build();
+//
+// Endpoints endPoints = new EndpointsBuilder()
+// .withNewMetadata().withName("TestService").endMetadata()
+// .addNewSubset()
+// .addNewAddress().withIp("ip1")
+// .withNewTargetRef().withUid("uid1").withName("TestServer").endTargetRef().endAddress()
+// .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+// .build();
+//
+// mockClient.pods().create(pod);
+// mockClient.services().create(service);
+// mockClient.endpoints().create(endPoints);
+// }
+//
+// @AfterEach
+// public void destroy() {
+// mockServer.after();
+// }
+//
+// @Test
+// public void testEndpointsUpdate() throws Exception {
+//
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// HashSet<String> serviceList = new HashSet<>(4);
+// serviceList.add("TestService");
+// Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+// Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+// serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+// mockClient.endpoints().withName("TestService")
+// .edit(endpoints ->
+// new EndpointsBuilder(endpoints)
+// .editFirstSubset()
+// .addNewAddress()
+// .withIp("ip2")
+// .withNewTargetRef().withUid("uid2").withName("TestServer").endTargetRef()
+// .endAddress().endSubset()
+// .build());
+//
+// Thread.sleep(5000);
+// ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+// ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+// Mockito.verify(mockListener, Mockito.times(2)).onEvent(eventArgumentCaptor.capture());
+// Assertions.assertEquals(2, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//
+// @Test
+// public void testPodsUpdate() throws Exception {
+//
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// HashSet<String> serviceList = new HashSet<>(4);
+// serviceList.add("TestService");
+// Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+// Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+//
+// serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+//
+// serviceInstance = new DefaultServiceInstance("TestService", "Test12345", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.update(serviceInstance);
+//
+// Thread.sleep(5000);
+// ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+// ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+// Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
+// Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//
+// @Test
+// public void testGetInstance() throws Exception {
+// KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+// serviceDiscovery.initialize(serverUrl);
+//
+// serviceDiscovery.setCurrentHostname("TestServer");
+// serviceDiscovery.setKubernetesClient(mockClient);
+//
+// ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345, ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
+// serviceDiscovery.register(serviceInstance);
+//
+// serviceDiscovery.update(serviceInstance);
+//
+// Assertions.assertEquals(1, serviceDiscovery.getServices().size());
+// Assertions.assertEquals(1, serviceDiscovery.getInstances("TestService").size());
+//
+// Assertions.assertEquals(serviceInstance, serviceDiscovery.getLocalInstance());
+//
+// serviceDiscovery.unregister(serviceInstance);
+//
+// serviceDiscovery.destroy();
+// }
+//}
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/MetadataInfo.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 645eb9e..51b8d2e 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();
@@ -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-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-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/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..5f4651d 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, " +
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-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..c230075 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,7 +18,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.utils.CollectionUtils;
import org.apache.dubbo.common.utils.UrlUtils;
@@ -26,7 +26,7 @@
import java.util.List;
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;
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..a3a73e2 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;
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..432a777 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
@@ -245,6 +245,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) {
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..8112a6b
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ReflectionBasedServiceDiscovery.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+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("1-7", "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/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..30daeb7 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,35 @@
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.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 +69,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 +107,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 +240,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,11 +282,12 @@
* @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())) {
@@ -291,33 +308,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 +377,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 +413,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 +436,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 +446,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 +522,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..cc311fa 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,8 +54,6 @@
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.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY;
@@ -67,7 +67,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 +77,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 +94,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 +126,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 +148,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 +181,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("1-17", "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 +244,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 +306,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 +346,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 +382,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 +409,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 +427,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 +472,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 +489,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 +514,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..9e5a207 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;
@@ -35,7 +35,7 @@
* 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 +71,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("4-2", "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..509a85c 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;
@@ -58,7 +58,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 +111,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 +124,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("1-18", "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("1-18", "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..bd57dc5 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;
@@ -45,7 +45,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 +107,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("1-14", "", "",
+ "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..922bfba 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;
@@ -59,7 +59,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 +204,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("2-1", "", "",
+ "Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+
return BitList.emptyList();
}
}
@@ -295,15 +298,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("1-8", "", "",
+ "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("1-8", "", "",
+ "unexpected error when unsubscribe service " + serviceKey + " from registry: " + registry.getUrl(), t);
}
ExtensionLoader<AddressListener> addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class);
@@ -318,7 +326,9 @@
try {
destroyAllInvokers();
} catch (Throwable t) {
- logger.warn("Failed to destroy service " + serviceKey, t);
+ // 1-15 - Failed to destroy service.
+ logger.warn("1-15", "", "",
+ "Failed to destroy service " + serviceKey, t);
}
routerChain.destroy();
invokersChangedListener = null;
@@ -333,7 +343,9 @@
try {
destroyAllInvokers();
} catch (Throwable t) {
- logger.warn("Failed to destroy service " + serviceKey, t);
+ // 1-15 - Failed to destroy service.
+ logger.warn("1-15", "", "",
+ "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..e3573de 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;
@@ -81,7 +82,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 +225,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("1-4", "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 +252,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 +261,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(
+ "3-1", "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 +347,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.
@@ -361,19 +375,28 @@
continue;
}
}
+
if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
+
if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
- logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
+
+ // 4-1 - Unsupported protocol
+
+ logger.error("4-1", "typo in URL", "", "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()));
+
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 +410,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("4-2", "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("4-3", "", "",
+ "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+ }
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(url, invoker);
@@ -494,7 +528,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("1-15", "", "",
+ "Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
}
}
localUrlInvokerMap.clear();
@@ -547,8 +583,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("1-16", "", "",
+ "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..19cc45f 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;
@@ -143,7 +143,8 @@
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
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..bcfe7d4 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;
@@ -38,7 +38,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 +113,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(
+ "1-13", "registry center offline", "Check the registry server.",
+ "Final failed to execute task " + taskName + ", url: " + url + ", retry " + retryTimes + " times.");
+
return;
}
if (logger.isInfoEnabled()) {
@@ -123,7 +127,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("1-13", "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..c65e519 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;
@@ -70,7 +70,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 +87,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 +107,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 +118,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("1-9", "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 +214,22 @@
if (!lockfile.exists()) {
lockfile.createNewFile();
}
+
try (RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
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("1-9", CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ "Adjust dubbo.registry.file.", ioException);
+
+ throw ioException;
}
+
// Save
try {
if (!file.exists()) {
@@ -228,29 +260,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("1-9", 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("1-9", 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("1-10", "", "",
+ String.format("Failed to delete lock file [%s]", lockfile.getName()));
}
}
}
@@ -266,9 +308,15 @@
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("1-9", 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("1-9", CAUSE_MULTI_DUBBO_USING_SAME_FILE, "",
+ "Failed to load registry cache file " + file, e);
}
}
@@ -422,7 +470,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("1-7", "consumer is offline", "",
+ "Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t);
}
}
}
@@ -430,7 +480,7 @@
}
/**
- * Notify changes from the Provider side.
+ * Notify changes from the provider side.
*
* @param url consumer side url
* @param listener listener
@@ -444,7 +494,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("1-4", "", "", "Ignore empty notify urls for subscribe url " + url);
return;
}
if (logger.isInfoEnabled()) {
@@ -468,6 +519,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 +573,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("1-8", "", "",
+ "Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
}
}
}
@@ -537,7 +591,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("1-8", "", "",
+ "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..2429822 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;
@@ -39,7 +40,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 +70,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 +84,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 +103,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("1-11", "", "",
+ "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..b1cccf2 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;
@@ -62,24 +62,34 @@
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 +106,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("0-2", "typo in property value", "This property requires an integer value.",
+ "Invalid registry properties configuration key " + key + ", value " + str);
}
}
return result;
@@ -110,7 +123,7 @@
protected void evictURLCache(URL url) {
Map<String, ServiceAddressURL> oldURLs = stringUrls.remove(url);
try {
- if (oldURLs != null && oldURLs.size() > 0) {
+ if (oldURLs != null && !oldURLs.isEmpty()) {
logger.info("Evicting urls for service " + url.getServiceKey() + ", size " + oldURLs.size());
Long currentTimestamp = System.currentTimeMillis();
for (Map.Entry<String, ServiceAddressURL> entry : oldURLs.entrySet()) {
@@ -123,22 +136,39 @@
}
}
} 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("1-3", "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));
URL copyOfConsumer = removeParamsFromConsumer(consumer);
+
if (oldURLs == null) {
for (String rawProvider : providers) {
rawProvider = stripOffVariableKeys(rawProvider);
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("1-1", "", "",
+ "Invalid address, failed to parse into URL " + rawProvider);
+
continue;
}
newURLs.put(rawProvider, cachedURL);
@@ -151,7 +181,9 @@
if (cachedURL == null) {
cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters());
if (cachedURL == null) {
- logger.warn("Invalid address, failed to parse into URL " + rawProvider);
+ logger.warn("1-1", "", "",
+ "Invalid address, failed to parse into URL " + rawProvider);
+
continue;
}
}
@@ -168,6 +200,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 +219,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("1-4", "", "",
+ "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)
@@ -201,20 +235,28 @@
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.
encoded = false;
}
+
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("1-5", "", "",
+ "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;
+
URLAddress address = stringAddress.computeIfAbsent(rawAddress, k -> URLAddress.parse(k, getDefaultURLProtocol(), isEncoded));
address.setTimestamp(System.currentTimeMillis());
@@ -222,9 +264,11 @@
param.setTimestamp(System.currentTimeMillis());
ServiceAddressURL cachedURL = createServiceURL(address, param, consumerURL);
+
if (isMatch(consumerURL, cachedURL)) {
return cachedURL;
}
+
return null;
}
@@ -314,7 +358,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 +387,11 @@
}
}
} catch (Throwable t) {
- logger.error("Error occurred when clearing cached URLs", t);
+ // 1-6 Error when clearing cached URLs.
+
+ logger.error("1-6", "", "",
+ "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..8ff00d8 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;
@@ -40,7 +40,7 @@
* 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 +124,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("1-12", "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-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 021102c..e96fce2 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..1fa01d4 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,11 +59,20 @@
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
@@ -69,10 +84,7 @@
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 +92,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 +107,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 +120,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..e4af8f8 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;
@@ -56,7 +57,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 +71,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) {
@@ -150,6 +156,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 +167,9 @@
}
}
});
+
zkClient.create(root, false);
+
List<String> services = zkClient.addChildListener(root, zkListener);
if (CollectionUtils.isNotEmpty(services)) {
for (String service : services) {
@@ -172,20 +181,25 @@
}
} else {
CountDownLatch latch = new CountDownLatch(1);
+
try {
List<URL> urls = new ArrayList<>();
+
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);
}
+
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
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.
@@ -282,12 +296,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());
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..9f10fa2 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,7 +17,7 @@
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.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceInstance;
@@ -169,7 +169,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..ac18f2a 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";
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..675e6f5 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,13 @@
*/
package org.apache.dubbo.remoting.api;
-import io.netty.handler.codec.http2.Http2FrameLogger;
+public abstract class AbstractWireProtocol implements WireProtocol {
-import static io.netty.handler.logging.LogLevel.DEBUG;
+ private final ProtocolDetector detector;
-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 AbstractWireProtocol(ProtocolDetector detector) {
+ this.detector = detector;
+ }
@Override
public ProtocolDetector detector() {
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..a38648b
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.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.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;
+
+public abstract class AbstractPortUnificationServer extends AbstractServer {
+ private final List<WireProtocol> protocols;
+
+ 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;
+ }
+
+}
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..f817855 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,25 @@
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 void 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;
});
}
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 +60,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/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/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..3573a65 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;
@@ -44,7 +44,7 @@
*/
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 +121,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("6-1", "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("6-2", "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..4493bb3 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
@@ -273,4 +273,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..1f0c3f7 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,10 @@
*/
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.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.resource.GlobalResourceInitializer;
import org.apache.dubbo.common.utils.NetUtils;
@@ -42,6 +41,7 @@
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;
@@ -62,7 +62,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
@@ -186,13 +186,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 "
+ + getRemoteAddress() + ", error message is:" + cause.getMessage(), cause);
+
+ logger.error("6-1", "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 "
+
+ // 6-2 Client-side timeout
+
+ 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());
+
+ logger.error("6-2", "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 80%
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..e732f96 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,20 +14,24 @@
* 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.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 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;
@@ -40,7 +44,7 @@
import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetSocketAddress;
-import java.util.List;
+import java.util.Collection;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY;
@@ -53,11 +57,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 static final Logger logger = LoggerFactory.getLogger(NettyPortUnificationServer.class);
private final DefaultChannelGroup channels = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE);
@@ -70,41 +72,35 @@
/**
* 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;
- public PortUnificationServer(URL url) {
+ public NettyPortUnificationServer(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+
// 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;
- }
-
- 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 +111,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 +125,9 @@
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(),
+ channels, NettyPortUnificationServer.this);
p.addLast("negotiation-protocol", puHandler);
}
});
@@ -139,7 +135,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,7 +144,8 @@
channel = channelFuture.channel();
}
- protected void doClose() throws Throwable {
+ @Override
+ public void doClose(){
final long st = System.currentTimeMillis();
try {
@@ -165,7 +162,7 @@
logger.warn("Interrupted while shutting down", e);
}
- for (WireProtocol protocol : protocols) {
+ for (WireProtocol protocol : getProtocols()) {
protocol.close();
}
@@ -189,6 +186,16 @@
return channel.isActive();
}
+ @Override
+ public Collection<Channel> getChannels() {
+ return null;
+ }
+
+ @Override
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return null;
+ }
+
public InetSocketAddress getLocalAddress() {
return (InetSocketAddress) channel.localAddress();
}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java
similarity index 73%
rename from dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java
rename to dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java
index 634b406..7605798 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/PortUnificationServerHandler.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServerHandler.java
@@ -14,12 +14,17 @@
* 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.io.Bytes;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
+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.api.pu.ChannelOperator;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
@@ -32,25 +37,28 @@
import java.util.List;
import java.util.Set;
-public class PortUnificationServerHandler extends ByteToMessageDecoder {
+public class NettyPortUnificationServerHandler extends ByteToMessageDecoder {
private static final Logger LOGGER = LoggerFactory.getLogger(
- PortUnificationServerHandler.class);
+ NettyPortUnificationServerHandler.class);
private final ChannelGroup channels;
private final SslContext sslCtx;
private final URL url;
+ private final ChannelHandler handler;
private final boolean detectSsl;
private final List<WireProtocol> protocols;
- public PortUnificationServerHandler(URL url, SslContext sslCtx, boolean detectSsl,
- List<WireProtocol> protocols, ChannelGroup channels) {
+ public NettyPortUnificationServerHandler(URL url, SslContext sslCtx, boolean detectSsl,
+ List<WireProtocol> protocols, ChannelGroup channels,
+ ChannelHandler handler) {
this.url = url;
this.sslCtx = sslCtx;
this.protocols = protocols;
this.detectSsl = detectSsl;
this.channels = channels;
+ this.handler = handler;
}
@Override
@@ -67,8 +75,10 @@
@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.
- if (in.readableBytes() < 5) {
+ // size of telnet command ls is 2 bytes
+ if (in.readableBytes() < 2) {
return;
}
@@ -77,13 +87,15 @@
} else {
for (final WireProtocol protocol : protocols) {
in.markReaderIndex();
- final ProtocolDetector.Result result = protocol.detector().detect(ctx, in);
+ ChannelBuffer buf = new NettyBackedChannelBuffer(in);
+ final ProtocolDetector.Result result = protocol.detector().detect(buf);
in.resetReaderIndex();
switch (result) {
case UNRECOGNIZED:
continue;
case RECOGNIZED:
- protocol.configServerPipeline(url, ctx.pipeline(), sslCtx);
+ ChannelOperator operator = new NettyConfigOperator(channel, handler);
+ protocol.configServerProtocolHandler(url, operator);
ctx.pipeline().remove(this);
case NEED_MORE_DATA:
return;
@@ -111,12 +123,13 @@
ChannelPipeline p = ctx.pipeline();
p.addLast("ssl", sslCtx.newHandler(ctx.alloc()));
p.addLast("unificationA",
- new PortUnificationServerHandler(url, sslCtx, false, protocols, channels));
+ new NettyPortUnificationServerHandler(url, sslCtx, false, protocols, channels, handler));
p.remove(this);
}
private boolean isSsl(ByteBuf buf) {
- if (detectSsl) {
+ // 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..604f29d 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;
@@ -59,7 +59,7 @@
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 +94,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("5-1", "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..30cc4d5 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;
@@ -63,7 +63,7 @@
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 +97,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("5-1", "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/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/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/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/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 719e036..499fcf8 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
@@ -224,7 +224,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..81e46d1 100644
--- a/dubbo-rpc/dubbo-rpc-triple/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml
@@ -30,15 +30,34 @@
<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>
+ <groupId>org.reactivestreams</groupId>
+ <artifactId>reactive-streams</artifactId>
+ <version>${reactive.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-core</artifactId>
+ <version>${reactor.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-api</artifactId>
<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 +73,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>
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 c6a534d..e358eff 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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/AbstractTripleReactorPublisher.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/AbstractTripleReactorPublisher.java
new file mode 100644
index 0000000..9eab281
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/AbstractTripleReactorSubscriber.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/AbstractTripleReactorSubscriber.java
new file mode 100644
index 0000000..40acec2
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ClientTripleReactorPublisher.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ClientTripleReactorPublisher.java
new file mode 100644
index 0000000..1deab5a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ClientTripleReactorSubscriber.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ClientTripleReactorSubscriber.java
new file mode 100644
index 0000000..3d5936d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ServerTripleReactorPublisher.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ServerTripleReactorPublisher.java
new file mode 100644
index 0000000..33addf5
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ServerTripleReactorSubscriber.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/ServerTripleReactorSubscriber.java
new file mode 100644
index 0000000..32453ac
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/calls/ReactorClientCalls.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/calls/ReactorClientCalls.java
new file mode 100644
index 0000000..4ae6d45
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.calls;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+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.protocol.tri.reactive.ClientTripleReactorPublisher;
+import org.apache.dubbo.rpc.protocol.tri.reactive.ClientTripleReactorSubscriber;
+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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/calls/ReactorServerCalls.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/calls/ReactorServerCalls.java
new file mode 100644
index 0000000..6454d26
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.calls;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.apache.dubbo.rpc.protocol.tri.reactive.ServerTripleReactorPublisher;
+import org.apache.dubbo.rpc.protocol.tri.reactive.ServerTripleReactorSubscriber;
+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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/ManyToManyMethodHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/ManyToManyMethodHandler.java
new file mode 100644
index 0000000..df583f6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.reactive.calls.ReactorServerCalls;
+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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/ManyToOneMethodHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/ManyToOneMethodHandler.java
new file mode 100644
index 0000000..133f92a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.CallStreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.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 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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/OneToManyMethodHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/OneToManyMethodHandler.java
new file mode 100644
index 0000000..ef94d6d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.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-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/OneToOneMethodHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/reactive/handler/OneToOneMethodHandler.java
new file mode 100644
index 0000000..0a8b0a7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive.handler;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.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-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-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/ManyToManyMethodHandlerTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/ManyToManyMethodHandlerTest.java
new file mode 100644
index 0000000..6669eaa
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.apache.dubbo.rpc.protocol.tri.reactive.handler.ManyToManyMethodHandler;
+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-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/ManyToOneMethodHandlerTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/ManyToOneMethodHandlerTest.java
new file mode 100644
index 0000000..6cdde8d
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.apache.dubbo.rpc.protocol.tri.reactive.handler.ManyToOneMethodHandler;
+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-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/OneToManyMethodHandlerTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/OneToManyMethodHandlerTest.java
new file mode 100644
index 0000000..42b6453
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter;
+import org.apache.dubbo.rpc.protocol.tri.reactive.handler.OneToManyMethodHandler;
+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-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/OneToOneMethodHandlerTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/reactive/OneToOneMethodHandlerTest.java
new file mode 100644
index 0000000..5ba898c
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/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.rpc.protocol.tri.reactive;
+
+import org.apache.dubbo.rpc.protocol.tri.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-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..15295fe
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/PilotExchanger.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.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..4f4d8c7
--- /dev/null
+++ b/dubbo-xds/src/main/java/org/apache/dubbo/registry/xds/util/XdsChannel.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+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.ManagedChannel;
+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 javax.net.ssl.SSLException;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+public class XdsChannel {
+
+ private static final Logger logger = LoggerFactory.getLogger(XdsChannel.class);
+
+ private final ManagedChannel channel;
+
+ protected XdsChannel(URL url) {
+ ManagedChannel managedChannel = null;
+ try {
+ 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();
+ } catch (SSLException 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..4946166
--- /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;
+
+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..68473be
--- /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();
+ }
+
+ abstract static class ServerInfo {
+ 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 {
+ abstract List<ServerInfo> servers();
+
+ 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..ce6490b
--- /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;
+
+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..de0402e
--- /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
+ 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..e1a871d
--- /dev/null
+++ b/dubbo-xds/src/test/java/org/apache/dubbo/registry/xds/util/bootstrap/BootstrapperTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 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");
+ }
+
+ 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 44c6af6..f8c7a0e 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.12-SNAPSHOT</revision>
+ <revision>3.1.1-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>