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>