You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by il...@apache.org on 2019/03/15 09:14:21 UTC
[incubator-dubbo] branch 2.7.1-release updated: Enhancement and
Bugfix in 2.7.1 ( Part 3 ) (#3675)
This is an automated email from the ASF dual-hosted git repository.
iluo pushed a commit to branch 2.7.1-release
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
The following commit(s) were added to refs/heads/2.7.1-release by this push:
new 7fefb81 Enhancement and Bugfix in 2.7.1 ( Part 3 ) (#3675)
7fefb81 is described below
commit 7fefb8145c2afb98bb8fa32b8e84b05f6b7319e0
Author: Mercy Ma <me...@gmail.com>
AuthorDate: Fri Mar 15 17:14:08 2019 +0800
Enhancement and Bugfix in 2.7.1 ( Part 3 ) (#3675)
* Polish /apache/incubator-dubbo#1306 : @Reference bean name conflict
* Polish /apache/incubator-dubbo#3582 : service register support on nacos
* Polish /apache/incubator-dubbo#3582 : Add license header and remove hard-code version
* Polish /apache/incubator-dubbo#3582 : Remove junit
* Polish /apache/incubator-dubbo#3582 : Translate to English
---
.../annotation/AnnotationBeanNameBuilder.java | 142 ++++++
.../ReferenceAnnotationBeanPostProcessor.java | 6 +-
.../ServiceAnnotationBeanPostProcessor.java | 16 +-
.../factory/annotation/ServiceBeanNameBuilder.java | 113 -----
...est.java => AnnotationBeanNameBuilderTest.java} | 54 ++-
dubbo-registry/dubbo-registry-nacos/pom.xml | 153 ++++++
.../apache/dubbo/registry/nacos/NacosRegistry.java | 530 +++++++++++++++++++++
.../dubbo/registry/nacos/NacosRegistryFactory.java | 108 +++++
.../org.apache.dubbo.registry.RegistryFactory | 1 +
.../consumer/DemoServiceConsumerBootstrap.java | 59 +++
.../consumer/DemoServiceConsumerXmlBootstrap.java | 42 ++
.../provider/DemoServiceProviderBootstrap.java | 41 ++
.../provider/DemoServiceProviderXmlBootstrap.java | 37 ++
.../apache/dubbo/demo/service/DefaultService.java | 46 ++
.../org/apache/dubbo/demo/service/DemoService.java | 34 ++
.../META-INF/spring/dubbo-consumer-context.xml | 15 +
.../META-INF/spring/dubbo-provider-context.xml | 18 +
.../src/test/resources/consumer-config.properties | 6 +
.../src/test/resources/provider-config.properties | 14 +
dubbo-registry/pom.xml | 1 +
20 files changed, 1288 insertions(+), 148 deletions(-)
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java
new file mode 100644
index 0000000..610058a
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.spring.beans.factory.annotation;
+
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.registry.Registry;
+
+import org.springframework.core.env.Environment;
+
+import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
+import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
+import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
+import static org.springframework.util.StringUtils.hasText;
+
+/**
+ * The Bean Name Builder for the annotations {@link Service} and {@link Reference}
+ * <p>
+ * The naming rule is consistent with the the implementation {@link Registry} that is based on the service-name aware
+ * infrastructure, e.g Spring Cloud, Cloud Native and so on.
+ * <p>
+ * The pattern of bean name : ${category}:${protocol}:${serviceInterface}:${version}:${group}.
+ * <p>
+ * ${version} and ${group} are optional.
+ *
+ * @since 2.6.6
+ */
+class AnnotationBeanNameBuilder {
+
+ private static final String SEPARATOR = ":";
+
+ // Required properties
+
+ private final String category;
+
+ private final String protocol;
+
+ private final String interfaceClassName;
+
+ // Optional properties
+
+ private String version;
+
+ private String group;
+
+ private Environment environment;
+
+ private AnnotationBeanNameBuilder(String category, String protocol, String interfaceClassName) {
+ this.category = category;
+ this.protocol = protocol;
+ this.interfaceClassName = interfaceClassName;
+ }
+
+ private AnnotationBeanNameBuilder(Service service, Class<?> interfaceClass) {
+ this(PROVIDERS_CATEGORY, resolveProtocol(service.protocol()), resolveInterfaceName(service, interfaceClass));
+ this.group(service.group());
+ this.version(service.version());
+ }
+
+ private AnnotationBeanNameBuilder(Reference reference, Class<?> interfaceClass) {
+ this(CONSUMERS_CATEGORY, resolveProtocol(reference.protocol()), resolveInterfaceName(reference, interfaceClass));
+ this.group(reference.group());
+ this.version(reference.version());
+ }
+
+ public static AnnotationBeanNameBuilder create(Service service, Class<?> interfaceClass) {
+ return new AnnotationBeanNameBuilder(service, interfaceClass);
+ }
+
+ public static AnnotationBeanNameBuilder create(Reference reference, Class<?> interfaceClass) {
+ return new AnnotationBeanNameBuilder(reference, interfaceClass);
+ }
+
+ private static void append(StringBuilder builder, String value) {
+ if (hasText(value)) {
+ builder.append(SEPARATOR).append(value);
+ }
+ }
+
+ public AnnotationBeanNameBuilder group(String group) {
+ this.group = group;
+ return this;
+ }
+
+ public AnnotationBeanNameBuilder version(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public AnnotationBeanNameBuilder environment(Environment environment) {
+ this.environment = environment;
+ return this;
+ }
+
+ /**
+ * Resolve the protocol
+ *
+ * @param protocols one or more protocols
+ * @return if <code>protocols</code> == <code>null</code>, it will return
+ * {@link Constants#DEFAULT_PROTOCOL "dubbo"} as the default protocol
+ * @see Constants#DEFAULT_PROTOCOL
+ */
+ private static String resolveProtocol(String... protocols) {
+ String protocol = arrayToCommaDelimitedString(protocols);
+ return hasText(protocol) ? protocol : DEFAULT_PROTOCOL;
+ }
+
+ /**
+ * Build bean name while resolve the placeholders if possible.
+ *
+ * @return pattern : ${category}:${protocol}:${serviceInterface}:${version}:${group}
+ */
+ public String build() {
+ // Append the required properties
+ StringBuilder beanNameBuilder = new StringBuilder(category);
+ append(beanNameBuilder, protocol);
+ append(beanNameBuilder, interfaceClassName);
+ // Append the optional properties
+ append(beanNameBuilder, version);
+ append(beanNameBuilder, group);
+ String beanName = beanNameBuilder.toString();
+ // Resolve placeholders
+ return environment != null ? environment.resolvePlaceholders(beanName) : beanName;
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index e02c1e1..94bf9e8 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -188,7 +188,9 @@ public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBean
private String buildReferencedBeanName(Reference reference, Class<?> injectedType) {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, injectedType, getEnvironment());
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, injectedType);
+
+ builder.environment(getEnvironment());
return getEnvironment().resolvePlaceholders(builder.build());
}
@@ -261,4 +263,4 @@ public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBean
this.injectedFieldReferenceBeanCache.clear();
this.injectedMethodReferenceBeanCache.clear();
}
-}
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
index 22fa704..532026d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.config.spring.beans.factory.annotation;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.config.MethodConfig;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
@@ -290,8 +289,9 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
*/
private String generateServiceBeanName(Service service, Class<?> interfaceClass, String annotatedServiceBeanName) {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, interfaceClass, environment);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, interfaceClass);
+ builder.environment(environment);
return builder.build();
@@ -316,8 +316,9 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
}
if (interfaceClass == null) {
-
- Class<?>[] allInterfaces = annotatedServiceBeanClass.getInterfaces();
+ // Find all interfaces from the annotated class
+ // To resolve an issue : https://github.com/apache/incubator-dubbo/issues/3251
+ Class<?>[] allInterfaces = ClassUtils.getAllInterfacesForClass(annotatedServiceBeanClass);
if (allInterfaces.length > 0) {
interfaceClass = allInterfaces[0];
@@ -435,11 +436,6 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
- List<MethodConfig> methodConfigs = MethodConfig.constructMethodConfig(service.methods());
- if (!methodConfigs.isEmpty()) {
- builder.addPropertyValue("methods", methodConfigs);
- }
-
return builder.getBeanDefinition();
}
@@ -490,4 +486,4 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
this.classLoader = classLoader;
}
-}
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java
deleted file mode 100644
index 6cd7124..0000000
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.spring.beans.factory.annotation;
-
-import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.annotation.Service;
-import org.apache.dubbo.config.spring.ReferenceBean;
-import org.apache.dubbo.config.spring.ServiceBean;
-
-import org.springframework.core.env.Environment;
-import org.springframework.util.StringUtils;
-
-import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
-
-
-/**
- * Dubbo {@link Service @Service} Bean Builder
- *
- * @see Service
- * @see Reference
- * @see ServiceBean
- * @see ReferenceBean
- * @since 2.6.5
- */
-class ServiceBeanNameBuilder {
-
- private static final String SEPARATOR = ":";
-
- private final String interfaceClassName;
-
- private final Environment environment;
-
- // Optional
- private String version;
-
- private String group;
-
- private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) {
- this.interfaceClassName = interfaceClassName;
- this.environment = environment;
- }
-
- private ServiceBeanNameBuilder(Class<?> interfaceClass, Environment environment) {
- this(interfaceClass.getName(), environment);
- }
-
- private ServiceBeanNameBuilder(Service service, Class<?> interfaceClass, Environment environment) {
- this(resolveInterfaceName(service, interfaceClass), environment);
- this.group(service.group());
- this.version(service.version());
- }
-
- private ServiceBeanNameBuilder(Reference reference, Class<?> interfaceClass, Environment environment) {
- this(resolveInterfaceName(reference, interfaceClass), environment);
- this.group(reference.group());
- this.version(reference.version());
- }
-
- public static ServiceBeanNameBuilder create(Class<?> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(interfaceClass, environment);
- }
-
- public static ServiceBeanNameBuilder create(Service service, Class<?> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(service, interfaceClass, environment);
- }
-
- public static ServiceBeanNameBuilder create(Reference reference, Class<?> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(reference, interfaceClass, environment);
- }
-
- private static void append(StringBuilder builder, String value) {
- if (StringUtils.hasText(value)) {
- builder.append(SEPARATOR).append(value);
- }
- }
-
- public ServiceBeanNameBuilder group(String group) {
- this.group = group;
- return this;
- }
-
- public ServiceBeanNameBuilder version(String version) {
- this.version = version;
- return this;
- }
-
- public String build() {
- StringBuilder beanNameBuilder = new StringBuilder("ServiceBean");
- // Required
- append(beanNameBuilder, interfaceClassName);
- // Optional
- append(beanNameBuilder, version);
- append(beanNameBuilder, group);
- // Build
- String rawBeanName = beanNameBuilder.toString();
- // Resolve placeholders
- return environment.resolvePlaceholders(rawBeanName);
- }
-}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
similarity index 51%
rename from dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java
rename to dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
index b3616cd..2e79109 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
@@ -16,29 +16,31 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
-
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.api.DemoService;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.ReflectionUtils;
+import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.GROUP;
+import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.VERSION;
/**
- * {@link ServiceBeanNameBuilder} Test
+ * {@link AnnotationBeanNameBuilder} Test
*
- * @see ServiceBeanNameBuilder
- * @since 2.6.5
+ * @see AnnotationBeanNameBuilder
+ * @since 2.6.6
*/
-@Service(interfaceClass = DemoService.class, group = ServiceBeanNameBuilderTest.GROUP, version = ServiceBeanNameBuilderTest.VERSION,
+@Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION,
application = "application", module = "module", registry = {"1", "2", "3"})
-public class ServiceBeanNameBuilderTest {
+public class AnnotationBeanNameBuilderTest {
- @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "1.0.0",
+ @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}",
application = "application", module = "module", registry = {"1", "2", "3"})
static final Class<?> INTERFACE_CLASS = DemoService.class;
@@ -46,30 +48,36 @@ public class ServiceBeanNameBuilderTest {
static final String VERSION = "1.0.0";
- static final String BEAN_NAME = "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO";
-
- private MockEnvironment environment = new MockEnvironment();
+ private MockEnvironment environment;
- @Test
- public void testRequiredAttributes() {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment);
- Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService", builder.build());
+ @Before
+ public void prepare() {
+ environment = new MockEnvironment();
+ environment.setProperty("dubbo.version", "1.0.0");
}
@Test
public void testServiceAnnotation() {
- Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class);
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment);
- Assertions.assertEquals(BEAN_NAME,
+ Service service = AnnotationUtils.getAnnotation(AnnotationBeanNameBuilderTest.class, Service.class);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, INTERFACE_CLASS);
+ Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
+ builder.build());
+
+ builder.environment(environment);
+ Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
@Test
public void testReferenceAnnotation() {
- Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment);
- Assertions.assertEquals(BEAN_NAME,
+ Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(AnnotationBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, INTERFACE_CLASS);
+ Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:${dubbo.version}:DUBBO",
+ builder.build());
+
+ builder.environment(environment);
+ Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
-}
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/pom.xml b/dubbo-registry/dubbo-registry-nacos/pom.xml
new file mode 100644
index 0000000..f64fa02
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/pom.xml
@@ -0,0 +1,153 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>2.7.1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-nacos</artifactId>
+ <name>${project.artifactId}</name>
+ <description>The Nacos registry module of Dubbo project</description>
+
+ <properties>
+ <nacos.version>0.9.0</nacos.version>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.version}</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.version}</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>com.alibaba.nacos</groupId>
+ <artifactId>nacos-client</artifactId>
+ <version>${nacos.version}</version>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- Test Libraries -->
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-config-api</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-serialization-hessian2</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-config-spring</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-dubbo</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-remoting-netty4</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- REST support dependencies -->
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-rest</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-client</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-netty4</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jackson-provider</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxb-provider</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
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
new file mode 100644
index 0000000..31ca2a8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
@@ -0,0 +1,530 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.nacos;
+
+import org.apache.dubbo.common.Constants;
+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.UrlUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.naming.NamingService;
+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;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.Constants.ADMIN_PROTOCOL;
+import static org.apache.dubbo.common.Constants.CATEGORY_KEY;
+import static org.apache.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
+import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.DEFAULT_CATEGORY;
+import static org.apache.dubbo.common.Constants.GROUP_KEY;
+import static org.apache.dubbo.common.Constants.INTERFACE_KEY;
+import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.ROUTERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.VERSION_KEY;
+
+/**
+ * Nacos {@link Registry}
+ *
+ * @see #SERVICE_NAME_SEPARATOR
+ * @see #PAGINATION_SIZE
+ * @see #LOOKUP_INTERVAL
+ * @since 2.6.5
+ */
+public class NacosRegistry extends FailbackRegistry {
+
+ /**
+ * All supported categories
+ */
+ private static final String[] ALL_SUPPORTED_CATEGORIES = of(
+ PROVIDERS_CATEGORY,
+ CONSUMERS_CATEGORY,
+ ROUTERS_CATEGORY,
+ CONFIGURATORS_CATEGORY
+ );
+
+ private static final int CATEGORY_INDEX = 0;
+
+ private static final int SERVICE_INTERFACE_INDEX = 1;
+
+ private static final int SERVICE_VERSION_INDEX = 2;
+
+ private static final int SERVICE_GROUP_INDEX = 3;
+
+ private static final String WILDCARD = "*";
+
+ /**
+ * The separator for service name
+ *
+ * @revert change a constant to be configurable, it's designed for Windows file name that is compatible with old
+ * Nacos binary release(< 0.6.1)
+ */
+ private static final String SERVICE_NAME_SEPARATOR = System.getProperty("nacos.service.name.separator", ":");
+
+ /**
+ * The pagination size of query for Nacos service names(only for Dubbo-OPS)
+ */
+ private static final int PAGINATION_SIZE = Integer.getInteger("nacos.service.names.pagination.size", 100);
+
+ /**
+ * 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);
+
+ /**
+ * {@link ScheduledExecutorService} lookup Nacos service names(only for Dubbo-OPS)
+ */
+ private volatile ScheduledExecutorService scheduledExecutorService;
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final NamingService namingService;
+
+ private final ConcurrentMap<String, EventListener> nacosListeners;
+
+ public NacosRegistry(URL url, NamingService namingService) {
+ super(url);
+ this.namingService = namingService;
+ this.nacosListeners = new ConcurrentHashMap<String, EventListener>();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return "UP".equals(namingService.getServerStatus());
+ }
+
+ @Override
+ public List<URL> lookup(final URL url) {
+ final List<URL> urls = new LinkedList<URL>();
+ execute(new NamingServiceCallback() {
+ @Override
+ public void callback(NamingService namingService) throws NacosException {
+ List<String> serviceNames = getServiceNames(url, null);
+ for (String serviceName : serviceNames) {
+ List<Instance> instances = namingService.getAllInstances(serviceName);
+ urls.addAll(buildURLs(url, instances));
+ }
+ }
+ });
+ return urls;
+ }
+
+ @Override
+ public void doRegister(URL url) {
+ final String serviceName = getServiceName(url);
+ final Instance instance = createInstance(url);
+ execute(new NamingServiceCallback() {
+ public void callback(NamingService namingService) throws NacosException {
+ namingService.registerInstance(serviceName, instance);
+ }
+ });
+ }
+
+ @Override
+ public void doUnregister(final URL url) {
+ execute(new NamingServiceCallback() {
+ public void callback(NamingService namingService) throws NacosException {
+ String serviceName = getServiceName(url);
+ Instance instance = createInstance(url);
+ namingService.deregisterInstance(serviceName, instance.getIp(), instance.getPort());
+ }
+ });
+ }
+
+ @Override
+ public void doSubscribe(final URL url, final NotifyListener listener) {
+ List<String> serviceNames = getServiceNames(url, listener);
+ doSubscribe(url, listener, serviceNames);
+ }
+
+ private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
+ execute(new NamingServiceCallback() {
+ @Override
+ public void callback(NamingService namingService) throws NacosException {
+ for (String serviceName : serviceNames) {
+ List<Instance> instances = namingService.getAllInstances(serviceName);
+ notifySubscriber(url, listener, instances);
+ subscribeEventListener(serviceName, url, listener);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+ if (isAdminProtocol(url)) {
+ shutdownServiceNamesLookup();
+ }
+ }
+
+ private void shutdownServiceNamesLookup() {
+ if (scheduledExecutorService != null) {
+ scheduledExecutorService.shutdown();
+ }
+ }
+
+ /**
+ * Get the service names from the specified {@link URL url}
+ *
+ * @param url {@link URL}
+ * @param listener {@link NotifyListener}
+ * @return non-null
+ */
+ private List<String> getServiceNames(URL url, NotifyListener listener) {
+ if (isAdminProtocol(url)) {
+ scheduleServiceNamesLookup(url, listener);
+ return getServiceNamesForOps(url);
+ } else {
+ return doGetServiceNames(url);
+ }
+ }
+
+ private boolean isAdminProtocol(URL url) {
+ return ADMIN_PROTOCOL.equals(url.getProtocol());
+ }
+
+ private void scheduleServiceNamesLookup(final URL url, final NotifyListener listener) {
+ if (scheduledExecutorService == null) {
+ scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ List<String> serviceNames = getAllServiceNames();
+ filterData(serviceNames, new NacosDataFilter<String>() {
+ @Override
+ public boolean accept(String serviceName) {
+ boolean accepted = false;
+ for (String category : ALL_SUPPORTED_CATEGORIES) {
+ String prefix = category + SERVICE_NAME_SEPARATOR;
+ if (StringUtils.startsWith(serviceName, prefix)) {
+ accepted = true;
+ break;
+ }
+ }
+ return accepted;
+ }
+ });
+ doSubscribe(url, listener, serviceNames);
+ }
+ }, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Get the service names for Dubbo OPS
+ *
+ * @param url {@link URL}
+ * @return non-null
+ */
+ private List<String> getServiceNamesForOps(URL url) {
+ List<String> serviceNames = getAllServiceNames();
+ filterServiceNames(serviceNames, url);
+ return serviceNames;
+ }
+
+ private List<String> getAllServiceNames() {
+
+ final List<String> serviceNames = new LinkedList<String>();
+
+ execute(new NamingServiceCallback() {
+ @Override
+ public void callback(NamingService namingService) throws NacosException {
+
+ int pageIndex = 1;
+ ListView<String> listView = namingService.getServicesOfServer(pageIndex, PAGINATION_SIZE);
+ // First page data
+ List<String> firstPageData = listView.getData();
+ // Append first page into list
+ serviceNames.addAll(firstPageData);
+ // the total count
+ int count = listView.getCount();
+ // the number of pages
+ int pageNumbers = count / PAGINATION_SIZE;
+ int remainder = count % PAGINATION_SIZE;
+ // remain
+ if (remainder > 0) {
+ pageNumbers += 1;
+ }
+ // If more than 1 page
+ while (pageIndex < pageNumbers) {
+ listView = namingService.getServicesOfServer(++pageIndex, PAGINATION_SIZE);
+ serviceNames.addAll(listView.getData());
+ }
+
+ }
+ });
+
+ return serviceNames;
+ }
+
+ private void filterServiceNames(List<String> serviceNames, URL url) {
+
+ final String[] categories = getCategories(url);
+
+ final String targetServiceInterface = url.getServiceInterface();
+
+ final String targetVersion = url.getParameter(VERSION_KEY);
+
+ final String targetGroup = url.getParameter(GROUP_KEY);
+
+ filterData(serviceNames, new NacosDataFilter<String>() {
+ @Override
+ public boolean accept(String serviceName) {
+ // split service name to segments
+ // (required) segments[0] = category
+ // (required) segments[1] = serviceInterface
+ // (required) segments[2] = version
+ // (optional) segments[3] = group
+ String[] segments = StringUtils.split(serviceName, SERVICE_NAME_SEPARATOR);
+ int length = segments.length;
+ if (length < 3) { // must present 3 segments or more
+ return false;
+ }
+
+ String category = segments[CATEGORY_INDEX];
+ if (!ArrayUtils.contains(categories, category)) { // no match category
+ return false;
+ }
+
+ String serviceInterface = segments[SERVICE_INTERFACE_INDEX];
+ if (!WILDCARD.equals(targetServiceInterface) &&
+ !StringUtils.equals(targetServiceInterface, serviceInterface)) { // no match service interface
+ return false;
+ }
+
+ String version = segments[SERVICE_VERSION_INDEX];
+ if (!WILDCARD.equals(targetVersion) &&
+ !StringUtils.equals(targetVersion, version)) { // no match service version
+ return false;
+ }
+
+ String group = length > 3 ? segments[SERVICE_GROUP_INDEX] : null;
+ if (group != null && !WILDCARD.equals(targetGroup)
+ && !StringUtils.equals(targetGroup, group)) { // no match service group
+ return false;
+ }
+
+ return true;
+ }
+ });
+ }
+
+ private <T> void filterData(Collection<T> collection, NacosDataFilter<T> filter) {
+ Iterator<T> iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ T data = iterator.next();
+ if (!filter.accept(data)) { // remove if not accept
+ iterator.remove();
+ }
+ }
+ }
+
+ private List<String> doGetServiceNames(URL url) {
+ String[] categories = getCategories(url);
+ List<String> serviceNames = new ArrayList<String>(categories.length);
+ for (String category : categories) {
+ final String serviceName = getServiceName(url, category);
+ serviceNames.add(serviceName);
+ }
+ return serviceNames;
+ }
+
+ private List<URL> buildURLs(URL consumerURL, Collection<Instance> instances) {
+ if (instances.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<URL> urls = new LinkedList<URL>();
+ for (Instance instance : instances) {
+ URL url = buildURL(instance);
+ if (UrlUtils.isMatch(consumerURL, url)) {
+ urls.add(url);
+ }
+ }
+ return urls;
+ }
+
+ private void subscribeEventListener(String serviceName, final URL url, final NotifyListener listener)
+ throws NacosException {
+ if (!nacosListeners.containsKey(serviceName)) {
+ EventListener eventListener = new EventListener() {
+ public void onEvent(Event event) {
+ if (event instanceof NamingEvent) {
+ NamingEvent e = (NamingEvent) event;
+ notifySubscriber(url, listener, e.getInstances());
+ }
+ }
+ };
+ namingService.subscribe(serviceName, eventListener);
+ nacosListeners.put(serviceName, eventListener);
+ }
+ }
+
+ /**
+ * Notify the Healthy {@link Instance instances} to subscriber.
+ *
+ * @param url {@link URL}
+ * @param listener {@link NotifyListener}
+ * @param instances all {@link Instance instances}
+ */
+ private void notifySubscriber(URL url, NotifyListener listener, Collection<Instance> instances) {
+ List<Instance> healthyInstances = new LinkedList<Instance>(instances);
+ // Healthy Instances
+ filterHealthyInstances(healthyInstances);
+ List<URL> urls = buildURLs(url, healthyInstances);
+ NacosRegistry.this.notify(url, listener, urls);
+ }
+
+ /**
+ * Get the categories from {@link URL}
+ *
+ * @param url {@link URL}
+ * @return non-null array
+ */
+ private String[] getCategories(URL url) {
+ return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
+ ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
+ }
+
+ private URL buildURL(Instance instance) {
+ Map<String, String> metadata = instance.getMetadata();
+ String protocol = metadata.get(Constants.PROTOCOL_KEY);
+ String path = metadata.get(Constants.PATH_KEY);
+ URL url = new URL(protocol,
+ instance.getIp(),
+ instance.getPort(),
+ path,
+ instance.getMetadata());
+ return url;
+ }
+
+ private Instance createInstance(URL url) {
+ // Append default category if absent
+ String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ URL newURL = url.addParameter(Constants.CATEGORY_KEY, category);
+ newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol());
+ newURL = newURL.addParameter(Constants.PATH_KEY, url.getPath());
+ String ip = url.getHost();
+ int port = url.getPort();
+ Instance instance = new Instance();
+ instance.setIp(ip);
+ instance.setPort(port);
+ instance.setMetadata(new HashMap<String, String>(newURL.getParameters()));
+ return instance;
+ }
+
+ private String getServiceName(URL url) {
+ String category = url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
+ return getServiceName(url, category);
+ }
+
+ private String getServiceName(URL url, String category) {
+ StringBuilder serviceNameBuilder = new StringBuilder(category);
+ appendIfPresent(serviceNameBuilder, url, INTERFACE_KEY);
+ appendIfPresent(serviceNameBuilder, url, VERSION_KEY);
+ appendIfPresent(serviceNameBuilder, url, GROUP_KEY);
+ return serviceNameBuilder.toString();
+ }
+
+ private void appendIfPresent(StringBuilder target, URL url, String parameterName) {
+ String parameterValue = url.getParameter(parameterName);
+ if (!StringUtils.isBlank(parameterValue)) {
+ target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
+ }
+ }
+
+ private void execute(NamingServiceCallback callback) {
+ try {
+ callback.callback(namingService);
+ } catch (NacosException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error(e.getErrMsg(), e);
+ }
+ }
+ }
+
+ private void filterHealthyInstances(Collection<Instance> instances) {
+ filterData(instances, new NacosDataFilter<Instance>() {
+ @Override
+ public boolean accept(Instance data) {
+ return data.isEnabled();
+ }
+ });
+ }
+
+ private static <T> T[] of(T... values) {
+ return values;
+ }
+
+
+ /**
+ * A filter for Nacos data
+ *
+ * @since 2.6.5
+ */
+ private interface NacosDataFilter<T> {
+
+ /**
+ * Tests whether or not the specified data should be accepted.
+ *
+ * @param data The data to be tested
+ * @return <code>true</code> if and only if <code>data</code>
+ * should be accepted
+ */
+ boolean accept(T data);
+
+ }
+
+ /**
+ * {@link NamingService} Callback
+ *
+ * @since 2.6.5
+ */
+ interface NamingServiceCallback {
+
+ /**
+ * Callback
+ *
+ * @param namingService {@link NamingService}
+ * @throws NacosException
+ */
+ void callback(NamingService namingService) throws NacosException;
+
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java
new file mode 100644
index 0000000..1470c19
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.RegistryFactory;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.naming.NamingService;
+import com.alibaba.nacos.client.naming.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
+import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
+import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
+import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
+import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME;
+import static org.apache.dubbo.common.Constants.BACKUP_KEY;
+
+/**
+ * Nacos {@link RegistryFactory}
+ *
+ * @since 2.6.5
+ */
+public class NacosRegistryFactory extends AbstractRegistryFactory {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected Registry createRegistry(URL url) {
+ return new NacosRegistry(url, buildNamingService(url));
+ }
+
+ private NamingService buildNamingService(URL url) {
+ Properties nacosProperties = buildNacosProperties(url);
+ NamingService namingService = null;
+ try {
+ namingService = NacosFactory.createNamingService(nacosProperties);
+ } catch (NacosException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error(e.getErrMsg(), e);
+ }
+ throw new IllegalStateException(e);
+ }
+ return namingService;
+ }
+
+ private Properties buildNacosProperties(URL url) {
+ Properties properties = new Properties();
+ setServerAddr(url, properties);
+ setProperties(url, properties);
+ return properties;
+ }
+
+ private void setServerAddr(URL url, Properties properties) {
+ StringBuilder serverAddrBuilder =
+ new StringBuilder(url.getHost()) // Host
+ .append(":")
+ .append(url.getPort()); // Port
+
+ // Append backup parameter as other servers
+ String backup = url.getParameter(BACKUP_KEY);
+ if (backup != null) {
+ serverAddrBuilder.append(",").append(backup);
+ }
+
+ String serverAddr = serverAddrBuilder.toString();
+ properties.put(SERVER_ADDR, serverAddr);
+ }
+
+ private void setProperties(URL url, Properties properties) {
+ putPropertyIfAbsent(url, properties, NAMESPACE);
+ putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
+ putPropertyIfAbsent(url, properties, ENDPOINT);
+ putPropertyIfAbsent(url, properties, NAMESPACE);
+ putPropertyIfAbsent(url, properties, ACCESS_KEY);
+ putPropertyIfAbsent(url, properties, SECRET_KEY);
+ putPropertyIfAbsent(url, properties, CLUSTER_NAME);
+ }
+
+ private void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
+ String propertyValue = url.getParameter(propertyName);
+ if (StringUtils.isNotEmpty(propertyValue)) {
+ properties.setProperty(propertyName, propertyValue);
+ }
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..bb75467
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java
new file mode 100644
index 0000000..b411d4a
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.demo.consumer;
+
+import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.PropertySource;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link DemoService} consumer demo
+ */
+@EnableDubbo
+@PropertySource(value = "classpath:/consumer-config.properties")
+public class DemoServiceConsumerBootstrap {
+
+ @Reference(version = "${demo.service.version}")
+ private DemoService demoService;
+
+ @Reference(version = "${demo.service.version}", protocol = "rest")
+ private DemoService restDemoService;
+
+ @PostConstruct
+ public void init() throws InterruptedException {
+ for (int j = 0; j < 10; j++) {
+ System.out.println(demoService.sayName("小马哥(mercyblitz)"));
+ System.out.println(restDemoService.sayName("小马哥(mercyblitz)"));
+ }
+ Thread.sleep(TimeUnit.SECONDS.toMillis(5));
+ }
+
+ public static void main(String[] args) throws IOException {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(DemoServiceConsumerBootstrap.class);
+ context.refresh();
+ System.in.read();
+ context.close();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.java
new file mode 100644
index 0000000..cc25f8c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.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.demo.consumer;
+
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} consumer demo XML bootstrap
+ */
+public class DemoServiceConsumerXmlBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
+ context.setConfigLocation("/META-INF/spring/dubbo-consumer-context.xml");
+ context.refresh();
+ System.out.println("DemoService consumer (XML) is starting...");
+ DemoService demoService = context.getBean("demoService", DemoService.class);
+ for (int i = 0; i < 10; i++) {
+ System.out.println(demoService.sayName("小马哥(mercyblitz)"));
+ }
+ System.in.read();
+ context.close();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.java
new file mode 100644
index 0000000..c1d2ec4
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.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.demo.provider;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.PropertySource;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} provider demo
+ */
+@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.service")
+@PropertySource(value = "classpath:/provider-config.properties")
+public class DemoServiceProviderBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(DemoServiceProviderBootstrap.class);
+ context.refresh();
+ System.out.println("DemoService provider is starting...");
+ System.in.read();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.java
new file mode 100644
index 0000000..0a37f3f
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.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.demo.provider;
+
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} provider demo XML bootstrap
+ */
+public class DemoServiceProviderXmlBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
+ context.setConfigLocation("/META-INF/spring/dubbo-provider-context.xml");
+ context.refresh();
+ System.out.println("DemoService provider (XML) is starting...");
+ System.in.read();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java
new file mode 100644
index 0000000..56393ef
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.demo.service;
+
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.rpc.RpcContext;
+
+import org.springframework.beans.factory.annotation.Value;
+
+
+/**
+ * Default {@link DemoService}
+ *
+ * @since 2.6.5
+ */
+@Service(version = "${demo.service.version}")
+public class DefaultService implements DemoService {
+
+ @Value("${demo.service.name}")
+ private String serviceName;
+
+ public String sayName(String name) {
+ RpcContext rpcContext = RpcContext.getContext();
+ return String.format("Service [name :%s , protocol: %s , port : %d] %s(\"%s\") : Hello,%s",
+ serviceName,
+ rpcContext.getUrl().getProtocol(),
+ rpcContext.getLocalPort(),
+ rpcContext.getMethodName(),
+ name,
+ name);
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.java
new file mode 100644
index 0000000..0c80877
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.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.demo.service;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * DemoService
+ *
+ * @since 2.6.5
+ */
+@Path("/demo-service")
+public interface DemoService {
+
+ @GET
+ String sayName(@QueryParam("name") String name);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml
new file mode 100644
index 0000000..3fa32d7
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="dubbo-consumer-xml-demo"/>
+
+ <!-- Nacos Registry -->
+ <dubbo:registry address="nacos://127.0.0.1:8848"/>
+
+ <!-- Reference interface -->
+ <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.service.DemoService" version="2.0.0"/>
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml
new file mode 100644
index 0000000..36b1267
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+
+ <dubbo:application name="dubbo-provider-xml-demo"/>
+
+ <!-- Nacos Registry -->
+ <dubbo:registry address="nacos://127.0.0.1:8848"/>
+
+ <!-- Use random port as Dubbo -->
+ <dubbo:protocol name="dubbo" port="-1"/>
+
+ <dubbo:service interface="org.apache.dubbo.demo.service.DemoService" ref="demoService" version="2.0.0"/>
+
+ <bean id="demoService" class="org.apache.dubbo.demo.service.DefaultService"/>
+</beans>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties b/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties
new file mode 100644
index 0000000..d32e6e8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties
@@ -0,0 +1,6 @@
+## Dubbo Application info
+dubbo.application.name=dubbo-consumer-demo
+## Nacos registry address
+dubbo.registry.address=nacos://127.0.0.1:8848
+# @Reference version
+demo.service.version=1.0.0
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties b/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties
new file mode 100644
index 0000000..e2dd335
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties
@@ -0,0 +1,14 @@
+## Dubbo Application info
+dubbo.application.name=dubbo-provider-demo
+## Nacos registry address
+dubbo.registry.protocol=nacos
+dubbo.registry.address=127.0.0.1:8848
+## Exports multiple protocols
+### Dubbo Protocol using random port
+dubbo.protocols.dubbo.port=-1
+### REST protocol
+dubbo.protocols.rest.port=9090
+dubbo.protocols.rest.server=netty
+# Provider @Service info
+demo.service.version=1.0.0
+demo.service.name=demoService
\ No newline at end of file
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 33a329b..ea09685 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -36,5 +36,6 @@
<module>dubbo-registry-redis</module>
<module>dubbo-registry-consul</module>
<module>dubbo-registry-etcd3</module>
+ <module>dubbo-registry-nacos</module>
</modules>
</project>