You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/01/30 11:28:14 UTC

[dubbo-spi-extensions] 02/39: init project:move some sub-modules from apache/dubbo to here

This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch 2.7.x
in repository https://gitbox.apache.org/repos/asf/dubbo-spi-extensions.git

commit 193bdaa36c0038c71422653013cf20439cc3032d
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon May 18 13:53:13 2020 +0800

    init project:move some sub-modules from apache/dubbo to here
---
 .gitignore                                         |   43 +
 .mvn/wrapper/maven-wrapper.jar                     |  Bin 0 -> 47774 bytes
 .mvn/wrapper/maven-wrapper.properties              |    1 +
 CODE_OF_CONDUCT.md                                 |   46 +
 CONTRIBUTING.md                                    |   76 +
 LICENSE                                            |  202 +
 NOTICE                                             |    6 +
 PULL_REQUEST_TEMPLATE.md                           |   20 +
 SECURITY.md                                        |   31 +
 codestyle/checkstyle-suppressions.xml              |    8 +
 codestyle/checkstyle.xml                           |   38 +
 codestyle/dubbo_codestyle_for_idea.xml             |   16 +
 codestyle/manage_profiles.png                      |  Bin 0 -> 430 bytes
 compiler/pom.xml                                   |  237 +
 .../org/apache/dubbo/gen/AbstractGenerator.java    |  293 ++
 .../org/apache/dubbo/gen/dubbo/DubboGenerator.java |   42 +
 .../apache/dubbo/gen/grpc/DubboGrpcGenerator.java  |   41 +
 .../grpc/reactive/ReactorDubboGrpcGenerator.java   |   42 +
 .../gen/grpc/reactive/RxDubboGrpcGenerator.java    |   41 +
 compiler/src/main/resources/DubboGrpcStub.mustache |  312 ++
 compiler/src/main/resources/DubboStub.mustache     |   53 +
 .../main/resources/ReactorDubboGrpcStub.mustache   |  212 +
 .../src/main/resources/RxDubboGrpcStub.mustache    |  246 +
 .../dubbo-configcenter-apollo/pom.xml              |   52 +
 .../support/apollo/ApolloDynamicConfiguration.java |  258 +
 .../apollo/ApolloDynamicConfigurationFactory.java  |   31 +
 ...config.configcenter.DynamicConfigurationFactory |    1 +
 .../apollo/ApolloDynamicConfigurationTest.java     |  191 +
 .../src/test/resources/META-INF/app.properties     |    1 +
 .../src/test/resources/mockdata-dubbo.properties   |    2 +
 .../dubbo-configcenter-consul/pom.xml              |   47 +
 .../consul/ConsulDynamicConfiguration.java         |  218 +
 .../consul/ConsulDynamicConfigurationFactory.java  |   32 +
 ...config.configcenter.DynamicConfigurationFactory |    1 +
 .../consul/ConsulDynamicConfigurationTest.java     |  109 +
 .../dubbo-configcenter-etcd/pom.xml                |   74 +
 .../support/etcd/EtcdDynamicConfiguration.java     |  197 +
 .../etcd/EtcdDynamicConfigurationFactory.java      |   33 +
 ...config.configcenter.DynamicConfigurationFactory |    1 +
 .../support/etcd/EtcdDynamicConfigurationTest.java |  154 +
 .../dubbo-configcenter-nacos/pom.xml               |   45 +
 .../support/nacos/NacosDynamicConfiguration.java   |  381 ++
 .../nacos/NacosDynamicConfigurationFactory.java    |   41 +
 ...config.configcenter.DynamicConfigurationFactory |    1 +
 .../nacos/NacosDynamicConfigurationTest.java       |  188 +
 dubbo-spi-configcenter/pom.xml                     |   38 +
 dubbo-spi-container/dubbo-container-log4j/pom.xml  |   38 +
 .../dubbo/container/log4j/Log4jContainer.java      |  103 +
 .../internal/org.apache.dubbo.container.Container  |    1 +
 .../dubbo/container/log4j/Log4jContainerTest.java  |   36 +
 .../dubbo-container-logback/pom.xml                |   42 +
 .../dubbo/container/logback/LogbackContainer.java  |  108 +
 .../internal/org.apache.dubbo.container.Container  |    1 +
 .../container/logback/LogbackContainerTest.java    |   47 +
 dubbo-spi-container/pom.xml                        |   35 +
 dubbo-spi-filter/dubbo-filter-cache/pom.xml        |   48 +
 .../main/java/org/apache/dubbo/cache/Cache.java    |   43 +
 .../java/org/apache/dubbo/cache/CacheFactory.java  |   43 +
 .../org/apache/dubbo/cache/filter/CacheFilter.java |  133 +
 .../dubbo/cache/support/AbstractCacheFactory.java  |   72 +
 .../cache/support/expiring/ExpiringCache.java      |   77 +
 .../support/expiring/ExpiringCacheFactory.java     |   44 +
 .../dubbo/cache/support/expiring/ExpiringMap.java  |  374 ++
 .../apache/dubbo/cache/support/jcache/JCache.java  |   87 +
 .../dubbo/cache/support/jcache/JCacheFactory.java  |   48 +
 .../apache/dubbo/cache/support/lfu/LfuCache.java   |   80 +
 .../dubbo/cache/support/lfu/LfuCacheFactory.java   |   43 +
 .../apache/dubbo/cache/support/lru/LruCache.java   |   80 +
 .../dubbo/cache/support/lru/LruCacheFactory.java   |   43 +
 .../support/threadlocal/ThreadLocalCache.java      |   77 +
 .../threadlocal/ThreadLocalCacheFactory.java       |   43 +
 .../internal/org.apache.dubbo.cache.CacheFactory   |    4 +
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |    1 +
 .../apache/dubbo/cache/filter/CacheFilterTest.java |  137 +
 .../cache/support/AbstractCacheFactoryTest.java    |   33 +
 .../support/expiring/ExpiringCacheFactoryTest.java |   38 +
 .../cache/support/jcache/JCacheFactoryTest.java    |   55 +
 .../cache/support/lru/LruCacheFactoryTest.java     |   38 +
 .../threadlocal/ThreadLocalCacheFactoryTest.java   |   38 +
 dubbo-spi-filter/dubbo-filter-validation/pom.xml   |   72 +
 .../apache/dubbo/validation/MethodValidated.java   |   40 +
 .../org/apache/dubbo/validation/Validation.java    |   39 +
 .../org/apache/dubbo/validation/Validator.java     |   27 +
 .../dubbo/validation/filter/ValidationFilter.java  |  104 +
 .../validation/support/AbstractValidation.java     |   51 +
 .../support/jvalidation/JValidation.java           |   40 +
 .../validation/support/jvalidation/JValidator.java |  330 ++
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |    1 +
 .../org.apache.dubbo.validation.Validation         |    1 +
 .../validation/filter/ValidationFilterTest.java    |  137 +
 .../support/jvalidation/JValidationTest.java       |   50 +
 .../support/jvalidation/JValidatorTest.java        |   86 +
 .../jvalidation/mock/JValidatorTestTarget.java     |   41 +
 .../jvalidation/mock/ValidationParameter.java      |   31 +
 dubbo-spi-filter/pom.xml                           |   35 +
 .../dubbo-metadata-report-consul/pom.xml           |   41 +
 .../store/consul/ConsulMetadataReport.java         |  135 +
 .../store/consul/ConsulMetadataReportFactory.java  |   32 +
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 +
 .../dubbo-metadata-report-etcd/pom.xml             |   69 +
 .../metadata/store/etcd/EtcdMetadataReport.java    |  150 +
 .../store/etcd/EtcdMetadataReportFactory.java      |   50 +
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 +
 .../store/etcd/EtcdMetadata4TstService.java        |   28 +
 .../store/etcd/EtcdMetadataReportTest.java         |  259 +
 .../dubbo-metadata-report-nacos/pom.xml            |   41 +
 .../metadata/store/nacos/NacosMetadataReport.java  |  234 +
 .../store/nacos/NacosMetadataReportFactory.java    |   32 +
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 +
 .../store/nacos/NacosMetadata4TstService.java      |   28 +
 .../store/nacos/NacosMetadataReportTest.java       |  247 +
 .../dubbo-metadata-report-redis/pom.xml            |   57 +
 .../metadata/store/redis/RedisMetadataReport.java  |  201 +
 .../store/redis/RedisMetadataReportFactory.java    |   34 +
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 +
 .../store/redis/RedisMetadata4TstService.java      |   28 +
 .../store/redis/RedisMetadataReportTest.java       |  220 +
 dubbo-spi-metadata/pom.xml                         |   41 +
 dubbo-spi-registry/dubbo-registry-consul/pom.xml   |   62 +
 .../registry/consul/AbstractConsulRegistry.java    |   39 +
 .../dubbo/registry/consul/ConsulRegistry.java      |  360 ++
 .../registry/consul/ConsulRegistryFactory.java     |   32 +
 .../registry/consul/ConsulServiceDiscovery.java    |  394 ++
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 +
 .../dubbo/registry/consul/ConsulRegistryTest.java  |  135 +
 .../consul/ConsulServiceDiscoveryTest.java         |  108 +
 dubbo-spi-registry/dubbo-registry-default/pom.xml  |   68 +
 .../apache/dubbo/registry/dubbo/DubboRegistry.java |  161 +
 .../dubbo/registry/dubbo/DubboRegistryFactory.java |  118 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 .../registry/dubbo/AbstractRegistryService.java    |  237 +
 .../apache/dubbo/registry/dubbo/DemoService.java   |   27 +
 .../dubbo/registry/dubbo/DemoServiceImpl.java      |   32 +
 .../dubbo/registry/dubbo/DubboRegistryTest.java    |  155 +
 .../apache/dubbo/registry/dubbo/MockChannel.java   |  141 +
 .../apache/dubbo/registry/dubbo/MockedClient.java  |  298 ++
 .../registry/dubbo/RegistryDirectoryTest.java      | 1143 +++++
 .../dubbo/registry/dubbo/RegistryProtocolTest.java |  233 +
 .../registry/dubbo/RegistryStatusCheckerTest.java  |   69 +
 .../registry/dubbo/SimpleRegistryExporter.java     |   87 +
 .../registry/dubbo/SimpleRegistryService.java      |  146 +
 .../src/test/resources/log4j.xml                   |   38 +
 dubbo-spi-registry/dubbo-registry-etcd3/pom.xml    |   51 +
 .../apache/dubbo/registry/etcd/EtcdRegistry.java   |  359 ++
 .../dubbo/registry/etcd/EtcdRegistryFactory.java   |   36 +
 .../dubbo/registry/etcd/EtcdServiceDiscovery.java  |  208 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 +
 .../dubbo/registry/etcd/EtcdRegistryTest.java      |  327 ++
 .../registry/etcd/EtcdServiceDiscoveryTest.java    |  124 +
 dubbo-spi-registry/dubbo-registry-eureka/pom.xml   |   85 +
 .../eureka/ConfigurableEurekaInstanceConfig.java   |  369 ++
 .../registry/eureka/EurekaServiceDiscovery.java    |  279 ++
 .../apache/dubbo/registry/eureka/package-info.java |   22 +
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 +
 .../eureka/EurekaServiceDiscoveryTest.java         |   67 +
 dubbo-spi-registry/dubbo-registry-multiple/pom.xml |   65 +
 .../dubbo/registry/multiple/MultipleRegistry.java  |  337 ++
 .../registry/multiple/MultipleRegistryFactory.java |   33 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 .../multiple/MultipleRegistry2S2RTest.java         |  213 +
 .../multiple/MultipleRegistryTestUtil.java         |  145 +
 dubbo-spi-registry/dubbo-registry-nacos/pom.xml    |   83 +
 .../apache/dubbo/registry/nacos/NacosRegistry.java |  629 +++
 .../dubbo/registry/nacos/NacosRegistryFactory.java |   42 +
 .../registry/nacos/NacosServiceDiscovery.java      |  130 +
 .../dubbo/registry/nacos/NacosServiceName.java     |  234 +
 .../nacos/util/NacosInstanceManageUtil.java        |   67 +
 .../nacos/util/NacosNamingServiceUtils.java        |  192 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 +
 dubbo-spi-registry/dubbo-registry-redis/pom.xml    |   52 +
 .../apache/dubbo/registry/redis/RedisRegistry.java |  661 +++
 .../dubbo/registry/redis/RedisRegistryFactory.java |   34 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 .../dubbo/registry/redis/RedisRegistryTest.java    |  130 +
 dubbo-spi-registry/dubbo-registry-sofa/pom.xml     |  133 +
 .../apache/dubbo/registry/sofa/SofaRegistry.java   |  300 ++
 .../dubbo/registry/sofa/SofaRegistryConstants.java |   43 +
 .../dubbo/registry/sofa/SofaRegistryFactory.java   |   41 +
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 .../apache/dubbo/registry/sofa/HelloService.java   |   24 +
 .../dubbo/registry/sofa/HelloServiceImpl.java      |   44 +
 .../src/test/resources/log4j.properties            |    7 +
 dubbo-spi-registry/pom.xml                         |   41 +
 dubbo-spi-remoting/dubbo-remoting-etcd3/pom.xml    |  106 +
 .../dubbo/remoting/etcd/AbstractRetryPolicy.java   |   45 +
 .../apache/dubbo/remoting/etcd/ChildListener.java  |   25 +
 .../org/apache/dubbo/remoting/etcd/Constants.java  |   55 +
 .../org/apache/dubbo/remoting/etcd/EtcdClient.java |  191 +
 .../dubbo/remoting/etcd/EtcdTransporter.java       |   47 +
 .../apache/dubbo/remoting/etcd/RetryPolicy.java    |   31 +
 .../apache/dubbo/remoting/etcd/StateListener.java  |   27 +
 .../etcd/jetcd/ConnectionStateListener.java        |   31 +
 .../dubbo/remoting/etcd/jetcd/JEtcdClient.java     |  473 ++
 .../remoting/etcd/jetcd/JEtcdClientWrapper.java    |  754 +++
 .../remoting/etcd/jetcd/JEtcdTransporter.java      |   30 +
 .../dubbo/remoting/etcd/jetcd/RetryLoops.java      |   99 +
 .../dubbo/remoting/etcd/jetcd/RetryNTimes.java     |   36 +
 .../dubbo/remoting/etcd/option/OptionUtil.java     |   78 +
 .../remoting/etcd/support/AbstractEtcdClient.java  |  194 +
 .../org.apache.dubbo.remoting.etcd.EtcdTransporter |    1 +
 .../dubbo/remoting/etcd/jetcd/JEtcdClientTest.java |  427 ++
 .../etcd/jetcd/JEtcdClientWrapperTest.java         |  187 +
 .../dubbo/remoting/etcd/jetcd/LeaseTest.java       |  154 +
 dubbo-spi-remoting/dubbo-remoting-grizzly/pom.xml  |   42 +
 .../remoting/transport/grizzly/GrizzlyChannel.java |  198 +
 .../remoting/transport/grizzly/GrizzlyClient.java  |  112 +
 .../transport/grizzly/GrizzlyCodecAdapter.java     |  145 +
 .../remoting/transport/grizzly/GrizzlyHandler.java |  118 +
 .../remoting/transport/grizzly/GrizzlyServer.java  |  127 +
 .../transport/grizzly/GrizzlyTransporter.java      |   43 +
 .../internal/org.apache.dubbo.remoting.Transporter |    1 +
 .../transport/grizzly/GrizzlyTransporterTest.java  |   41 +
 dubbo-spi-remoting/dubbo-remoting-http/pom.xml     |   61 +
 .../org/apache/dubbo/remoting/http/HttpBinder.java |   39 +
 .../apache/dubbo/remoting/http/HttpHandler.java    |   39 +
 .../org/apache/dubbo/remoting/http/HttpServer.java |   72 +
 .../dubbo/remoting/http/jetty/JettyHttpBinder.java |   34 +
 .../dubbo/remoting/http/jetty/JettyHttpServer.java |  112 +
 .../remoting/http/servlet/BootstrapListener.java   |   37 +
 .../remoting/http/servlet/DispatcherServlet.java   |   65 +
 .../remoting/http/servlet/ServletHttpBinder.java   |   34 +
 .../remoting/http/servlet/ServletHttpServer.java   |   31 +
 .../remoting/http/servlet/ServletManager.java      |   50 +
 .../remoting/http/support/AbstractHttpServer.java  |  134 +
 .../remoting/http/tomcat/TomcatHttpBinder.java     |   31 +
 .../remoting/http/tomcat/TomcatHttpServer.java     |   93 +
 .../org.apache.dubbo.remoting.http.HttpBinder      |    3 +
 .../remoting/http/jetty/JettyHttpBinderTest.java   |   53 +
 .../remoting/http/tomcat/TomcatHttpBinderTest.java |   55 +
 dubbo-spi-remoting/dubbo-remoting-mina/pom.xml     |   52 +
 .../dubbo/remoting/transport/mina/MinaChannel.java |  191 +
 .../dubbo/remoting/transport/mina/MinaClient.java  |  174 +
 .../remoting/transport/mina/MinaCodecAdapter.java  |  167 +
 .../dubbo/remoting/transport/mina/MinaHandler.java |   95 +
 .../dubbo/remoting/transport/mina/MinaServer.java  |  112 +
 .../remoting/transport/mina/MinaTransporter.java   |   40 +
 .../internal/org.apache.dubbo.remoting.Transporter |    1 +
 .../transport/mina/ClientToServerTest.java         |   92 +
 .../remoting/transport/mina/ClientsTest.java       |   65 +
 .../org/apache/remoting/transport/mina/Hello.java  |   45 +
 .../transport/mina/MinaClientToServerTest.java     |   41 +
 .../org/apache/remoting/transport/mina/World.java  |   45 +
 .../remoting/transport/mina/WorldHandler.java      |   36 +
 dubbo-spi-remoting/dubbo-remoting-p2p/pom.xml      |   44 +
 .../java/org/apache/dubbo/remoting/p2p/Group.java  |   57 +
 .../org/apache/dubbo/remoting/p2p/Networker.java   |   39 +
 .../org/apache/dubbo/remoting/p2p/Networkers.java  |   47 +
 .../java/org/apache/dubbo/remoting/p2p/Peer.java   |   36 +
 .../dubbo/remoting/p2p/exchange/ExchangeGroup.java |   36 +
 .../remoting/p2p/exchange/ExchangeNetworker.java   |   35 +
 .../remoting/p2p/exchange/ExchangeNetworkers.java  |   45 +
 .../dubbo/remoting/p2p/exchange/ExchangePeer.java  |   26 +
 .../exchange/support/AbstractExchangeGroup.java    |  128 +
 .../p2p/exchange/support/ExchangeServerPeer.java   |  137 +
 .../p2p/exchange/support/FileExchangeGroup.java    |  135 +
 .../exchange/support/FileExchangeNetworker.java    |   34 +
 .../exchange/support/MulticastExchangeGroup.java   |  108 +
 .../support/MulticastExchangeNetworker.java        |   34 +
 .../dubbo/remoting/p2p/support/AbstractGroup.java  |  119 +
 .../dubbo/remoting/p2p/support/FileGroup.java      |  133 +
 .../dubbo/remoting/p2p/support/FileNetworker.java  |   34 +
 .../dubbo/remoting/p2p/support/MulticastGroup.java |  108 +
 .../remoting/p2p/support/MulticastNetworker.java   |   34 +
 .../dubbo/remoting/p2p/support/ServerPeer.java     |  124 +
 .../org.apache.dubbo.remoting.p2p.Networker        |    2 +
 .../support/MulticastExchangeNetworkerTest.java    |   81 +
 .../remoting/p2p/support/FileNetworkerTest.java    |   83 +
 .../p2p/support/MulticastNetworkerTest.java        |   70 +
 .../dubbo-remoting-zookeeper/pom.xml               |   56 +
 .../dubbo/remoting/zookeeper/ChildListener.java    |   25 +
 .../dubbo/remoting/zookeeper/DataListener.java     |   25 +
 .../apache/dubbo/remoting/zookeeper/EventType.java |   65 +
 .../dubbo/remoting/zookeeper/StateListener.java    |   33 +
 .../dubbo/remoting/zookeeper/ZookeeperClient.java  |   65 +
 .../remoting/zookeeper/ZookeeperTransporter.java   |   30 +
 .../zookeeper/curator/CuratorZookeeperClient.java  |  398 ++
 .../curator/CuratorZookeeperTransporter.java       |   30 +
 .../zookeeper/support/AbstractZookeeperClient.java |  226 +
 .../support/AbstractZookeeperTransporter.java      |  182 +
 ...e.dubbo.remoting.zookeeper.ZookeeperTransporter |    1 +
 .../curator/CuratorZookeeperClientTest.java        |  195 +
 .../curator/CuratorZookeeperTransporterTest.java   |   57 +
 .../support/AbstractZookeeperTransporterTest.java  |  225 +
 dubbo-spi-remoting/pom.xml                         |   40 +
 dubbo-spi-rpc/dubbo-rpc-grpc/pom.xml               |   68 +
 .../rpc/protocol/grpc/DubboHandlerRegistry.java    |   70 +
 .../apache/dubbo/rpc/protocol/grpc/GrpcConfig.java |   21 +
 .../dubbo/rpc/protocol/grpc/GrpcConstants.java     |   41 +
 .../dubbo/rpc/protocol/grpc/GrpcInvoker.java       |  118 +
 .../dubbo/rpc/protocol/grpc/GrpcOptionsUtils.java  |  225 +
 .../dubbo/rpc/protocol/grpc/GrpcProtocol.java      |  239 +
 .../grpc/ReferenceCountManagedChannel.java         |   85 +
 .../grpc/interceptors/ClientInterceptor.java       |   26 +
 .../grpc/interceptors/GrpcConfigurator.java        |   41 +
 .../grpc/interceptors/RpcContextInterceptor.java   |  100 +
 .../grpc/interceptors/ServerInterceptor.java       |   26 +
 .../grpc/interceptors/ServerTransportFilter.java   |   27 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 ...pc.protocol.grpc.interceptors.ClientInterceptor |    1 +
 ...pc.protocol.grpc.interceptors.ServerInterceptor |    1 +
 dubbo-spi-rpc/dubbo-rpc-hessian/pom.xml            |   62 +
 .../dubbo/rpc/protocol/hessian/Constants.java      |   35 +
 .../hessian/DubboHessianURLConnectionFactory.java  |   41 +
 .../rpc/protocol/hessian/HessianProtocol.java      |  202 +
 .../rpc/protocol/hessian/HttpClientConnection.java |   99 +
 .../hessian/HttpClientConnectionFactory.java       |   58 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../rpc/protocol/hessian/HessianProtocolTest.java  |  250 +
 .../dubbo/rpc/protocol/hessian/HessianService.java |   37 +
 .../rpc/protocol/hessian/HessianServiceImpl.java   |   75 +
 dubbo-spi-rpc/dubbo-rpc-http/pom.xml               |   61 +
 .../dubbo/rpc/protocol/http/HttpProtocol.java      |  191 +
 .../rpc/protocol/http/HttpProtocolErrorCode.java   |   29 +
 .../rpc/protocol/http/JsonRemoteInvocation.java    |   61 +
 .../rpc/protocol/http/JsonRpcProxyFactoryBean.java |   86 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../dubbo/rpc/protocol/http/HttpProtocolTest.java  |   92 +
 .../dubbo/rpc/protocol/http/HttpService.java       |   27 +
 .../dubbo/rpc/protocol/http/HttpServiceImpl.java   |   58 +
 dubbo-spi-rpc/dubbo-rpc-injvm/pom.xml              |   38 +
 .../dubbo/rpc/protocol/injvm/InjvmExporter.java    |   47 +
 .../dubbo/rpc/protocol/injvm/InjvmInvoker.java     |   65 +
 .../dubbo/rpc/protocol/injvm/InjvmProtocol.java    |  120 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../dubbo/rpc/protocol/injvm/DemoRequest.java      |   58 +
 .../dubbo/rpc/protocol/injvm/DemoService.java      |   44 +
 .../dubbo/rpc/protocol/injvm/DemoServiceImpl.java  |   78 +
 .../org/apache/dubbo/rpc/protocol/injvm/IEcho.java |   21 +
 .../rpc/protocol/injvm/InjvmProtocolTest.java      |  126 +
 .../dubbo/rpc/protocol/injvm/ProtocolTest.java     |   72 +
 .../org/apache/dubbo/rpc/protocol/injvm/Type.java  |   21 +
 dubbo-spi-rpc/dubbo-rpc-memcached/pom.xml          |   42 +
 .../rpc/protocol/memcached/MemcachedProtocol.java  |  122 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../protocol/memcached/MemcachedProtocolTest.java  |   21 +
 dubbo-spi-rpc/dubbo-rpc-native-thrift/pom.xml      |   49 +
 .../rpc/protocol/nativethrift/ThriftProtocol.java  |  189 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../src/test/idls/DemoService.thrift               |   17 +
 .../src/test/idls/UserService.thrift               |    6 +
 .../rpc/protocol/nativethrift/DemoService.java     | 5141 ++++++++++++++++++++
 .../rpc/protocol/nativethrift/DemoServiceImpl.java |   78 +
 .../protocol/nativethrift/ThriftProtocolTest.java  |   84 +
 .../rpc/protocol/nativethrift/UserService.java     |  952 ++++
 .../rpc/protocol/nativethrift/UserServiceImpl.java |   24 +
 dubbo-spi-rpc/dubbo-rpc-redis/pom.xml              |   58 +
 .../dubbo/rpc/protocol/redis/RedisProtocol.java    |  187 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../dubbo/rpc/protocol/redis/IDemoService.java     |   29 +
 .../rpc/protocol/redis/RedisProtocolTest.java      |  230 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 dubbo-spi-rpc/dubbo-rpc-rest/pom.xml               |  115 +
 .../rpc/protocol/rest/BaseRestProtocolServer.java  |   80 +
 .../apache/dubbo/rpc/protocol/rest/Constants.java  |   29 +
 .../rpc/protocol/rest/DubboHttpProtocolServer.java |  131 +
 .../rpc/protocol/rest/DubboResourceFactory.java    |   76 +
 .../rpc/protocol/rest/NettyRestProtocolServer.java |   73 +
 .../rpc/protocol/rest/RestConstraintViolation.java |   66 +
 .../dubbo/rpc/protocol/rest/RestProtocol.java      |  295 ++
 .../rpc/protocol/rest/RestProtocolServer.java      |   33 +
 .../dubbo/rpc/protocol/rest/RestServerFactory.java |   44 +
 .../dubbo/rpc/protocol/rest/RpcContextFilter.java  |  106 +
 .../rpc/protocol/rest/RpcExceptionMapper.java      |   51 +
 .../dubbo/rpc/protocol/rest/ViolationReport.java   |   48 +
 .../swagger/DubboSwaggerApiListingResource.java    |   48 +
 .../integration/swagger/DubboSwaggerService.java   |   43 +
 .../rpc/protocol/rest/support/ContentType.java     |   26 +
 .../rpc/protocol/rest/support/LoggingFilter.java   |  148 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../dubbo/rpc/protocol/rest/DemoService.java       |   45 +
 .../dubbo/rpc/protocol/rest/DemoServiceImpl.java   |   57 +
 .../dubbo/rpc/protocol/rest/RestProtocolTest.java  |  282 ++
 .../rpc/protocol/rest/RpcExceptionMapperTest.java  |   67 +
 .../DubboSwaggerApiListingResourceTest.java        |   62 +
 .../rest/integration/swagger/SwaggerService.java   |   34 +
 dubbo-spi-rpc/dubbo-rpc-rmi/pom.xml                |   42 +
 .../rpc/protocol/rmi/RmiRemoteInvocation.java      |   39 +
 .../apache/dubbo/rpc/protocol/rmi/RmiProtocol.java |  154 +
 .../rpc/protocol/rmi/RmiRemoteInvocation.java      |   64 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../apache/dubbo/rpc/protocol/rmi/DemoService.java |   47 +
 .../dubbo/rpc/protocol/rmi/DemoServiceImpl.java    |   89 +
 .../dubbo/rpc/protocol/rmi/RemoteService.java      |   26 +
 .../dubbo/rpc/protocol/rmi/RemoteServiceImpl.java  |   32 +
 .../dubbo/rpc/protocol/rmi/RmiProtocolTest.java    |  227 +
 .../org/apache/dubbo/rpc/protocol/rmi/Type.java    |   21 +
 dubbo-spi-rpc/dubbo-rpc-thrift/pom.xml             |   73 +
 .../rpc/protocol/thrift/ClassNameGenerator.java    |   31 +
 .../protocol/thrift/DubboClassNameGenerator.java   |   36 +
 .../protocol/thrift/ThriftClassNameGenerator.java  |   36 +
 .../dubbo/rpc/protocol/thrift/ThriftCodec.java     |  697 +++
 .../dubbo/rpc/protocol/thrift/ThriftConstants.java |   32 +
 .../dubbo/rpc/protocol/thrift/ThriftInvoker.java   |  171 +
 .../rpc/protocol/thrift/ThriftNativeCodec.java     |   96 +
 .../dubbo/rpc/protocol/thrift/ThriftProtocol.java  |  272 ++
 .../dubbo/rpc/protocol/thrift/ThriftType.java      |   51 +
 .../dubbo/rpc/protocol/thrift/ThriftUtils.java     |  135 +
 .../protocol/thrift/ext/MultiServiceProcessor.java |  121 +
 .../rpc/protocol/thrift/io/InputStreamWrapper.java |   88 +
 .../io/RandomAccessByteArrayOutputStream.java      |  117 +
 .../internal/org.apache.dubbo.remoting.Codec2      |    1 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 ...he.dubbo.rpc.protocol.thrift.ClassNameGenerator |    2 +
 .../src/test/java/$__ClassNameTestDubboStub.java   |  681 +++
 .../src/test/java/ClassNameTest.java               |   45 +
 .../src/test/java/ClassNameTestDubbo.java          |   29 +
 .../src/test/java/ClassNameTestThrift.java         |  767 +++
 .../apache/dubbo/rpc/gen/dubbo/$__DemoStub.java    | 4347 +++++++++++++++++
 .../java/org/apache/dubbo/rpc/gen/dubbo/Demo.java  |   42 +
 .../java/org/apache/dubbo/rpc/gen/thrift/Demo.java | 4753 ++++++++++++++++++
 .../dubbo/rpc/protocol/thrift/AbstractTest.java    |  150 +
 .../apache/dubbo/rpc/protocol/thrift/DemoImpl.java |   56 +
 .../dubbo/rpc/protocol/thrift/DubboDemoImpl.java   |   23 +
 .../protocol/thrift/FramedTransportFactory.java    |   30 +
 .../dubbo/rpc/protocol/thrift/MockedChannel.java   |  116 +
 .../rpc/protocol/thrift/ServerExceptionTest.java   |  100 +
 .../protocol/thrift/ServiceMethodNotFoundTest.java |  146 +
 .../dubbo/rpc/protocol/thrift/ThriftCodecTest.java |  477 ++
 .../dubbo/rpc/protocol/thrift/ThriftDemoImpl.java  |   22 +
 .../rpc/protocol/thrift/ThriftProtocolTest.java    |   87 +
 .../dubbo/rpc/protocol/thrift/ThriftUtilsTest.java |   88 +
 .../thrift/examples/DubboDemoConsumer.java         |   36 +
 .../thrift/examples/DubboDemoProvider.java         |   31 +
 .../src/test/resources/dubbo-demo-consumer.xml     |   32 +
 .../src/test/resources/dubbo-demo-provider.xml     |   34 +
 .../src/test/thrift/ClassNameTestDubbo.thrift      |    3 +
 .../src/test/thrift/ClassNameTestThrift.thrift     |    3 +
 .../dubbo-rpc-thrift/src/test/thrift/Demo.thrift   |   16 +
 dubbo-spi-rpc/dubbo-rpc-webservice/pom.xml         |   76 +
 .../protocol/webservice/WebServiceProtocol.java    |  204 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../dubbo/rpc/protocol/webservice/DemoService.java |   43 +
 .../rpc/protocol/webservice/DemoServiceImpl.java   |   80 +
 .../apache/dubbo/rpc/protocol/webservice/User.java |   38 +
 .../webservice/WebserviceProtocolTest.java         |   77 +
 dubbo-spi-rpc/dubbo-rpc-xml/README.md              |   88 +
 dubbo-spi-rpc/dubbo-rpc-xml/pom.xml                |   66 +
 .../xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java    |  196 +
 .../protocol/xmlrpc/XmlRpcProxyFactoryBean.java    |  142 +
 .../org.apache.dubbo.remoting.http.HttpBinder      |    1 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 .../rpc/protocol/xmlrpc/XmlRpcProtocolTest.java    |   90 +
 .../xml/rpc/protocol/xmlrpc/XmlRpcService.java     |   25 +
 .../xml/rpc/protocol/xmlrpc/XmlRpcServiceImpl.java |   51 +
 dubbo-spi-rpc/pom.xml                              |   45 +
 .../dubbo-serialization-avro/pom.xml               |   44 +
 .../common/serialize/avro/AvroObjectInput.java     |  118 +
 .../common/serialize/avro/AvroObjectOutput.java    |  106 +
 .../common/serialize/avro/AvroSerialization.java   |   52 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../dubbo-serialization-fastjson/pom.xml           |   43 +
 .../serialize/fastjson/FastJsonObjectInput.java    |  121 +
 .../serialize/fastjson/FastJsonObjectOutput.java   |  113 +
 .../serialize/fastjson/FastJsonSerialization.java  |   59 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../dubbo-serialization-fst/pom.xml                |   42 +
 .../dubbo/common/serialize/fst/FstFactory.java     |   53 +
 .../dubbo/common/serialize/fst/FstObjectInput.java |  117 +
 .../common/serialize/fst/FstObjectOutput.java      |  106 +
 .../common/serialize/fst/FstSerialization.java     |   58 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../dubbo-serialization-gson/pom.xml               |   42 +
 .../common/serialize/gson/GsonJsonObjectInput.java |  121 +
 .../serialize/gson/GsonJsonObjectOutput.java       |  108 +
 .../common/serialize/gson/GsonSerialization.java   |   53 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../serialize/gson/GsonJsonObjectOutputTest.java   |  143 +
 .../serialize/gson/GsonJsonSerializationTest.java  |   64 +
 .../apache/dubbo/common/serialize/gson/Image.java  |  120 +
 .../dubbo-serialization-jdk/pom.xml                |   43 +
 .../serialize/java/CompactedJavaSerialization.java |   59 +
 .../serialize/java/CompactedObjectInputStream.java |   64 +
 .../java/CompactedObjectOutputStream.java          |   43 +
 .../common/serialize/java/JavaObjectInput.java     |   91 +
 .../common/serialize/java/JavaObjectOutput.java    |   61 +
 .../common/serialize/java/JavaSerialization.java   |   59 +
 .../nativejava/NativeJavaObjectInput.java          |  118 +
 .../nativejava/NativeJavaObjectOutput.java         |  115 +
 .../nativejava/NativeJavaSerialization.java        |   60 +
 ...org.apache.dubbo.common.serialize.Serialization |    3 +
 .../dubbo-serialization-kryo/pom.xml               |   47 +
 .../common/serialize/kryo/CompatibleKryo.java      |   54 +
 .../common/serialize/kryo/KryoObjectInput.java     |  162 +
 .../common/serialize/kryo/KryoObjectOutput.java    |  118 +
 .../common/serialize/kryo/KryoSerialization.java   |   58 +
 .../serialize/kryo/optimized/KryoObjectInput2.java |  168 +
 .../kryo/optimized/KryoObjectOutput2.java          |  122 +
 .../kryo/optimized/KryoSerialization2.java         |   57 +
 .../serialize/kryo/utils/AbstractKryoFactory.java  |  158 +
 .../common/serialize/kryo/utils/KryoUtils.java     |   44 +
 .../serialize/kryo/utils/PooledKryoFactory.java    |   40 +
 .../serialize/kryo/utils/PrototypeKryoFactory.java |   32 +
 .../serialize/kryo/utils/ReflectionUtils.java      |   33 +
 .../kryo/utils/ThreadLocalKryoFactory.java         |   39 +
 ...org.apache.dubbo.common.serialize.Serialization |    2 +
 .../dubbo-serialization-native-hession/pom.xml     |   42 +
 .../serialize/hessian/Hessian2ObjectInput.java     |   98 +
 .../serialize/hessian/Hessian2ObjectOutput.java    |   95 +
 .../serialize/hessian/Hessian2Serialization.java   |   53 +
 .../hessian/Hessian2SerializerFactory.java         |   42 +
 .../serialize/hessian/Java8SerializerFactory.java  |   88 +
 .../hessian/serializer/java8/DurationHandle.java   |   53 +
 .../hessian/serializer/java8/InstantHandle.java    |   54 +
 .../serializer/java8/Java8TimeSerializer.java      |   57 +
 .../hessian/serializer/java8/LocalDateHandle.java  |   55 +
 .../serializer/java8/LocalDateTimeHandle.java      |   55 +
 .../hessian/serializer/java8/LocalTimeHandle.java  |   57 +
 .../hessian/serializer/java8/MonthDayHandle.java   |   53 +
 .../serializer/java8/OffsetDateTimeHandle.java     |   55 +
 .../hessian/serializer/java8/OffsetTimeHandle.java |   55 +
 .../hessian/serializer/java8/PeriodHandle.java     |   56 +
 .../hessian/serializer/java8/YearHandle.java       |   52 +
 .../hessian/serializer/java8/YearMonthHandle.java  |   53 +
 .../hessian/serializer/java8/ZoneIdHandle.java     |   52 +
 .../hessian/serializer/java8/ZoneIdSerializer.java |   43 +
 .../hessian/serializer/java8/ZoneOffsetHandle.java |   51 +
 .../serializer/java8/ZonedDateTimeHandle.java      |   62 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../serialize/hessian/Java8TimeSerializerTest.java |  150 +
 .../protobuf/support/wrapper/MapValue.java         |  793 +++
 .../protobuf/support/wrapper/ThrowablePB.java      | 2662 ++++++++++
 .../dubbo-serialization-protobuf/pom.xml           |   96 +
 .../support/GenericProtobufJsonObjectInput.java    |  164 +
 .../support/GenericProtobufJsonObjectOutput.java   |  161 +
 .../support/GenericProtobufJsonSerialization.java  |   54 +
 .../support/GenericProtobufObjectInput.java        |  146 +
 .../support/GenericProtobufObjectOutput.java       |  157 +
 .../support/GenericProtobufSerialization.java      |   63 +
 .../serialize/protobuf/support/ProtobufUtils.java  |  206 +
 .../protobuf/support/ProtobufWrappedException.java |   68 +
 .../src/main/proto/MapValue.proto                  |   27 +
 .../src/main/proto/ThrowablePB.proto               |   64 +
 ...org.apache.dubbo.common.serialize.Serialization |    2 +
 .../dubbo-serialization-protostuff/pom.xml         |   53 +
 .../protostuff/ProtostuffObjectInput.java          |  136 +
 .../protostuff/ProtostuffObjectOutput.java         |  130 +
 .../protostuff/ProtostuffSerialization.java        |   58 +
 .../dubbo/common/serialize/protostuff/Wrapper.java |   33 +
 .../protostuff/delegate/SqlDateDelegate.java       |   55 +
 .../protostuff/delegate/TimeDelegate.java          |   57 +
 .../protostuff/delegate/TimestampDelegate.java     |   57 +
 .../serialize/protostuff/utils/WrapperUtils.java   |  115 +
 ...org.apache.dubbo.common.serialize.Serialization |    1 +
 .../dubbo-serialization-test/pom.xml               |   86 +
 .../serialize/avro/AvroObjectInputOutputTest.java  |  196 +
 .../serialize/avro/AvroSerializationTest.java      |   64 +
 .../base/AbstractSerializationPersonFailTest.java  |  141 +
 .../base/AbstractSerializationPersonOkTest.java    |   92 +
 .../serialize/base/AbstractSerializationTest.java  | 1212 +++++
 .../fastjson/FastJsonObjectInputTest.java          |  199 +
 .../fastjson/FastJsonObjectOutputTest.java         |  142 +
 .../fastjson/FastJsonSerializationTest.java        |   62 +
 .../dubbo/common/serialize/fst/FstFactoryTest.java |   32 +
 .../common/serialize/fst/FstObjectInputTest.java   |   51 +
 .../common/serialize/fst/FstObjectOutputTest.java  |  186 +
 .../common/serialize/fst/FstSerializationTest.java |   63 +
 .../serialize/hessian2/Hessian2PersonOkTest.java   |  212 +
 .../hessian2/Hessian2SerializationTest.java        |  210 +
 .../jdk/CompactedJavaSerializationTest.java        |   27 +
 .../serialize/jdk/JavaSerializationTest.java       |   27 +
 .../common/serialize/jdk/JdkPersonOkTest.java      |   30 +
 .../serialize/jdk/NativeJavaSerializationTest.java |   26 +
 .../common/serialize/kryo/KryoPersonOkTest.java    |   29 +
 .../serialize/kryo/KyroSerializationTest.java      |   26 +
 .../common/serialize/kryo/ReflectionUtilsTest.java |   46 +
 .../dubbo/common/serialize/model/AnimalEnum.java   |   21 +
 .../dubbo/common/serialize/model/BizException.java |   29 +
 .../model/BizExceptionNoDefaultConstructor.java    |   26 +
 .../dubbo/common/serialize/model/Organization.java |   30 +
 .../dubbo/common/serialize/model/Person.java       |   95 +
 .../common/serialize/model/SerializablePerson.java |   97 +
 .../dubbo/common/serialize/model/media/Image.java  |  120 +
 .../dubbo/common/serialize/model/media/Media.java  |  205 +
 .../common/serialize/model/media/MediaContent.java |   78 +
 .../common/serialize/model/person/BigPerson.java   |  151 +
 .../common/serialize/model/person/FullAddress.java |  202 +
 .../common/serialize/model/person/PersonInfo.java  |  206 +
 .../serialize/model/person/PersonStatus.java       |   22 +
 .../dubbo/common/serialize/model/person/Phone.java |  139 +
 .../support/AbstractProtobufSerializationTest.java |  372 ++
 .../GenericProtobufJsonObjectOutputTest.java       |  206 +
 .../GenericProtobufJsonSerializationTest.java      |   23 +
 .../support/GenericProtobufSerializationTest.java  |   23 +
 .../serialize/protobuf/support/model/GooglePB.java | 3431 +++++++++++++
 .../protobuf/support/model/ServiceInterface.java   |   21 +
 .../protostuff/ProtostuffObjectOutputTest.java     |  242 +
 .../protostuff/ProtostuffSerializationTest.java    |   27 +
 .../support/SerializableClassRegistryTest.java     |   38 +
 .../src/test/proto/GooglePB.proto                  |   51 +
 .../src/test/resources/log4j.xml                   |   31 +
 .../SimpleDO.fc                                    |    2 +
 dubbo-spi-serialization/pom.xml                    |   43 +
 mvnw                                               |  227 +
 mvnw.cmd                                           |  143 +
 pom.xml                                            |   40 +
 598 files changed, 76515 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..15002c4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# maven ignore
+target/
+*.jar
+!.mvn/wrapper/*
+*.war
+*.zip
+*.tar
+*.tar.gz
+.flattened-pom.xml
+
+# eclipse ignore
+.settings/
+.project
+.classpath
+
+# idea ignore
+.idea/
+*.ipr
+*.iml
+*.iws
+
+# temp ignore
+*.log
+*.cache
+*.diff
+*.patch
+*.tmp
+
+# system ignore
+.DS_Store
+Thumbs.db
+*.orig
+
+# license check result
+license-list
+
+# grpc compiler
+compiler/gradle.properties
+compiler/build/*
+compiler/.gradle/*
+
+# protobuf
+dubbo-serialization/dubbo-serialization-protobuf/build/*
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..41c70a7
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..a3ba20e
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..b7c31c2
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@dubbo.apache.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..c92e4a1
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,76 @@
+
+## Contributing to dubbo
+Dubbo is released under the non-restrictive Apache 2.0 license, and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you want to contribute even something trivial please do not hesitate, but follow the guidelines below.
+
+### Sign the Contributor License Agreement
+Before we accept a non-trivial patch or pull request we will need you to sign the Contributor License Agreement. Signing the contributor’s agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests.
+
+### Contact
+
+#### Mailing list
+
+The mailing list is the recommended way for discussing almost anything that related to Dubbo. Please refer to this [guide](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide) for detailed documentation on how to subscribe.
+
+- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): the develop mailing list, you can ask question here if you have encountered any problem when using or developing Dubbo.
+- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): all the commits will be sent to this mailing list. You can subscribe to it if you are interested in Dubbo's development.
+- [notifications@dubbo.apache.org](mailto:notifications-subscribe@dubbo.apache.org): all the Github [issue](https://github.com/apache/dubbo/issues) updates and [pull request](https://github.com/apache/dubbo/pulls) updates will be sent to this mailing list.
+
+### Reporting issue
+
+Please follow the [template](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md) for reporting any issues.
+
+### Code Conventions
+Our code style is almost in line with the standard java conventions (Popular IDE's default setting satisfy this), with the following additional restricts:  
+* If there are more than 120 characters in current line, start a new line.
+
+* Make sure all new .java files to have a simple Javadoc class comment with at least a @date tag identifying birth, and preferably at least a paragraph on what the class is for.
+
+* Add the ASF license header comment to all new .java files (copy from existing files in the project)
+
+* Make sure no @author tag added to the file you contribute since @author tag is not used at Apache, other ways such as cvs will record all your contributions fairly.
+
+* Add some Javadocs and, if you change the namespace, some XSD doc elements.
+
+* A few unit tests should be added for a new feature or an important bugfix.
+
+* If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project).
+
+* When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes #XXX at the end of the commit message (where XXX is the issue number).
+
+### Contribution flow
+
+This is a rough outline of what a contributor's workflow looks like:
+
+* Fork the current repository
+* Create a topic branch from where to base the contribution. This is usually master.
+* Make commits of logical units.
+* Make sure commit messages are in the proper format (see below).
+* Push changes in a topic branch to your forked repository.
+* Follow the checklist in the [pull request template](https://github.com/apache/dubbo/blob/master/PULL_REQUEST_TEMPLATE.md)
+* Before you sending out the pull request, please sync your forked repository with remote repository, this will make your pull request simple and clear. See guide below:
+```
+git remote add upstream git@github.com:apache/dubbo.git
+git fetch upstream
+git rebase upstream/master
+git checkout -b your_awesome_patch
+... add some work
+git push origin your_awesome_patch
+```
+* Submit a pull request to apache/dubbo and wait for the reply.
+
+Thanks for contributing!
+
+### Code style
+
+We provide a template file [dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml) for IntelliJ idea, you can import it to you IDE. 
+If you use Eclipse you can config manually by referencing the same file.
+
+**NOTICE**
+
+It is very important to set the dubbo_codestyle_for_idea.xml, otherwise you will fail to pass the Travis CI. Steps to set the code style are as below:
+
+1. Enter `Editor > Code Style`
+2. To manage a code style scheme, in the Code Style page, select the desired scheme from the drop-down list, and click ![manage profiles](codestyle/manage_profiles.png).
+From the drop-down list, select `Import Scheme`, then select this option `IntelliJ IDEA code style XML` to import scheme
+3. In the Scheme field, type the name of the new scheme and press ⏎ to save the changes.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..7a95db5
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,6 @@
+Apache Dubbo
+Copyright 2018-2020 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..20c5854
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,20 @@
+## What is the purpose of the change
+
+XXXXX
+
+## Brief changelog
+
+XXXXX
+
+## Verifying this change
+
+XXXXX
+
+Follow this checklist to help us incorporate your contribution quickly and easily:
+
+- [x] Make sure there is a [GITHUB_issue](https://github.com/apache/dubbo/issues) field for the change (usually before you start working on it). Trivial changes like typos do not require a GITHUB issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue.
+- [ ] Format the pull request title like `[Dubbo-XXX] Fix UnknownException when host config not exist #XXX`. Each commit in the pull request should have a meaningful subject line and body.
+- [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
+- [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add sample in [dubbo samples](https://github.com/apache/dubbo-samples) project.
+- [ ] Run `mvn clean install -DskipTests=false` & `mvn clean test-compile failsafe:integration-test` to make sure unit-test and integration-test pass.
+- [ ] If this contribution is large, please follow the [Software Donation Guide](https://github.com/apache/dubbo/wiki/Software-donation-guide).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..ac0c2ad
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,31 @@
+# Security Policy
+
+## Supported Versions
+
+Below is a table that shows versions that accept security fix.
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 2.7.x   | :white_check_mark: |
+| 2.6.x   | :white_check_mark: |
+| 2.5.x   | :x: |
+
+
+## Reporting a Vulnerability
+
+The Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its software projects. Apache Dubbo is highly sensitive and forthcoming to issues pertaining to its features and functionality.
+
+If you have apprehensions regarding Dubbo's security or you discover vulnerability or potential threat, don’t hesitate to get in touch with the Apache Dubbo Security Team by dropping a mail at security@dubbo.apache.org. In the mail, specify the description of the issue or potential threat. You are also urged to recommend the way to reproduce and replicate the issue. The Dubbo community will get back to you after assessing and analysing the findings.
+
+PLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on public domain.
+
+## VULNERABILITY HANDLING
+
+An overview of the vulnerability handling process is:
+
+* The reporter reports the vulnerability privately to Apache.
+* The appropriate project's security team works privately with the reporter to resolve the vulnerability.
+* A new release of the Apache product concerned is made that includes the fix.
+* The vulnerability is publicly announced.
+
+A more detailed description of the process can be found [here](https://www.apache.org/security/committers.html).
diff --git a/codestyle/checkstyle-suppressions.xml b/codestyle/checkstyle-suppressions.xml
new file mode 100644
index 0000000..1817cf7
--- /dev/null
+++ b/codestyle/checkstyle-suppressions.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE suppressions PUBLIC
+        "-//Puppy Crawl//DTD Suppressions 1.1//EN"
+        "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+<suppressions>
+    <suppress files="[\\/]src[\\/]main[\\/]java[\\/]com[\\/]alibaba[\\/]com[\\/]caucho[\\/]hessian" checks=".*"/>
+    <suppress files="Yylex\.java" checks="AvoidEscapedUnicodeCharacters"/>
+</suppressions>
\ No newline at end of file
diff --git a/codestyle/checkstyle.xml b/codestyle/checkstyle.xml
new file mode 100644
index 0000000..050fbed
--- /dev/null
+++ b/codestyle/checkstyle.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+        "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
+
+<module name="Checker">
+    <property name="charset" value="UTF-8"/>
+    <property name="fileExtensions" value="java"/>
+
+    <module name="Header">
+        <property name="headerFile" value="/checkstyle-header.txt"/>
+        <property name="fileExtensions" value="java"/>
+    </module>
+
+    <!-- TreeWalker Checks -->
+    <module name="TreeWalker">
+        <module name="SuppressWarningsHolder"/>
+
+        <module name="AvoidStarImport"/>
+        <module name="AvoidEscapedUnicodeCharacters">
+            <property name="allowEscapesForControlCharacters" value="true"/>
+            <property name="allowByTailComment" value="true"/>
+            <property name="allowNonPrintableEscapes" value="true"/>
+        </module>
+        <module name="NoLineWrap"/>
+        <module name="OuterTypeFilename"/>
+        <module name="UnusedImports"/>
+        <module name="RedundantImport"/>
+
+        <module name="EqualsHashCode"/>
+
+        <!--<module name="CustomImportOrder">-->
+        <!--<property name="specialImportsRegExp" value="org.apache.dubbo.*"/>-->
+        <!--<property name="sortImportsInGroupAlphabetically" value="false"/>-->
+        <!--<property name="customImportOrderRules" value="SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE###STATIC"/>-->
+        <!--</module>-->
+    </module>
+</module>
diff --git a/codestyle/dubbo_codestyle_for_idea.xml b/codestyle/dubbo_codestyle_for_idea.xml
new file mode 100644
index 0000000..1f87caa
--- /dev/null
+++ b/codestyle/dubbo_codestyle_for_idea.xml
@@ -0,0 +1,16 @@
+<code_scheme name="dubbo_codestyle">
+    <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99"/>
+    <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99"/>
+    <option name="IMPORT_LAYOUT_TABLE">
+        <value>
+            <package name="org.apache.dubbo" withSubpackages="true" static="false"/>
+            <emptyLine/>
+            <package name="" withSubpackages="true" static="false"/>
+            <emptyLine/>
+            <package name="javax" withSubpackages="true" static="false"/>
+            <package name="java" withSubpackages="true" static="false"/>
+            <emptyLine/>
+            <package name="" withSubpackages="true" static="true"/>
+        </value>
+    </option>
+</code_scheme>
\ No newline at end of file
diff --git a/codestyle/manage_profiles.png b/codestyle/manage_profiles.png
new file mode 100644
index 0000000..1664d67
Binary files /dev/null and b/codestyle/manage_profiles.png differ
diff --git a/compiler/pom.xml b/compiler/pom.xml
new file mode 100644
index 0000000..c3d4201
--- /dev/null
+++ b/compiler/pom.xml
@@ -0,0 +1,237 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>21</version>
+    </parent>
+
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-compiler</artifactId>
+    <version>0.0.1</version>
+
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven_compiler_version>3.6.0</maven_compiler_version>
+        <maven_jar_version>3.0.2</maven_jar_version>
+        <maven_source_version>3.0.1</maven_source_version>
+        <maven_javadoc_version>3.0.1</maven_javadoc_version>
+        <java_source_version>1.8</java_source_version>
+        <java_target_version>1.8</java_target_version>
+        <file_encoding>UTF-8</file_encoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.salesforce.servicelibs</groupId>
+            <artifactId>grpc-contrib</artifactId>
+            <version>0.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.salesforce.servicelibs</groupId>
+            <artifactId>jprotoc</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven_compiler_version}</version>
+                <configuration>
+                    <compilerArgument>-proc:none</compilerArgument>
+                    <fork>true</fork>
+                    <source>${java_source_version}</source>
+                    <target>${java_target_version}</target>
+                    <encoding>${file_encoding}</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>${maven_jar_version}</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <mainClass>org.apache.dubbo.gen.grpc.DubboGrpcGenerator</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- Optional, used to build directly executable packages (without using 'java -jar'),
+            for example 'artifactId-1.0.0-osx-x86_64.exe', 'artifactId-1.0.0-osx-x86_64.exe' -->
+            <plugin>
+                <groupId>com.salesforce.servicelibs</groupId>
+                <artifactId>canteen-maven-plugin</artifactId>
+                <version>1.0.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>bootstrap</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <properties>
+                <log4j2_version>2.11.1</log4j2_version>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <version>${maven_source_version}</version>
+                        <executions>
+                            <execution>
+                                <id>attach-sources</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <version>${maven_javadoc_version}</version>
+                        <executions>
+                            <execution>
+                                <id>attach-javadoc</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                    <goal>aggregate</goal>
+                                </goals>
+                                <configuration>
+                                    <additionalDependencies>
+                                        <additionalDependency>
+                                            <groupId>org.apache.logging.log4j</groupId>
+                                            <artifactId>log4j-api</artifactId>
+                                            <version>${log4j2_version}</version>
+                                        </additionalDependency>
+                                        <additionalDependency>
+                                            <groupId>org.apache.logging.log4j</groupId>
+                                            <artifactId>log4j-core</artifactId>
+                                            <version>${log4j2_version}</version>
+                                        </additionalDependency>
+                                    </additionalDependencies>
+                                </configuration>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <show>public</show>
+                            <charset>UTF-8</charset>
+                            <encoding>UTF-8</encoding>
+                            <docencoding>UTF-8</docencoding>
+                            <links>
+                                <link>http://docs.oracle.com/javase/8/docs/api</link>
+                            </links>
+                            <doclint>none</doclint>
+                            <excludePackageNames>
+                                org.apache.dubbo.demo,org.apache.dubbo.demo.*
+                            </excludePackageNames>
+                            <doctitle>Apache Dubbo ${project.version} API</doctitle>
+                            <windowtitle>Apache Dubbo ${project.version} API</windowtitle>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <name>dubbo-compiler</name>
+    <description>Dubbo customized RPC stub compiler.</description>
+    <url>https://github.com/apache/dubbo</url>
+    <inceptionYear>2011</inceptionYear>
+    <licenses>
+        <license>
+            <name>Apache License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <scm>
+        <url>https://github.com/apache/dubbo</url>
+        <connection>scm:git:https://github.com/apache/dubbo.git</connection>
+        <developerConnection>scm:git:https://github.com/apache/dubbo.git</developerConnection>
+        <tag>HEAD</tag>
+    </scm>
+    <mailingLists>
+        <mailingList>
+            <name>Development List</name>
+            <subscribe>dev-subscribe@dubbo.apache.org</subscribe>
+            <unsubscribe>dev-unsubscribe@dubbo.apache.org</unsubscribe>
+            <post>dev@dubbo.apache.org</post>
+        </mailingList>
+        <mailingList>
+            <name>Commits List</name>
+            <subscribe>commits-subscribe@dubbo.apache.org</subscribe>
+            <unsubscribe>commits-unsubscribe@dubbo.apache.org</unsubscribe>
+            <post>commits@dubbo.apache.org</post>
+        </mailingList>
+        <mailingList>
+            <name>Issues List</name>
+            <subscribe>issues-subscribe@dubbo.apache.org</subscribe>
+            <unsubscribe>issues-unsubscribe@dubbo.apache.org</unsubscribe>
+            <post>issues@dubbo.apache.org</post>
+        </mailingList>
+    </mailingLists>
+    <developers>
+        <developer>
+            <id>dubbo.io</id>
+            <name>The Dubbo Project Contributors</name>
+            <email>dev-subscribe@dubbo.apache.org</email>
+            <url>http://dubbo.apache.org/</url>
+        </developer>
+    </developers>
+
+    <organization>
+        <name>The Apache Software Foundation</name>
+        <url>http://www.apache.org/</url>
+    </organization>
+
+    <issueManagement>
+        <system>Github Issues</system>
+        <url>https://github.com/apache/dubbo/issues</url>
+    </issueManagement>
+</project>
\ No newline at end of file
diff --git a/compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java b/compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java
new file mode 100644
index 0000000..3e8dee1
--- /dev/null
+++ b/compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.gen;
+
+import com.google.common.base.Strings;
+import com.google.common.html.HtmlEscapers;
+import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
+import com.google.protobuf.DescriptorProtos.FileOptions;
+import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
+import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
+import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location;
+import com.google.protobuf.compiler.PluginProtos;
+import com.salesforce.jprotoc.Generator;
+import com.salesforce.jprotoc.GeneratorException;
+import com.salesforce.jprotoc.ProtoTypeMap;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public abstract class AbstractGenerator extends Generator {
+
+    private static final int SERVICE_NUMBER_OF_PATHS = 2;
+    private static final int METHOD_NUMBER_OF_PATHS = 4;
+
+    protected abstract String getClassPrefix();
+
+    protected abstract String getClassSuffix();
+
+    private String getServiceJavaDocPrefix() {
+        return "    ";
+    }
+
+    private String getMethodJavaDocPrefix() {
+        return "        ";
+    }
+
+    @Override
+    public List<PluginProtos.CodeGeneratorResponse.File> generateFiles(PluginProtos.CodeGeneratorRequest request) throws GeneratorException {
+        final ProtoTypeMap typeMap = ProtoTypeMap.of(request.getProtoFileList());
+
+        List<FileDescriptorProto> protosToGenerate = request.getProtoFileList().stream()
+                .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName()))
+                .collect(Collectors.toList());
+
+        List<ServiceContext> services = findServices(protosToGenerate, typeMap);
+        return generateFiles(services);
+    }
+
+    private List<ServiceContext> findServices(List<FileDescriptorProto> protos, ProtoTypeMap typeMap) {
+        List<ServiceContext> contexts = new ArrayList<>();
+
+        protos.forEach(fileProto -> {
+            for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount(); serviceNumber++) {
+                ServiceContext serviceContext = buildServiceContext(
+                    fileProto.getService(serviceNumber),
+                    typeMap,
+                    fileProto.getSourceCodeInfo().getLocationList(),
+                    serviceNumber
+                );
+                serviceContext.protoName = fileProto.getName();
+                serviceContext.packageName = extractPackageName(fileProto);
+                contexts.add(serviceContext);
+            }
+        });
+
+        return contexts;
+    }
+
+    private String extractPackageName(FileDescriptorProto proto) {
+        FileOptions options = proto.getOptions();
+        if (options != null) {
+            String javaPackage = options.getJavaPackage();
+            if (!Strings.isNullOrEmpty(javaPackage)) {
+                return javaPackage;
+            }
+        }
+
+        return Strings.nullToEmpty(proto.getPackage());
+    }
+
+    private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto, ProtoTypeMap typeMap, List<Location> locations, int serviceNumber) {
+        ServiceContext serviceContext = new ServiceContext();
+        serviceContext.fileName = getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java";
+        serviceContext.className = getClassPrefix() + serviceProto.getName() + getClassSuffix();
+        serviceContext.serviceName = serviceProto.getName();
+        serviceContext.deprecated = serviceProto.getOptions() != null && serviceProto.getOptions().getDeprecated();
+
+        List<Location> allLocationsForService = locations.stream()
+                .filter(location ->
+                    location.getPathCount() >= 2 &&
+                       location.getPath(0) == FileDescriptorProto.SERVICE_FIELD_NUMBER &&
+                       location.getPath(1) == serviceNumber
+                )
+                .collect(Collectors.toList());
+
+        Location serviceLocation = allLocationsForService.stream()
+                .filter(location -> location.getPathCount() == SERVICE_NUMBER_OF_PATHS)
+                .findFirst()
+                .orElseGet(Location::getDefaultInstance);
+        serviceContext.javaDoc = getJavaDoc(getComments(serviceLocation), getServiceJavaDocPrefix());
+
+        for (int methodNumber = 0; methodNumber < serviceProto.getMethodCount(); methodNumber++) {
+            MethodContext methodContext = buildMethodContext(
+                serviceProto.getMethod(methodNumber),
+                typeMap,
+                locations,
+                methodNumber
+            );
+
+            serviceContext.methods.add(methodContext);
+            serviceContext.methodTypes.add(methodContext.inputType);
+            serviceContext.methodTypes.add(methodContext.outputType);
+        }
+        return serviceContext;
+    }
+
+    private MethodContext buildMethodContext(MethodDescriptorProto methodProto, ProtoTypeMap typeMap, List<Location> locations, int methodNumber) {
+        MethodContext methodContext = new MethodContext();
+        methodContext.methodName = lowerCaseFirst(methodProto.getName());
+        methodContext.inputType = typeMap.toJavaTypeName(methodProto.getInputType());
+        methodContext.outputType = typeMap.toJavaTypeName(methodProto.getOutputType());
+        methodContext.deprecated = methodProto.getOptions() != null && methodProto.getOptions().getDeprecated();
+        methodContext.isManyInput = methodProto.getClientStreaming();
+        methodContext.isManyOutput = methodProto.getServerStreaming();
+        methodContext.methodNumber = methodNumber;
+
+        Location methodLocation = locations.stream()
+                .filter(location ->
+                    location.getPathCount() == METHOD_NUMBER_OF_PATHS &&
+                        location.getPath(METHOD_NUMBER_OF_PATHS - 1) == methodNumber
+                )
+                .findFirst()
+                .orElseGet(Location::getDefaultInstance);
+        methodContext.javaDoc = getJavaDoc(getComments(methodLocation), getMethodJavaDocPrefix());
+
+        if (!methodProto.getClientStreaming() && !methodProto.getServerStreaming()) {
+            methodContext.reactiveCallsMethodName = "oneToOne";
+            methodContext.grpcCallsMethodName = "asyncUnaryCall";
+        }
+        if (!methodProto.getClientStreaming() && methodProto.getServerStreaming()) {
+            methodContext.reactiveCallsMethodName = "oneToMany";
+            methodContext.grpcCallsMethodName = "asyncServerStreamingCall";
+        }
+        if (methodProto.getClientStreaming() && !methodProto.getServerStreaming()) {
+            methodContext.reactiveCallsMethodName = "manyToOne";
+            methodContext.grpcCallsMethodName = "asyncClientStreamingCall";
+        }
+        if (methodProto.getClientStreaming() && methodProto.getServerStreaming()) {
+            methodContext.reactiveCallsMethodName = "manyToMany";
+            methodContext.grpcCallsMethodName = "asyncBidiStreamingCall";
+        }
+        return methodContext;
+    }
+
+    private String lowerCaseFirst(String s) {
+        return Character.toLowerCase(s.charAt(0)) + s.substring(1);
+    }
+
+    private List<PluginProtos.CodeGeneratorResponse.File> generateFiles(List<ServiceContext> services) {
+        return services.stream()
+                .map(this::buildFile)
+                .collect(Collectors.toList());
+    }
+
+    private PluginProtos.CodeGeneratorResponse.File buildFile(ServiceContext context) {
+        String content = applyTemplate(getClassPrefix() + getClassSuffix() + "Stub.mustache", context);
+        return PluginProtos.CodeGeneratorResponse.File
+                .newBuilder()
+                .setName(absoluteFileName(context))
+                .setContent(content)
+                .build();
+    }
+
+    private String absoluteFileName(ServiceContext ctx) {
+        String dir = ctx.packageName.replace('.', '/');
+        if (Strings.isNullOrEmpty(dir)) {
+            return ctx.fileName;
+        } else {
+            return dir + "/" + ctx.fileName;
+        }
+    }
+
+    private String getComments(Location location) {
+        return location.getLeadingComments().isEmpty() ? location.getTrailingComments() : location.getLeadingComments();
+    }
+
+    private String getJavaDoc(String comments, String prefix) {
+        if (!comments.isEmpty()) {
+            StringBuilder builder = new StringBuilder("/**\n")
+                    .append(prefix).append(" * <pre>\n");
+            Arrays.stream(HtmlEscapers.htmlEscaper().escape(comments).split("\n"))
+                    .map(line -> line.replace("*/", "&#42;&#47;").replace("*", "&#42;"))
+                    .forEach(line -> builder.append(prefix).append(" * ").append(line).append("\n"));
+            builder
+                    .append(prefix).append(" * </pre>\n")
+                    .append(prefix).append(" */");
+            return builder.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Template class for proto Service objects.
+     */
+    private class ServiceContext {
+        // CHECKSTYLE DISABLE VisibilityModifier FOR 8 LINES
+        public String fileName;
+        public String protoName;
+        public String packageName;
+        public String className;
+        public String serviceName;
+        public boolean deprecated;
+        public String javaDoc;
+        public List<MethodContext> methods = new ArrayList<>();
+
+        public Set<String> methodTypes = new HashSet<>();
+
+        public List<MethodContext> unaryRequestMethods() {
+            return methods.stream().filter(m -> !m.isManyInput).collect(Collectors.toList());
+        }
+
+        public List<MethodContext> unaryMethods() {
+            return methods.stream().filter(m -> (!m.isManyInput && !m.isManyOutput)).collect(Collectors.toList());
+        }
+
+        public List<MethodContext> serverStreamingMethods() {
+            return methods.stream().filter(m -> !m.isManyInput && m.isManyOutput).collect(Collectors.toList());
+        }
+
+        public List<MethodContext> biStreamingMethods() {
+            return methods.stream().filter(m -> m.isManyInput).collect(Collectors.toList());
+        }
+    }
+
+    /**
+     * Template class for proto RPC objects.
+     */
+    private class MethodContext {
+        // CHECKSTYLE DISABLE VisibilityModifier FOR 10 LINES
+        public String methodName;
+        public String inputType;
+        public String outputType;
+        public boolean deprecated;
+        public boolean isManyInput;
+        public boolean isManyOutput;
+        public String reactiveCallsMethodName;
+        public String grpcCallsMethodName;
+        public int methodNumber;
+        public String javaDoc;
+
+        // This method mimics the upper-casing method ogf gRPC to ensure compatibility
+        // See https://github.com/grpc/grpc-java/blob/v1.8.0/compiler/src/java_plugin/cpp/java_generator.cpp#L58
+        public String methodNameUpperUnderscore() {
+            StringBuilder s = new StringBuilder();
+            for (int i = 0; i < methodName.length(); i++) {
+                char c = methodName.charAt(i);
+                s.append(Character.toUpperCase(c));
+                if ((i < methodName.length() - 1) && Character.isLowerCase(c) && Character.isUpperCase(methodName.charAt(i + 1))) {
+                    s.append('_');
+                }
+            }
+            return s.toString();
+        }
+
+        public String methodNamePascalCase() {
+            String mn = methodName.replace("_", "");
+            return String.valueOf(Character.toUpperCase(mn.charAt(0))) + mn.substring(1);
+        }
+
+        public String methodNameCamelCase() {
+            String mn = methodName.replace("_", "");
+            return String.valueOf(Character.toLowerCase(mn.charAt(0))) + mn.substring(1);
+        }
+    }
+}
diff --git a/compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java b/compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java
new file mode 100644
index 0000000..1c35f0a
--- /dev/null
+++ b/compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.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.gen.dubbo;
+
+import org.apache.dubbo.gen.AbstractGenerator;
+
+import com.salesforce.jprotoc.ProtocPlugin;
+
+public class DubboGenerator extends AbstractGenerator {
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            ProtocPlugin.generate(new DubboGenerator());
+        } else {
+            ProtocPlugin.debug(new DubboGenerator(), args[0]);
+        }
+    }
+
+    @Override
+    protected String getClassPrefix() {
+        return "";
+    }
+
+    @Override
+    protected String getClassSuffix() {
+        return "Dubbo";
+    }
+}
\ No newline at end of file
diff --git a/compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java b/compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java
new file mode 100644
index 0000000..dafdd5a
--- /dev/null
+++ b/compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.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.gen.grpc;
+
+import org.apache.dubbo.gen.AbstractGenerator;
+
+import com.salesforce.jprotoc.ProtocPlugin;
+
+public class DubboGrpcGenerator extends AbstractGenerator {
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            ProtocPlugin.generate(new DubboGrpcGenerator());
+        } else {
+            ProtocPlugin.debug(new DubboGrpcGenerator(), args[0]);
+        }
+    }
+
+    @Override
+    protected String getClassPrefix() {
+        return "Dubbo";
+    }
+
+    protected String getClassSuffix() {
+        return "Grpc";
+    }
+}
diff --git a/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java b/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java
new file mode 100644
index 0000000..dd9520b
--- /dev/null
+++ b/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.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.gen.grpc.reactive;
+
+import org.apache.dubbo.gen.AbstractGenerator;
+
+import com.salesforce.jprotoc.ProtocPlugin;
+
+public class ReactorDubboGrpcGenerator extends AbstractGenerator {
+
+    @Override
+    protected String getClassPrefix() {
+        return "ReactorDubbo";
+    }
+
+    @Override
+    protected String getClassSuffix() {
+        return "Grpc";
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            ProtocPlugin.generate(new ReactorDubboGrpcGenerator());
+        } else {
+            ProtocPlugin.debug(new ReactorDubboGrpcGenerator(), args[0]);
+        }
+    }
+}
diff --git a/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java b/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java
new file mode 100644
index 0000000..028332a
--- /dev/null
+++ b/compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.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.gen.grpc.reactive;
+
+import org.apache.dubbo.gen.AbstractGenerator;
+
+import com.salesforce.jprotoc.ProtocPlugin;
+
+public class RxDubboGrpcGenerator extends AbstractGenerator {
+    @Override
+    protected String getClassPrefix() {
+        return "RxDubbo";
+    }
+
+    @Override
+    protected String getClassSuffix() {
+        return "Grpc";
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            ProtocPlugin.generate(new RxDubboGrpcGenerator());
+        } else {
+            ProtocPlugin.debug(new RxDubboGrpcGenerator(), args[0]);
+        }
+    }
+}
diff --git a/compiler/src/main/resources/DubboGrpcStub.mustache b/compiler/src/main/resources/DubboGrpcStub.mustache
new file mode 100644
index 0000000..a02d0c4
--- /dev/null
+++ b/compiler/src/main/resources/DubboGrpcStub.mustache
@@ -0,0 +1,312 @@
+{{#packageName}}
+    package {{packageName}};
+{{/packageName}}
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.config.ReferenceConfigBase;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+
+import static {{packageName}}.{{serviceName}}Grpc.getServiceDescriptor;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
+
+{{#deprecated}}
+    @java.lang.Deprecated
+{{/deprecated}}
+@javax.annotation.Generated(
+value = "by DubboGrpc generator",
+comments = "Source: {{protoName}}")
+public final class {{className}} {
+private {{className}}() {}
+
+public static class Dubbo{{serviceName}}Stub implements I{{serviceName}} {
+
+protected URL url;
+protected ReferenceConfigBase<?> referenceConfig;
+
+protected {{serviceName}}Grpc.{{serviceName}}BlockingStub blockingStub;
+protected {{serviceName}}Grpc.{{serviceName}}FutureStub futureStub;
+protected {{serviceName}}Grpc.{{serviceName}}Stub stub;
+
+public Dubbo{{serviceName}}Stub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+this.url = url;
+this.referenceConfig = referenceConfig;
+
+blockingStub = {{serviceName}}Grpc.newBlockingStub(channel).build(channel, callOptions);
+futureStub = {{serviceName}}Grpc.newFutureStub(channel).build(channel, callOptions);
+stub = {{serviceName}}Grpc.newStub(channel).build(channel, callOptions);
+}
+
+{{#unaryMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{outputType}} {{methodName}}({{inputType}} request) {
+    return blockingStub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(request);
+    }
+
+    public com.google.common.util.concurrent.ListenableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request) {
+    return futureStub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(request);
+    }
+
+    public void {{methodName}}({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> responseObserver){
+    stub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(request, responseObserver);
+    }
+
+{{/unaryMethods}}
+{{#serverStreamingMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public java.util.Iterator<{{outputType}}> {{methodName}}({{inputType}} request) {
+    return blockingStub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(request);
+    }
+
+    public void {{methodName}}({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> responseObserver) {
+    stub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(request, responseObserver);
+    }
+
+{{/serverStreamingMethods}}
+{{#biStreamingMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public io.grpc.stub.StreamObserver<{{inputType}}> {{methodName}}(io.grpc.stub.StreamObserver<{{outputType}}> responseObserver) {
+    return stub
+    .withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS)
+    .{{methodName}}(responseObserver);
+    }
+{{/biStreamingMethods}}
+}
+
+public static Dubbo{{serviceName}}Stub getDubboStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+return new Dubbo{{serviceName}}Stub(channel, callOptions, url, referenceConfig);
+}
+
+public interface I{{serviceName}} {
+{{#unaryMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    default public {{outputType}} {{methodName}}({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    default public com.google.common.util.concurrent.ListenableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public void {{methodName}}({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> responseObserver);
+
+{{/unaryMethods}}
+{{#serverStreamingMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    default public java.util.Iterator<{{outputType}}> {{methodName}}({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public void {{methodName}}({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> responseObserver);
+
+{{/serverStreamingMethods}}
+{{#biStreamingMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public io.grpc.stub.StreamObserver<{{inputType}}> {{methodName}}(io.grpc.stub.StreamObserver<{{outputType}}> responseObserver);
+
+{{/biStreamingMethods}}
+}
+
+{{#javaDoc}}
+    {{{javaDoc}}}
+{{/javaDoc}}
+public static abstract class {{serviceName}}ImplBase implements io.grpc.BindableService, I{{serviceName}} {
+
+private I{{serviceName}} proxiedImpl;
+
+public final void setProxiedImpl(I{{serviceName}} proxiedImpl) {
+this.proxiedImpl = proxiedImpl;
+}
+
+{{#unaryMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    @java.lang.Override
+    public final {{outputType}} {{methodName}}({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    @java.lang.Override
+    public final com.google.common.util.concurrent.ListenableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+{{/unaryMethods}}
+{{#serverStreamingMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    @java.lang.Override
+    public final java.util.Iterator<{{outputType}}> {{methodName}}({{inputType}} request) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+{{/serverStreamingMethods}}
+{{#methods}}
+    {{#isManyInput}}
+        public io.grpc.stub.StreamObserver<{{inputType}}> {{methodName}}(
+        io.grpc.stub.StreamObserver<{{outputType}}> responseObserver) {
+        return asyncUnimplementedStreamingCall({{packageName}}.{{serviceName}}Grpc.get{{methodNamePascalCase}}Method(), responseObserver);
+        }
+    {{/isManyInput}}{{^isManyInput}}
+        public void {{methodName}}({{inputType}} request,
+        io.grpc.stub.StreamObserver<{{outputType}}> responseObserver) {
+        asyncUnimplementedUnaryCall({{packageName}}.{{serviceName}}Grpc.get{{methodNamePascalCase}}Method(), responseObserver);
+        }
+    {{/isManyInput}}
+{{/methods}}
+
+@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+{{#methods}}
+    .addMethod(
+    {{packageName}}.{{serviceName}}Grpc.get{{methodNamePascalCase}}Method(),
+    {{grpcCallsMethodName}}(
+    new MethodHandlers<
+    {{inputType}},
+    {{outputType}}>(
+    proxiedImpl, METHODID_{{methodNameUpperUnderscore}})))
+{{/methods}}
+.build();
+}
+}
+{{#methods}}
+    private static final int METHODID_{{methodNameUpperUnderscore}} = {{methodNumber}};
+{{/methods}}
+
+private static final class MethodHandlers
+<Req, Resp> implements
+io.grpc.stub.ServerCalls.UnaryMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ServerStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ClientStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.BidiStreamingMethod
+<Req, Resp> {
+private final I{{serviceName}} serviceImpl;
+private final int methodId;
+
+MethodHandlers(I{{serviceName}} serviceImpl, int methodId) {
+this.serviceImpl = serviceImpl;
+this.methodId = methodId;
+}
+
+@java.lang.Override
+@java.lang.SuppressWarnings("unchecked")
+public void invoke(Req request, io.grpc.stub.StreamObserver
+<Resp> responseObserver) {
+    switch (methodId) {
+    {{#methods}}
+        {{^isManyInput}}
+            case METHODID_{{methodNameUpperUnderscore}}:
+            serviceImpl.{{methodName}}(({{inputType}}) request,
+            (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver);
+            break;
+        {{/isManyInput}}
+    {{/methods}}
+    default:
+    throw new java.lang.AssertionError();
+    }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver
+    <Req> invoke(io.grpc.stub.StreamObserver
+        <Resp> responseObserver) {
+            switch (methodId) {
+            {{#methods}}
+                {{#isManyInput}}
+                    case METHODID_{{methodNameUpperUnderscore}}:
+                    return (io.grpc.stub.StreamObserver
+                <Req>) serviceImpl.{{methodName}}(
+                    (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver);
+                {{/isManyInput}}
+            {{/methods}}
+            default:
+            throw new java.lang.AssertionError();
+            }
+            }
+            }
+
+            }
diff --git a/compiler/src/main/resources/DubboStub.mustache b/compiler/src/main/resources/DubboStub.mustache
new file mode 100644
index 0000000..06bd7b4
--- /dev/null
+++ b/compiler/src/main/resources/DubboStub.mustache
@@ -0,0 +1,53 @@
+{{#packageName}}
+    package {{packageName}};
+{{/packageName}}
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+{{#deprecated}}
+    @java.lang.Deprecated
+{{/deprecated}}
+@javax.annotation.Generated(
+value = "by Dubbo generator",
+comments = "Source: {{protoName}}")
+public final class {{className}} {
+private static final AtomicBoolean registered = new AtomicBoolean();
+
+private static Class<?> init() {
+Class<?> clazz = null;
+try {
+clazz = Class.forName({{serviceName}}Dubbo.class.getName());
+if (registered.compareAndSet(false, true)) {
+{{#methodTypes}}
+    org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
+    {{.}}.getDefaultInstance());
+{{/methodTypes}}
+}
+} catch (ClassNotFoundException e) {
+// ignore
+}
+return clazz;
+}
+
+private {{serviceName}}Dubbo() {}
+
+public static final String SERVICE_NAME = "{{packageName}}.{{serviceName}}";
+
+/**
+* Code generated for Dubbo
+*/
+public interface I{{serviceName}} {
+
+static Class<?> clazz = init();
+
+{{#methods}}
+    {{outputType}} {{methodName}}({{inputType}} request);
+
+    CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request);
+
+{{/methods}}
+
+}
+
+}
diff --git a/compiler/src/main/resources/ReactorDubboGrpcStub.mustache b/compiler/src/main/resources/ReactorDubboGrpcStub.mustache
new file mode 100644
index 0000000..2cf1471
--- /dev/null
+++ b/compiler/src/main/resources/ReactorDubboGrpcStub.mustache
@@ -0,0 +1,212 @@
+{{#packageName}}
+    package {{packageName}};
+{{/packageName}}
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.config.ReferenceConfigBase;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+
+import static {{packageName}}.{{serviceName}}Grpc.getServiceDescriptor;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
+
+
+{{#deprecated}}
+    @java.lang.Deprecated
+{{/deprecated}}
+@javax.annotation.Generated(
+value = "by ReactorDubboGrpc generator",
+comments = "Source: {{protoName}}")
+public final class {{className}} {
+private {{className}}() {}
+
+public static ReactorDubbo{{serviceName}}Stub getDubboStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+return new ReactorDubbo{{serviceName}}Stub(channel, callOptions, url, referenceConfig);
+}
+
+{{#javaDoc}}
+    {{{javaDoc}}}
+{{/javaDoc}}
+public static final class ReactorDubbo{{serviceName}}Stub implements IReactor{{serviceName}} {
+
+protected URL url;
+protected ReferenceConfigBase<?> referenceConfig;
+
+protected {{serviceName}}Grpc.{{serviceName}}Stub stub;
+
+public ReactorDubbo{{serviceName}}Stub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+this.url = url;
+this.referenceConfig = referenceConfig;
+stub = {{serviceName}}Grpc.newStub(channel).build(channel, callOptions);
+}
+
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}reactor.core.publisher.Flux{{/isManyInput}}{{^isManyInput}}reactor.core.publisher.Mono{{/isManyInput}}<{{inputType}}> reactorRequest) {
+    {{serviceName}}Grpc.{{serviceName}}Stub localStub = stub.withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS);
+    return com.salesforce.reactorgrpc.stub.ClientCalls.{{reactiveCallsMethodName}}(reactorRequest, localStub::{{methodName}});
+    }
+
+{{/methods}}
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} reactorRequest) {
+    {{serviceName}}Grpc.{{serviceName}}Stub localStub = stub.withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS);
+    return com.salesforce.reactorgrpc.stub.ClientCalls.{{reactiveCallsMethodName}}(reactor.core.publisher.Mono.just(reactorRequest), localStub::{{methodName}});
+    }
+
+{{/unaryRequestMethods}}
+}
+
+public interface IReactor{{serviceName}} {
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}reactor.core.publisher.Flux{{/isManyInput}}{{^isManyInput}}reactor.core.publisher.Mono{{/isManyInput}}<{{inputType}}> reactorRequest);
+
+{{/methods}}
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} reactorRequest);
+
+{{/unaryRequestMethods}}
+}
+
+{{#javaDoc}}
+    {{{javaDoc}}}
+{{/javaDoc}}
+public static abstract class {{serviceName}}ImplBase implements IReactor{{serviceName}}, io.grpc.BindableService {
+
+private IReactor{{serviceName}} proxiedImpl;
+
+public final void setProxiedImpl(IReactor{{serviceName}} proxiedImpl) {
+this.proxiedImpl = proxiedImpl;
+}
+
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public final {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} reactorRequest) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+{{/unaryRequestMethods}}
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}reactor.core.publisher.Flux{{/isManyOutput}}{{^isManyOutput}}reactor.core.publisher.Mono{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}reactor.core.publisher.Flux{{/isManyInput}}{{^isManyInput}}reactor.core.publisher.Mono{{/isManyInput}}<{{inputType}}> request) {
+    throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+    }
+
+{{/methods}}
+@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+{{#methods}}
+    .addMethod(
+    {{packageName}}.{{serviceName}}Grpc.get{{methodNamePascalCase}}Method(),
+    {{grpcCallsMethodName}}(
+    new MethodHandlers<
+    {{inputType}},
+    {{outputType}}>(
+    proxiedImpl, METHODID_{{methodNameUpperUnderscore}})))
+{{/methods}}
+.build();
+}
+}
+
+{{#methods}}
+    private static final int METHODID_{{methodNameUpperUnderscore}} = {{methodNumber}};
+{{/methods}}
+
+private static final class MethodHandlers
+<Req, Resp> implements
+io.grpc.stub.ServerCalls.UnaryMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ServerStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ClientStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.BidiStreamingMethod
+<Req, Resp> {
+private final IReactor{{serviceName}} serviceImpl;
+private final int methodId;
+
+MethodHandlers(IReactor{{serviceName}} serviceImpl, int methodId) {
+this.serviceImpl = serviceImpl;
+this.methodId = methodId;
+}
+
+@java.lang.Override
+@java.lang.SuppressWarnings("unchecked")
+public void invoke(Req request, io.grpc.stub.StreamObserver
+<Resp> responseObserver) {
+    switch (methodId) {
+    {{#methods}}
+        {{^isManyInput}}
+            case METHODID_{{methodNameUpperUnderscore}}:
+            com.salesforce.reactorgrpc.stub.ServerCalls.{{reactiveCallsMethodName}}(({{inputType}}) request,
+            (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver,
+            serviceImpl::{{methodName}});
+            break;
+        {{/isManyInput}}
+    {{/methods}}
+    default:
+    throw new java.lang.AssertionError();
+    }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver
+    <Req> invoke(io.grpc.stub.StreamObserver
+        <Resp> responseObserver) {
+            switch (methodId) {
+            {{#methods}}
+                {{#isManyInput}}
+                    case METHODID_{{methodNameUpperUnderscore}}:
+                    return (io.grpc.stub.StreamObserver
+                <Req>) com.salesforce.reactorgrpc.stub.ServerCalls.{{reactiveCallsMethodName}}(
+                    (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver,
+                    serviceImpl::{{methodName}});
+                {{/isManyInput}}
+            {{/methods}}
+            default:
+            throw new java.lang.AssertionError();
+            }
+            }
+            }
+
+            }
diff --git a/compiler/src/main/resources/RxDubboGrpcStub.mustache b/compiler/src/main/resources/RxDubboGrpcStub.mustache
new file mode 100644
index 0000000..22c06b0
--- /dev/null
+++ b/compiler/src/main/resources/RxDubboGrpcStub.mustache
@@ -0,0 +1,246 @@
+{{#packageName}}
+    package {{packageName}};
+{{/packageName}}
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.config.ReferenceConfigBase;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+
+import static {{packageName}}.{{serviceName}}Grpc.getServiceDescriptor;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
+import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
+
+
+{{#deprecated}}
+    @java.lang.Deprecated
+{{/deprecated}}
+@javax.annotation.Generated(
+value = "by RxDubboGrpc generator",
+comments = "Source: {{protoName}}")
+public final class {{className}} {
+private {{className}}() {}
+
+public static RxDubbo{{serviceName}}Stub getDubboStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+return new RxDubbo{{serviceName}}Stub(channel, callOptions, url, referenceConfig);
+}
+
+{{#javaDoc}}
+    {{{javaDoc}}}
+{{/javaDoc}}
+public static final class RxDubbo{{serviceName}}Stub implements IRx{{serviceName}} {
+
+protected URL url;
+protected ReferenceConfigBase<?> referenceConfig;
+
+protected {{serviceName}}Grpc.{{serviceName}}Stub stub;
+
+public RxDubbo{{serviceName}}Stub(io.grpc.Channel channel, io.grpc.CallOptions callOptions, URL url, ReferenceConfigBase<?> referenceConfig) {
+this.url = url;
+this.referenceConfig = referenceConfig;
+stub = {{serviceName}}Grpc.newStub(channel).build(channel, callOptions);
+}
+
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}io.reactivex.Flowable{{/isManyInput}}{{^isManyInput}}io.reactivex.Single{{/isManyInput}}<{{inputType}}> rxRequest) {
+    return com.salesforce.rxgrpc.stub.ClientCalls.{{reactiveCallsMethodName}}(rxRequest,
+    {{^isManyInput}}
+        new com.salesforce.reactivegrpc.common.BiConsumer<{{inputType}}, io.grpc.stub.StreamObserver<{{outputType}}>>() {
+        @java.lang.Override
+        public void accept({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> observer) {
+        stub.withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS).{{methodNameCamelCase}}(request, observer);
+        }
+        });
+    {{/isManyInput}}
+    {{#isManyInput}}
+        new com.salesforce.reactivegrpc.common.Function
+        <io.grpc.stub.StreamObserver<{{outputType}}>, io.grpc.stub.StreamObserver<{{inputType}}>>() {
+        @java.lang.Override
+        public io.grpc.stub.StreamObserver<{{inputType}}> apply(io.grpc.stub.StreamObserver<{{outputType}}> observer) {
+        return stub.withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS).{{methodNameCamelCase}}(observer);
+        }
+        });
+    {{/isManyInput}}
+    }
+
+{{/methods}}
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} rxRequest) {
+    return com.salesforce.rxgrpc.stub.ClientCalls.{{reactiveCallsMethodName}}(io.reactivex.Single.just(rxRequest),
+    new com.salesforce.reactivegrpc.common.BiConsumer<{{inputType}}, io.grpc.stub.StreamObserver<{{outputType}}>>() {
+    @java.lang.Override
+    public void accept({{inputType}} request, io.grpc.stub.StreamObserver<{{outputType}}> observer) {
+    stub.withDeadlineAfter(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS).{{methodNameCamelCase}}(request, observer);
+    }
+    });
+    }
+
+{{/unaryRequestMethods}}
+}
+
+public interface IRx{{serviceName}} {
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodName}}({{#isManyInput}}io.reactivex.Flowable{{/isManyInput}}{{^isManyInput}}io.reactivex.Single{{/isManyInput}}<{{inputType}}> rxRequest);
+
+{{/methods}}
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} rxRequest);
+
+{{/unaryRequestMethods}}
+}
+
+
+{{#javaDoc}}
+    {{{javaDoc}}}
+{{/javaDoc}}
+public static abstract class {{serviceName}}ImplBase implements IRx{{serviceName}}, io.grpc.BindableService {
+
+private IRx{{serviceName}} proxiedImpl;
+
+public final void setProxiedImpl(IRx{{serviceName}} proxiedImpl) {
+this.proxiedImpl = proxiedImpl;
+}
+{{#unaryRequestMethods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public final {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodName}}({{inputType}} rxRequest) {
+    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
+    }
+
+{{/unaryRequestMethods}}
+{{#methods}}
+    {{#javaDoc}}
+        {{{javaDoc}}}
+    {{/javaDoc}}
+    {{#deprecated}}
+        @java.lang.Deprecated
+    {{/deprecated}}
+    public {{#isManyOutput}}io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}io.reactivex.Single{{/isManyOutput}}<{{outputType}}> {{methodNameCamelCase}}({{#isManyInput}}io.reactivex.Flowable{{/isManyInput}}{{^isManyInput}}io.reactivex.Single{{/isManyInput}}<{{inputType}}> request) {
+    throw new io.grpc.StatusRuntimeException(io.grpc.Status.UNIMPLEMENTED);
+    }
+
+{{/methods}}
+@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+{{#methods}}
+    .addMethod(
+    {{packageName}}.{{serviceName}}Grpc.get{{methodNamePascalCase}}Method(),
+    {{grpcCallsMethodName}}(
+    new MethodHandlers<
+    {{inputType}},
+    {{outputType}}>(
+    proxiedImpl, METHODID_{{methodNameUpperUnderscore}})))
+{{/methods}}
+.build();
+}
+}
+
+{{#methods}}
+    private static final int METHODID_{{methodNameUpperUnderscore}} = {{methodNumber}};
+{{/methods}}
+
+private static final class MethodHandlers
+<Req, Resp> implements
+io.grpc.stub.ServerCalls.UnaryMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ServerStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.ClientStreamingMethod
+<Req, Resp>,
+io.grpc.stub.ServerCalls.BidiStreamingMethod
+<Req, Resp> {
+private final IRx{{serviceName}} serviceImpl;
+private final int methodId;
+
+MethodHandlers(IRx{{serviceName}} serviceImpl, int methodId) {
+this.serviceImpl = serviceImpl;
+this.methodId = methodId;
+}
+
+@java.lang.Override
+@java.lang.SuppressWarnings("unchecked")
+public void invoke(Req request, io.grpc.stub.StreamObserver
+<Resp> responseObserver) {
+    switch (methodId) {
+    {{#methods}}
+        {{^isManyInput}}
+            case METHODID_{{methodNameUpperUnderscore}}:
+            com.salesforce.rxgrpc.stub.ServerCalls.{{reactiveCallsMethodName}}(({{inputType}}) request,
+            (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver,
+            new com.salesforce.reactivegrpc.common.Function
+                <{{#isManyInput}}io.reactivex.Flowable{{/isManyInput}}{{^isManyInput}}io.reactivex.Single{{/isManyInput}}
+            <{{inputType}}>, {{#isManyOutput}}
+            io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}
+            io.reactivex.Single{{/isManyOutput}}<{{outputType}}>>() {
+            @java.lang.Override
+            public {{#isManyOutput}}
+            io.reactivex.Flowable{{/isManyOutput}}{{^isManyOutput}}
+            io.reactivex.Single{{/isManyOutput}}<{{outputType}}> apply({{#isManyInput}}
+            io.reactivex.Flowable{{/isManyInput}}{{^isManyInput}}
+            io.reactivex.Single{{/isManyInput}}<{{inputType}}> single) {
+            return serviceImpl.{{methodNameCamelCase}}(single);
+            }
+            });
+            break;
+        {{/isManyInput}}
+    {{/methods}}
+    default:
+    throw new java.lang.AssertionError();
+    }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver
+    <Req> invoke(io.grpc.stub.StreamObserver
+        <Resp> responseObserver) {
+            switch (methodId) {
+            {{#methods}}
+                {{#isManyInput}}
+                    case METHODID_{{methodNameUpperUnderscore}}:
+                    return (io.grpc.stub.StreamObserver
+                <Req>) com.salesforce.rxgrpc.stub.ServerCalls.{{reactiveCallsMethodName}}(
+                    (io.grpc.stub.StreamObserver<{{outputType}}>) responseObserver,
+                    serviceImpl::{{methodNameCamelCase}});
+                {{/isManyInput}}
+            {{/methods}}
+            default:
+            throw new java.lang.AssertionError();
+            }
+            }
+            }
+
+            }
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/pom.xml b/dubbo-spi-configcenter/dubbo-configcenter-apollo/pom.xml
new file mode 100644
index 0000000..8d3dc0e
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/pom.xml
@@ -0,0 +1,52 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-configcenter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>dubbo-configcenter-apollo</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The Apollo implementation of the configcenter api</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+        <apollo_mock_server_version>1.1.1</apollo_mock_server_version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ctrip.framework.apollo</groupId>
+            <artifactId>apollo-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ctrip.framework.apollo</groupId>
+            <artifactId>apollo-mockserver</artifactId>
+            <version>${apollo_mock_server_version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
new file mode 100644
index 0000000..f191178
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.configcenter.support.apollo;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigChangeListener;
+import com.ctrip.framework.apollo.ConfigFile;
+import com.ctrip.framework.apollo.ConfigService;
+import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
+import com.ctrip.framework.apollo.enums.ConfigSourceType;
+import com.ctrip.framework.apollo.enums.PropertyChangeType;
+import com.ctrip.framework.apollo.model.ConfigChange;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.stream.Collectors;
+
+import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CHECK_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+
+/**
+ * Apollo implementation, https://github.com/ctripcorp/apollo
+ *
+ * Apollo will be used for management of both governance rules and .properties files, by default, these two different
+ * kinds of data share the same namespace 'dubbo'. To gain better performance, we recommend separate them by giving
+ * namespace and group different values, for example:
+ *
+ * <dubbo:config-center namespace="governance" group="dubbo" />, 'dubbo=governance' is for governance rules while
+ * 'group=dubbo' is for properties files.
+ *
+ * Please see http://dubbo.apache.org/zh-cn/docs/user/configuration/config-center.html for details.
+ */
+public class ApolloDynamicConfiguration implements DynamicConfiguration {
+    private static final Logger logger = LoggerFactory.getLogger(ApolloDynamicConfiguration.class);
+    private static final String APOLLO_ENV_KEY = "env";
+    private static final String APOLLO_ADDR_KEY = "apollo.meta";
+    private static final String APOLLO_CLUSTER_KEY = "apollo.cluster";
+    private static final String APOLLO_PROTOCOL_PREFIX = "http://";
+    private static final String APOLLO_APPLICATION_KEY = "application";
+    private static final String APOLLO_APPID_KEY = "app.id";
+
+    private URL url;
+    private Config dubboConfig;
+    private ConfigFile dubboConfigFile;
+    private ConcurrentMap<String, ApolloListener> listeners = new ConcurrentHashMap<>();
+
+    ApolloDynamicConfiguration(URL url) {
+        this.url = url;
+        // Instead of using Dubbo's configuration, I would suggest use the original configuration method Apollo provides.
+        String configEnv = url.getParameter(APOLLO_ENV_KEY);
+        String configAddr = getAddressWithProtocolPrefix(url);
+        String configCluster = url.getParameter(CLUSTER_KEY);
+        String configAppId = url.getParameter(APOLLO_APPID_KEY);
+        if (StringUtils.isEmpty(System.getProperty(APOLLO_ENV_KEY)) && configEnv != null) {
+            System.setProperty(APOLLO_ENV_KEY, configEnv);
+        }
+        if (StringUtils.isEmpty(System.getProperty(APOLLO_ADDR_KEY)) && !ANYHOST_VALUE.equals(url.getHost())) {
+            System.setProperty(APOLLO_ADDR_KEY, configAddr);
+        }
+        if (StringUtils.isEmpty(System.getProperty(APOLLO_CLUSTER_KEY)) && configCluster != null) {
+            System.setProperty(APOLLO_CLUSTER_KEY, configCluster);
+        }
+        if (StringUtils.isEmpty(System.getProperty(APOLLO_APPID_KEY)) && configAppId != null) {
+            System.setProperty(APOLLO_APPID_KEY, configAppId);
+        }
+
+        String namespace = url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP);
+        String apolloNamespace = StringUtils.isEmpty(namespace) ? url.getParameter(GROUP_KEY, DEFAULT_GROUP) : namespace;
+        dubboConfig = ConfigService.getConfig(apolloNamespace);
+        dubboConfigFile = ConfigService.getConfigFile(apolloNamespace, ConfigFileFormat.Properties);
+
+        // Decide to fail or to continue when failed to connect to remote server.
+        boolean check = url.getParameter(CHECK_KEY, true);
+        if (dubboConfig.getSourceType() != ConfigSourceType.REMOTE) {
+            if (check) {
+                throw new IllegalStateException("Failed to connect to config center, the config center is Apollo, " +
+                        "the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv));
+            } else {
+                logger.warn("Failed to connect to config center, the config center is Apollo, " +
+                        "the address is: " + (StringUtils.isNotEmpty(configAddr) ? configAddr : configEnv) +
+                        ", will use the local cache value instead before eventually the connection is established.");
+            }
+        }
+    }
+
+    private String getAddressWithProtocolPrefix(URL url) {
+        String address = url.getBackupAddress();
+        if (StringUtils.isNotEmpty(address)) {
+            address = Arrays.stream(COMMA_SPLIT_PATTERN.split(address))
+                    .map(addr -> {
+                        if (addr.startsWith(APOLLO_PROTOCOL_PREFIX)) {
+                            return addr;
+                        }
+                        return APOLLO_PROTOCOL_PREFIX + addr;
+                    })
+                    .collect(Collectors.joining(","));
+        }
+        return address;
+    }
+
+    /**
+     * Since all governance rules will lay under dubbo group, this method now always uses the default dubboConfig and
+     * ignores the group parameter.
+     */
+    @Override
+    public void addListener(String key, String group, ConfigurationListener listener) {
+        ApolloListener apolloListener = listeners.computeIfAbsent(group + key, k -> createTargetListener(key, group));
+        apolloListener.addListener(listener);
+        dubboConfig.addChangeListener(apolloListener, Collections.singleton(key));
+    }
+
+    @Override
+    public void removeListener(String key, String group, ConfigurationListener listener) {
+        ApolloListener apolloListener = listeners.get(group + key);
+        if (apolloListener != null) {
+            apolloListener.removeListener(listener);
+            if (!apolloListener.hasInternalListener()) {
+                dubboConfig.removeChangeListener(apolloListener);
+            }
+        }
+    }
+
+    @Override
+    public String getConfig(String key, String group, long timeout) throws IllegalStateException {
+        if (StringUtils.isNotEmpty(group)) {
+            if (group.equals(url.getParameter(APPLICATION_KEY))) {
+                return ConfigService.getAppConfig().getProperty(key, null);
+            } else {
+                return ConfigService.getConfig(group).getProperty(key, null);
+            }
+        }
+        return dubboConfig.getProperty(key, null);
+    }
+
+    /**
+     * Recommend specify namespace and group when using Apollo.
+     * <p>
+     * <dubbo:config-center namespace="governance" group="dubbo" />, 'dubbo=governance' is for governance rules while
+     * 'group=dubbo' is for properties files.
+     *
+     * @param key     default value is 'dubbo.properties', currently useless for Apollo.
+     * @param group
+     * @param timeout
+     * @return
+     * @throws IllegalStateException
+     */
+    @Override
+    public String getProperties(String key, String group, long timeout) throws IllegalStateException {
+        if (StringUtils.isEmpty(group)) {
+            return dubboConfigFile.getContent();
+        }
+        if (group.equals(url.getParameter(APPLICATION_KEY))) {
+            return ConfigService.getConfigFile(APOLLO_APPLICATION_KEY, ConfigFileFormat.Properties).getContent();
+        }
+
+        ConfigFile configFile = ConfigService.getConfigFile(group, ConfigFileFormat.Properties);
+        if (configFile == null) {
+            throw new IllegalStateException("There is no namespace named " + group + " in Apollo.");
+        }
+        return configFile.getContent();
+    }
+
+    /**
+     * This method will be used by Configuration to get valid value at runtime.
+     * The group is expected to be 'app level', which can be fetched from the 'config.appnamespace' in url if necessary.
+     * But I think Apollo's inheritance feature of namespace can solve the problem .
+     */
+    @Override
+    public String getInternalProperty(String key) {
+        return dubboConfig.getProperty(key, null);
+    }
+
+    /**
+     * Ignores the group parameter.
+     *
+     * @param key   property key the native listener will listen on
+     * @param group to distinguish different set of properties
+     * @return
+     */
+    private ApolloListener createTargetListener(String key, String group) {
+        return new ApolloListener();
+    }
+
+    public class ApolloListener implements ConfigChangeListener {
+
+        private Set<ConfigurationListener> listeners = new CopyOnWriteArraySet<>();
+
+        ApolloListener() {
+        }
+
+        @Override
+        public void onChange(com.ctrip.framework.apollo.model.ConfigChangeEvent changeEvent) {
+            for (String key : changeEvent.changedKeys()) {
+                ConfigChange change = changeEvent.getChange(key);
+                if ("".equals(change.getNewValue())) {
+                    logger.warn("an empty rule is received for " + key + ", the current working rule is " +
+                            change.getOldValue() + ", the empty rule will not take effect.");
+                    return;
+                }
+
+                ConfigChangedEvent event = new ConfigChangedEvent(key, change.getNamespace(), change.getNewValue(), getChangeType(change));
+                listeners.forEach(listener -> listener.process(event));
+            }
+        }
+
+        private ConfigChangeType getChangeType(ConfigChange change) {
+            if (change.getChangeType() == PropertyChangeType.DELETED) {
+                return ConfigChangeType.DELETED;
+            }
+            return ConfigChangeType.MODIFIED;
+        }
+
+        void addListener(ConfigurationListener configurationListener) {
+            this.listeners.add(configurationListener);
+        }
+
+        void removeListener(ConfigurationListener configurationListener) {
+            this.listeners.remove(configurationListener);
+        }
+
+        boolean hasInternalListener() {
+            return listeners != null && listeners.size() > 0;
+        }
+    }
+
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java
new file mode 100644
index 0000000..6a8ce30
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.configcenter.support.apollo;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+
+/**
+ *
+ */
+public class ApolloDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
+    @Override
+    protected DynamicConfiguration createDynamicConfiguration(URL url) {
+        return new ApolloDynamicConfiguration(url);
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
new file mode 100644
index 0000000..0ea08c5
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
@@ -0,0 +1 @@
+apollo=org.apache.dubbo.configcenter.support.apollo.ApolloDynamicConfigurationFactory
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java
new file mode 100644
index 0000000..ca48bd6
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfigurationTest.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.configcenter.support.apollo;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+
+import com.ctrip.framework.apollo.mockserver.EmbeddedApollo;
+import com.google.common.util.concurrent.SettableFuture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Apollo dynamic configuration mock test.
+ * Notice: EmbeddedApollo(apollo mock server) only support < junit5, please not upgrade the junit version in this UT,
+ * the junit version in this UT is junit4, and the dependency comes from apollo-mockserver.
+ */
+public class ApolloDynamicConfigurationTest {
+    private static final String SESSION_TIMEOUT_KEY = "session";
+    private static final String DEFAULT_NAMESPACE = "dubbo";
+    private static ApolloDynamicConfiguration apolloDynamicConfiguration;
+    private static URL url;
+
+    /**
+     * The constant embeddedApollo.
+     */
+    @ClassRule
+    public static EmbeddedApollo embeddedApollo = new EmbeddedApollo();
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        String apolloUrl = System.getProperty("apollo.configService");
+        String urlForDubbo = "apollo://" + apolloUrl.substring(apolloUrl.lastIndexOf("/") + 1) + "/org.apache.dubbo.apollo.testService?namespace=dubbo&check=true";
+        url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000);
+    }
+
+//    /**
+//     * Embedded Apollo does not work as expected.
+//     */
+//    @Test
+//    public void testProperties() {
+//        URL url = this.url.addParameter(GROUP_KEY, "dubbo")
+//                .addParameter("namespace", "governance");
+//
+//        apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
+//        putData("dubbo", "dubbo.registry.address", "zookeeper://127.0.0.1:2181");
+//        assertEquals("zookeeper://127.0.0.1:2181", apolloDynamicConfiguration.getProperties(null, "dubbo"));
+//
+//        putData("governance", "router.tag", "router tag rule");
+//        assertEquals("router tag rule", apolloDynamicConfiguration.getConfig("router.tag", "governance"));
+//
+//    }
+
+    /**
+     * Test get rule.
+     */
+    @Test
+    public void testGetRule() {
+        String mockKey = "mockKey1";
+        String mockValue = String.valueOf(new Random().nextInt());
+        putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE);
+        apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
+        assertEquals(mockValue, apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L));
+
+        mockKey = "notExistKey";
+        assertNull(apolloDynamicConfiguration.getConfig(mockKey, DEFAULT_NAMESPACE, 3000L));
+    }
+
+    /**
+     * Test get internal property.
+     *
+     * @throws InterruptedException the interrupted exception
+     */
+    @Test
+    public void testGetInternalProperty() throws InterruptedException {
+        String mockKey = "mockKey2";
+        String mockValue = String.valueOf(new Random().nextInt());
+        putMockRuleData(mockKey, mockValue, DEFAULT_NAMESPACE);
+        TimeUnit.MILLISECONDS.sleep(1000);
+        apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
+        assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey));
+
+        mockValue = "mockValue2";
+        System.setProperty(mockKey, mockValue);
+        assertEquals(mockValue, apolloDynamicConfiguration.getInternalProperty(mockKey));
+
+        mockKey = "notExistKey";
+        assertNull(apolloDynamicConfiguration.getInternalProperty(mockKey));
+    }
+
+    /**
+     * Test add listener.
+     *
+     * @throws Exception the exception
+     */
+    @Test
+    public void testAddListener() throws Exception {
+        String mockKey = "mockKey3";
+        String mockValue = String.valueOf(new Random().nextInt());
+
+        final SettableFuture<org.apache.dubbo.common.config.configcenter.ConfigChangedEvent> future = SettableFuture.create();
+
+        apolloDynamicConfiguration = new ApolloDynamicConfiguration(url);
+
+        apolloDynamicConfiguration.addListener(mockKey, DEFAULT_NAMESPACE, new ConfigurationListener() {
+            @Override
+            public void process(org.apache.dubbo.common.config.configcenter.ConfigChangedEvent event) {
+                future.set(event);
+            }
+        });
+
+        putData(mockKey, mockValue);
+        org.apache.dubbo.common.config.configcenter.ConfigChangedEvent result = future.get(3000, TimeUnit.MILLISECONDS);
+        assertEquals(mockValue, result.getContent());
+        assertEquals(mockKey, result.getKey());
+        assertEquals(ConfigChangeType.MODIFIED, result.getChangeType());
+    }
+
+    private static void putData(String namespace, String key, String value) {
+        embeddedApollo.addOrModifyProperty(namespace, key, value);
+    }
+
+    private static void putData(String key, String value) {
+        embeddedApollo.addOrModifyProperty(DEFAULT_NAMESPACE, key, value);
+    }
+
+    private static void putMockRuleData(String key, String value, String group) {
+        String fileName = ApolloDynamicConfigurationTest.class.getResource("/").getPath() + "mockdata-" + group + ".properties";
+        putMockData(key, value, fileName);
+    }
+
+    private static void putMockData(String key, String value, String fileName) {
+        Properties pro = new Properties();
+        FileOutputStream oFile = null;
+        try {
+            oFile = new FileOutputStream(fileName);
+            pro.setProperty(key, value);
+            pro.store(oFile, "put mock data");
+        } catch (IOException exx) {
+            fail(exx.getMessage());
+
+        } finally {
+            if (null != oFile) {
+                try {
+                    oFile.close();
+                } catch (IOException e) {
+                    fail(e.getMessage());
+                }
+            }
+        }
+    }
+
+    /**
+     * Tear down.
+     */
+    @After
+    public void tearDown() {
+
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/META-INF/app.properties b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/META-INF/app.properties
new file mode 100644
index 0000000..4d963e2
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/META-INF/app.properties
@@ -0,0 +1 @@
+app.id=someAppId
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/mockdata-dubbo.properties b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/mockdata-dubbo.properties
new file mode 100644
index 0000000..f995a3b
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-apollo/src/test/resources/mockdata-dubbo.properties
@@ -0,0 +1,2 @@
+key1=value1
+key2=value2
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-consul/pom.xml b/dubbo-spi-configcenter/dubbo-configcenter-consul/pom.xml
new file mode 100644
index 0000000..bdabca7
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-consul/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-configcenter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>dubbo-configcenter-consul</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.orbitz.consul</groupId>
+            <artifactId>consul-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.pszymczyk.consul</groupId>
+            <artifactId>embedded-consul</artifactId>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java
new file mode 100644
index 0000000..282bdef
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.consul;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import com.google.common.base.Charsets;
+import com.google.common.net.HostAndPort;
+import com.orbitz.consul.Consul;
+import com.orbitz.consul.KeyValueClient;
+import com.orbitz.consul.cache.KVCache;
+import com.orbitz.consul.model.kv.Value;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
+import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING;
+
+/**
+ * config center implementation for consul
+ */
+public class ConsulDynamicConfiguration implements DynamicConfiguration {
+    private static final Logger logger = LoggerFactory.getLogger(ConsulDynamicConfiguration.class);
+
+    private static final int DEFAULT_PORT = 8500;
+    private static final int DEFAULT_WATCH_TIMEOUT = 60 * 1000;
+    private static final String WATCH_TIMEOUT = "consul-watch-timeout";
+
+    private URL url;
+    private String rootPath;
+    private Consul client;
+    private KeyValueClient kvClient;
+    private ConcurrentMap<String, ConsulListener> watchers = new ConcurrentHashMap<>();
+
+    public ConsulDynamicConfiguration(URL url) {
+        this.url = url;
+        this.rootPath = PATH_SEPARATOR + url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP) + PATH_SEPARATOR + "config";
+        String host = url.getHost();
+        int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
+        client = Consul.builder().withHostAndPort(HostAndPort.fromParts(host, port)).build();
+        this.kvClient = client.keyValueClient();
+    }
+
+    @Override
+    public void addListener(String key, String group, ConfigurationListener listener) {
+        logger.info("register listener " + listener.getClass() + " for config with key: " + key + ", group: " + group);
+        String normalizedKey = convertKey(group, key);
+        ConsulListener watcher = watchers.computeIfAbsent(normalizedKey, k -> new ConsulListener(key, group));
+        watcher.addListener(listener);
+    }
+
+    @Override
+    public void removeListener(String key, String group, ConfigurationListener listener) {
+        logger.info("unregister listener " + listener.getClass() + " for config with key: " + key + ", group: " + group);
+        ConsulListener watcher = watchers.get(convertKey(group, key));
+        if (watcher != null) {
+            watcher.removeListener(listener);
+        }
+    }
+
+    @Override
+    public String getConfig(String key, String group, long timeout) throws IllegalStateException {
+        return (String) getInternalProperty(convertKey(group, key));
+    }
+
+    @Override
+    public SortedSet<String> getConfigKeys(String group) throws UnsupportedOperationException {
+        SortedSet<String> configKeys = new TreeSet<>();
+        String normalizedKey = convertKey(group, EMPTY_STRING);
+        List<String> keys = kvClient.getKeys(normalizedKey);
+        if (CollectionUtils.isNotEmpty(keys)) {
+            keys.stream()
+                    .filter(k -> !k.equals(normalizedKey))
+                    .map(k -> k.substring(k.lastIndexOf(PATH_SEPARATOR) + 1))
+                    .forEach(configKeys::add);
+        }
+        return configKeys;
+//        SortedSet<String> configKeys = new TreeSet<>();
+//        String normalizedKey = convertKey(group, key);
+//        kvClient.getValueAsString(normalizedKey).ifPresent(v -> {
+//            Collections.addAll(configKeys, v.split(","));
+//        });
+//        return configKeys;
+    }
+
+    /**
+     * @param key     the key to represent a configuration
+     * @param group   the group where the key belongs to
+     * @param content the content of configuration
+     * @return
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public boolean publishConfig(String key, String group, String content) throws UnsupportedOperationException {
+//        String normalizedKey = convertKey(group, key);
+//        Value value = kvClient.getValue(normalizedKey).orElseThrow(() -> new IllegalArgumentException(normalizedKey + " does not exit."));
+//        Optional<String> old = value.getValueAsString();
+//        if (old.isPresent()) {
+//            content = old.get() + "," + content;
+//        }
+//
+//        while (!kvClient.putValue(key, content, value.getModifyIndex())) {
+//            value = kvClient.getValue(normalizedKey).orElseThrow(() -> new IllegalArgumentException(normalizedKey + " does not exit."));
+//            old = value.getValueAsString();
+//            if (old.isPresent()) {
+//                content = old.get() + "," + content;
+//            }
+//            try {
+//                Thread.sleep(10);
+//            } catch (InterruptedException e) {
+//                e.printStackTrace();
+//            }
+//        }
+//        return true;
+        String normalizedKey = convertKey(group, key);
+        return kvClient.putValue(normalizedKey + PATH_SEPARATOR + content);
+    }
+
+    @Override
+    public Object getInternalProperty(String key) {
+        logger.info("getting config from: " + key);
+        return kvClient.getValueAsString(key, Charsets.UTF_8).orElse(null);
+    }
+
+    @Override
+    public void close() throws Exception {
+        client.destroy();
+    }
+
+    private String buildPath(String group) {
+        String actualGroup = StringUtils.isEmpty(group) ? DEFAULT_GROUP : group;
+        return rootPath + PATH_SEPARATOR + actualGroup;
+    }
+
+    private String convertKey(String group, String key) {
+        return buildPath(group) + PATH_SEPARATOR + key;
+    }
+
+    private class ConsulListener implements KVCache.Listener<String, Value> {
+
+        private KVCache kvCache;
+        private Set<ConfigurationListener> listeners = new LinkedHashSet<>();
+        private String key;
+        private String group;
+        private String normalizedKey;
+
+        public ConsulListener(String key, String group) {
+            this.key = key;
+            this.group = group;
+            this.normalizedKey = convertKey(group, key);
+            initKVCache();
+        }
+
+        private void initKVCache() {
+            this.kvCache = KVCache.newCache(kvClient, normalizedKey);
+            kvCache.addListener(this);
+            kvCache.start();
+        }
+
+        @Override
+        public void notify(Map<String, Value> newValues) {
+            // Cache notifies all paths with "foo" the root path
+            // If you want to watch only "foo" value, you must filter other paths
+            Optional<Value> newValue = newValues.values().stream()
+                    .filter(value -> value.getKey().equals(normalizedKey))
+                    .findAny();
+
+            newValue.ifPresent(value -> {
+                // Values are encoded in key/value store, decode it if needed
+                Optional<String> decodedValue = newValue.get().getValueAsString();
+                decodedValue.ifPresent(v -> listeners.forEach(l -> {
+                    ConfigChangedEvent event = new ConfigChangedEvent(key, group, v, ConfigChangeType.MODIFIED);
+                    l.process(event);
+                }));
+            });
+        }
+
+        private void addListener(ConfigurationListener listener) {
+            this.listeners.add(listener);
+        }
+
+        private void removeListener(ConfigurationListener listener) {
+            this.listeners.remove(listener);
+        }
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java
new file mode 100644
index 0000000..980a156
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.consul;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+
+/**
+ * Config center factory for consul
+ */
+public class ConsulDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
+    @Override
+    protected DynamicConfiguration createDynamicConfiguration(URL url) {
+        return new ConsulDynamicConfiguration(url);
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
new file mode 100644
index 0000000..b7a5091
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
@@ -0,0 +1 @@
+consul=org.apache.dubbo.configcenter.consul.ConsulDynamicConfigurationFactory
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java
new file mode 100644
index 0000000..8ada5fb
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.configcenter.consul;
+
+import org.apache.dubbo.common.URL;
+
+import com.google.common.net.HostAndPort;
+import com.orbitz.consul.Consul;
+import com.orbitz.consul.KeyValueClient;
+import com.orbitz.consul.cache.KVCache;
+import com.orbitz.consul.model.kv.Value;
+import com.pszymczyk.consul.ConsulProcess;
+import com.pszymczyk.consul.ConsulStarterBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ *
+ */
+public class ConsulDynamicConfigurationTest {
+
+    private static ConsulProcess consul;
+    private static URL configCenterUrl;
+    private static ConsulDynamicConfiguration configuration;
+
+    private static Consul client;
+    private static KeyValueClient kvClient;
+
+    @BeforeAll
+    public static void setUp() throws Exception {
+        consul = ConsulStarterBuilder.consulStarter()
+                .build()
+                .start();
+        configCenterUrl = URL.valueOf("consul://localhost:" + consul.getHttpPort());
+
+        configuration = new ConsulDynamicConfiguration(configCenterUrl);
+        client = Consul.builder().withHostAndPort(HostAndPort.fromParts("localhost", consul.getHttpPort())).build();
+        kvClient = client.keyValueClient();
+    }
+
+    @AfterAll
+    public static void tearDown() throws Exception {
+        consul.close();
+        configuration.close();
+    }
+
+    @Test
+    public void testGetConfig() {
+        kvClient.putValue("/dubbo/config/dubbo/foo", "bar");
+        // test equals
+        assertEquals("bar", configuration.getConfig("foo", "dubbo"));
+        // test does not block
+        assertEquals("bar", configuration.getConfig("foo", "dubbo"));
+        Assertions.assertNull(configuration.getConfig("not-exist", "dubbo"));
+    }
+
+    @Test
+    public void testAddListener() {
+        KVCache cache = KVCache.newCache(kvClient, "/dubbo/config/dubbo/foo");
+        cache.addListener(newValues -> {
+            // Cache notifies all paths with "foo" the root path
+            // If you want to watch only "foo" value, you must filter other paths
+            Optional<Value> newValue = newValues.values().stream()
+                    .filter(value -> value.getKey().equals("foo"))
+                    .findAny();
+
+            newValue.ifPresent(value -> {
+                // Values are encoded in key/value store, decode it if needed
+                Optional<String> decodedValue = newValue.get().getValueAsString();
+                decodedValue.ifPresent(v -> System.out.println(String.format("Value is: %s", v))); //prints "bar"
+            });
+        });
+        cache.start();
+
+        kvClient.putValue("/dubbo/config/dubbo/foo", "new-value");
+        kvClient.putValue("/dubbo/config/dubbo/foo/sub", "sub-value");
+        kvClient.putValue("/dubbo/config/dubbo/foo/sub2", "sub-value2");
+        kvClient.putValue("/dubbo/config/foo", "parent-value");
+
+        System.out.println(kvClient.getKeys("/dubbo/config/dubbo/foo"));
+        System.out.println(kvClient.getKeys("/dubbo/config"));
+        System.out.println(kvClient.getValues("/dubbo/config/dubbo/foo"));
+    }
+
+    @Test
+    public void testGetConfigKeys() {
+
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-etcd/pom.xml b/dubbo-spi-configcenter/dubbo-configcenter-etcd/pom.xml
new file mode 100644
index 0000000..570ec92
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-etcd/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-configcenter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>dubbo-configcenter-etcd</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The etcd implementation of the config-center api</description>
+
+    <properties>
+        <skipIntegrationTests>true</skipIntegrationTests>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.etcd</groupId>
+            <artifactId>jetcd-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-remoting-etcd3</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipIntegrationTests}</skipTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
new file mode 100644
index 0000000..f686e86
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.support.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.etcd.StateListener;
+import org.apache.dubbo.remoting.etcd.jetcd.JEtcdClient;
+
+import com.google.protobuf.ByteString;
+import io.etcd.jetcd.api.Event;
+import io.etcd.jetcd.api.WatchCancelRequest;
+import io.etcd.jetcd.api.WatchCreateRequest;
+import io.etcd.jetcd.api.WatchGrpc;
+import io.etcd.jetcd.api.WatchRequest;
+import io.etcd.jetcd.api.WatchResponse;
+import io.grpc.ManagedChannel;
+import io.grpc.stub.StreamObserver;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.dubbo.common.config.configcenter.Constants.CONFIG_NAMESPACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
+
+/**
+ * The etcd implementation of {@link DynamicConfiguration}
+ */
+public class EtcdDynamicConfiguration implements DynamicConfiguration {
+
+    /**
+     * The final root path would be: /$NAME_SPACE/config
+     */
+    private String rootPath;
+
+    /**
+     * The etcd client
+     */
+    private final JEtcdClient etcdClient;
+
+    /**
+     * The map store the key to {@link EtcdConfigWatcher} mapping
+     */
+    private final ConcurrentMap<ConfigurationListener, EtcdConfigWatcher> watchListenerMap;
+
+    EtcdDynamicConfiguration(URL url) {
+        rootPath = PATH_SEPARATOR + url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP) + "/config";
+        etcdClient = new JEtcdClient(url);
+        etcdClient.addStateListener(state -> {
+            if (state == StateListener.CONNECTED) {
+                try {
+                    recover();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        });
+        watchListenerMap = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public void addListener(String key, String group, ConfigurationListener listener) {
+        if (watchListenerMap.get(listener) == null) {
+            EtcdConfigWatcher watcher = new EtcdConfigWatcher(key, group, listener);
+            watchListenerMap.put(listener, watcher);
+            watcher.watch();
+        }
+    }
+
+    @Override
+    public void removeListener(String key, String group, ConfigurationListener listener) {
+        EtcdConfigWatcher watcher = watchListenerMap.get(listener);
+        watcher.cancelWatch();
+    }
+
+    @Override
+    public String getConfig(String key, String group, long timeout) throws IllegalStateException {
+        return (String) getInternalProperty(convertKey(group, key));
+    }
+
+//    @Override
+//    public String getConfigs(String key, String group, long timeout) throws IllegalStateException {
+//        if (StringUtils.isEmpty(group)) {
+//            group = DEFAULT_GROUP;
+//        }
+//        return (String) getInternalProperty(convertKey(group, key));
+//    }
+
+    @Override
+    public Object getInternalProperty(String key) {
+        return etcdClient.getKVValue(key);
+    }
+
+    private String buildPath(String group) {
+        String actualGroup = StringUtils.isEmpty(group) ? DEFAULT_GROUP : group;
+        return rootPath + PATH_SEPARATOR + actualGroup;
+    }
+
+    private String convertKey(String group, String key) {
+        return buildPath(group) + PATH_SEPARATOR + key;
+    }
+
+    private void recover() {
+        for (EtcdConfigWatcher watcher : watchListenerMap.values()) {
+            watcher.watch();
+        }
+    }
+
+    public class EtcdConfigWatcher implements StreamObserver<WatchResponse> {
+
+        private ConfigurationListener listener;
+        protected WatchGrpc.WatchStub watchStub;
+        private StreamObserver<WatchRequest> observer;
+        protected long watchId;
+        private ManagedChannel channel;
+
+        private final String key;
+
+        private final String group;
+
+        private String normalizedKey;
+
+        public EtcdConfigWatcher(String key, String group, ConfigurationListener listener) {
+            this.key = key;
+            this.group = group;
+            this.normalizedKey = convertKey(group, key);
+            this.listener = listener;
+            this.channel = etcdClient.getChannel();
+        }
+
+        @Override
+        public void onNext(WatchResponse watchResponse) {
+            this.watchId = watchResponse.getWatchId();
+            for (Event etcdEvent : watchResponse.getEventsList()) {
+                ConfigChangeType type = ConfigChangeType.MODIFIED;
+                if (etcdEvent.getType() == Event.EventType.DELETE) {
+                    type = ConfigChangeType.DELETED;
+                }
+                ConfigChangedEvent event = new ConfigChangedEvent(key, group,
+                        etcdEvent.getKv().getValue().toString(UTF_8), type);
+                listener.process(event);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            // ignore
+        }
+
+        @Override
+        public void onCompleted() {
+            // ignore
+        }
+
+        public long getWatchId() {
+            return watchId;
+        }
+
+        private void watch() {
+            watchStub = WatchGrpc.newStub(channel);
+            observer = watchStub.watch(this);
+            WatchCreateRequest.Builder builder = WatchCreateRequest.newBuilder()
+                    .setKey(ByteString.copyFromUtf8(normalizedKey))
+                    .setProgressNotify(true);
+            WatchRequest req = WatchRequest.newBuilder().setCreateRequest(builder).build();
+            observer.onNext(req);
+        }
+
+        private void cancelWatch() {
+            WatchCancelRequest watchCancelRequest =
+                    WatchCancelRequest.newBuilder().setWatchId(watchId).build();
+            WatchRequest cancelRequest = WatchRequest.newBuilder()
+                    .setCancelRequest(watchCancelRequest).build();
+            observer.onNext(cancelRequest);
+        }
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java
new file mode 100644
index 0000000..269cee6
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.support.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+
+/**
+ * The etcd implementation of {@link AbstractDynamicConfigurationFactory}
+ */
+public class EtcdDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
+
+    @Override
+    protected DynamicConfiguration createDynamicConfiguration(URL url) {
+        return new EtcdDynamicConfiguration(url);
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
new file mode 100644
index 0000000..d84b1ae
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
@@ -0,0 +1 @@
+etcd=org.apache.dubbo.configcenter.support.etcd.EtcdDynamicConfigurationFactory
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
new file mode 100644
index 0000000..86dd306
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.support.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.launcher.EtcdCluster;
+import io.etcd.jetcd.launcher.EtcdClusterFactory;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.dubbo.remoting.etcd.Constants.SESSION_TIMEOUT_KEY;
+
+/**
+ * Unit test for etcd config center support
+ * Integrate with https://github.com/etcd-io/jetcd#launcher
+ */
+public class EtcdDynamicConfigurationTest {
+
+    private static EtcdDynamicConfiguration config;
+
+    public EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 3, false, false);
+
+    private static Client client;
+
+    @Test
+    public void testGetConfig() {
+
+        put("/dubbo/config/org.apache.dubbo.etcd.testService/configurators", "hello");
+        put("/dubbo/config/test/dubbo.properties", "aaa=bbb");
+        Assert.assertEquals("hello", config.getConfig("org.apache.dubbo.etcd.testService.configurators", DynamicConfiguration.DEFAULT_GROUP));
+        Assert.assertEquals("aaa=bbb", config.getConfig("dubbo.properties", "test"));
+    }
+
+    @Test
+    public void testAddListener() throws Exception {
+        CountDownLatch latch = new CountDownLatch(4);
+        TestListener listener1 = new TestListener(latch);
+        TestListener listener2 = new TestListener(latch);
+        TestListener listener3 = new TestListener(latch);
+        TestListener listener4 = new TestListener(latch);
+        config.addListener("AService.configurators", listener1);
+        config.addListener("AService.configurators", listener2);
+        config.addListener("testapp.tagrouters", listener3);
+        config.addListener("testapp.tagrouters", listener4);
+
+        put("/dubbo/config/AService/configurators", "new value1");
+        Thread.sleep(200);
+        put("/dubbo/config/testapp/tagrouters", "new value2");
+        Thread.sleep(200);
+        put("/dubbo/config/testapp", "new value3");
+
+        Thread.sleep(1000);
+
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertEquals(1, listener1.getCount("/dubbo/config/AService/configurators"));
+        Assert.assertEquals(1, listener2.getCount("/dubbo/config/AService/configurators"));
+        Assert.assertEquals(1, listener3.getCount("/dubbo/config/testapp/tagrouters"));
+        Assert.assertEquals(1, listener4.getCount("/dubbo/config/testapp/tagrouters"));
+
+        Assert.assertEquals("new value1", listener1.getValue());
+        Assert.assertEquals("new value1", listener2.getValue());
+        Assert.assertEquals("new value2", listener3.getValue());
+        Assert.assertEquals("new value2", listener4.getValue());
+    }
+
+    private class TestListener implements ConfigurationListener {
+        private CountDownLatch latch;
+        private String value;
+        private Map<String, Integer> countMap = new HashMap<>();
+
+        public TestListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void process(ConfigChangedEvent event) {
+            Integer count = countMap.computeIfAbsent(event.getKey(), k -> 0);
+            countMap.put(event.getKey(), ++count);
+            value = event.getContent();
+            latch.countDown();
+        }
+
+        public int getCount(String key) {
+            return countMap.get(key);
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    private void put(String key, String value) {
+        try {
+            client.getKVClient().put(ByteSequence.from(key, UTF_8), ByteSequence.from(value, UTF_8)).get();
+        } catch (Exception e) {
+            System.out.println("Error put value to etcd.");
+        }
+    }
+
+    @Before
+    public void setUp() {
+
+        etcdCluster.start();
+
+        client = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
+
+        List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
+
+        String ipAddress = clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort();
+        String urlForDubbo = "etcd3://" + ipAddress + "/org.apache.dubbo.etcd.testService";
+
+        // timeout in 15 seconds.
+        URL url = URL.valueOf(urlForDubbo)
+                .addParameter(SESSION_TIMEOUT_KEY, 15000);
+        config = new EtcdDynamicConfiguration(url);
+    }
+
+    @After
+    public void tearDown() {
+        etcdCluster.close();
+    }
+
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-nacos/pom.xml b/dubbo-spi-configcenter/dubbo-configcenter-nacos/pom.xml
new file mode 100644
index 0000000..42dcc69
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-nacos/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-configcenter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-configcenter-nacos</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The nacos implementation of the config-center api</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java
new file mode 100644
index 0000000..2227015
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java
@@ -0,0 +1,381 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.support.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
+import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.client.config.http.HttpAgent;
+import com.alibaba.nacos.client.config.impl.HttpSimpleClient;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Executor;
+import java.util.stream.Stream;
+
+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.CONFIG_LONG_POLL_TIMEOUT;
+import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_RETRY_TIME;
+import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT;
+import static com.alibaba.nacos.api.PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING;
+import static com.alibaba.nacos.api.PropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE;
+import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_LOAD_CACHE_AT_START;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_POLLING_THREAD_COUNT;
+import static com.alibaba.nacos.api.PropertyKeyConst.RAM_ROLE_NAME;
+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 java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
+import static org.apache.dubbo.common.utils.StringUtils.HYPHEN_CHAR;
+import static org.apache.dubbo.common.utils.StringUtils.SLASH_CHAR;
+
+/**
+ * The nacos implementation of {@link DynamicConfiguration}
+ */
+public class NacosDynamicConfiguration implements DynamicConfiguration {
+
+    private static final String GET_CONFIG_KEYS_PATH = "/v1/cs/configs";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    /**
+     * the default timeout in millis to get config from nacos
+     */
+    private static final long DEFAULT_TIMEOUT = 5000L;
+
+    private Properties nacosProperties;
+
+    /**
+     * The nacos configService
+     */
+    private final ConfigService configService;
+
+    private HttpAgent httpAgent;
+
+    /**
+     * The map store the key to {@link NacosConfigListener} mapping
+     */
+    private final ConcurrentMap<String, NacosConfigListener> watchListenerMap;
+
+    NacosDynamicConfiguration(URL url) {
+        this.nacosProperties = buildNacosProperties(url);
+        this.configService = buildConfigService(url);
+        this.httpAgent = getHttpAgent(configService);
+        watchListenerMap = new ConcurrentHashMap<>();
+    }
+
+    private ConfigService buildConfigService(URL url) {
+        ConfigService configService = null;
+        try {
+            configService = NacosFactory.createConfigService(nacosProperties);
+        } catch (NacosException e) {
+            if (logger.isErrorEnabled()) {
+                logger.error(e.getErrMsg(), e);
+            }
+            throw new IllegalStateException(e);
+        }
+        return configService;
+    }
+
+    private HttpAgent getHttpAgent(ConfigService configService) {
+        HttpAgent agent = null;
+        try {
+            Field field = configService.getClass().getDeclaredField("agent");
+            field.setAccessible(true);
+            agent = (HttpAgent) field.get(configService);
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+        return agent;
+    }
+
+    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 static void setProperties(URL url, Properties properties) {
+        putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
+        putPropertyIfAbsent(url, properties, IS_USE_CLOUD_NAMESPACE_PARSING);
+        putPropertyIfAbsent(url, properties, IS_USE_ENDPOINT_PARSING_RULE);
+        putPropertyIfAbsent(url, properties, ENDPOINT);
+        putPropertyIfAbsent(url, properties, ENDPOINT_PORT);
+        putPropertyIfAbsent(url, properties, NAMESPACE);
+        putPropertyIfAbsent(url, properties, ACCESS_KEY);
+        putPropertyIfAbsent(url, properties, SECRET_KEY);
+        putPropertyIfAbsent(url, properties, RAM_ROLE_NAME);
+        putPropertyIfAbsent(url, properties, CONTEXT_PATH);
+        putPropertyIfAbsent(url, properties, CLUSTER_NAME);
+        putPropertyIfAbsent(url, properties, ENCODE);
+        putPropertyIfAbsent(url, properties, CONFIG_LONG_POLL_TIMEOUT);
+        putPropertyIfAbsent(url, properties, CONFIG_RETRY_TIME);
+        putPropertyIfAbsent(url, properties, MAX_RETRY);
+        putPropertyIfAbsent(url, properties, ENABLE_REMOTE_SYNC_CONFIG);
+        putPropertyIfAbsent(url, properties, NAMING_LOAD_CACHE_AT_START, "true");
+        putPropertyIfAbsent(url, properties, NAMING_CLIENT_BEAT_THREAD_COUNT);
+        putPropertyIfAbsent(url, properties, NAMING_POLLING_THREAD_COUNT);
+    }
+
+    private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
+        String propertyValue = url.getParameter(propertyName);
+        if (StringUtils.isNotEmpty(propertyValue)) {
+            properties.setProperty(propertyName, propertyValue);
+        }
+    }
+
+    private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) {
+        String propertyValue = url.getParameter(propertyName);
+        if (StringUtils.isNotEmpty(propertyValue)) {
+            properties.setProperty(propertyName, propertyValue);
+        } else {
+            properties.setProperty(propertyName, defaultValue);
+        }
+    }
+
+    /**
+     * Ignores the group parameter.
+     *
+     * @param key   property key the native listener will listen on
+     * @param group to distinguish different set of properties
+     * @return
+     */
+    private NacosConfigListener createTargetListener(String key, String group) {
+        NacosConfigListener configListener = new NacosConfigListener();
+        configListener.fillContext(key, group);
+        return configListener;
+    }
+
+    @Override
+    public void addListener(String key, String group, ConfigurationListener listener) {
+        String resolvedGroup = resolveGroup(group);
+        String listenerKey = buildListenerKey(key, group);
+        NacosConfigListener nacosConfigListener = watchListenerMap.computeIfAbsent(listenerKey, k -> createTargetListener(key, resolvedGroup));
+        nacosConfigListener.addListener(listener);
+        try {
+            configService.addListener(key, resolvedGroup, nacosConfigListener);
+        } catch (NacosException e) {
+            logger.error(e.getMessage());
+        }
+    }
+
+    @Override
+    public void removeListener(String key, String group, ConfigurationListener listener) {
+        String listenerKey = buildListenerKey(key, group);
+        NacosConfigListener eventListener = watchListenerMap.get(listenerKey);
+        if (eventListener != null) {
+            eventListener.removeListener(listener);
+        }
+    }
+
+    @Override
+    public String getConfig(String key, String group, long timeout) throws IllegalStateException {
+        String resolvedGroup = resolveGroup(group);
+        try {
+            long nacosTimeout = timeout < 0 ? getDefaultTimeout() : timeout;
+            if (StringUtils.isEmpty(resolvedGroup)) {
+                resolvedGroup = DEFAULT_GROUP;
+            }
+            return configService.getConfig(key, resolvedGroup, nacosTimeout);
+        } catch (NacosException e) {
+            logger.error(e.getMessage());
+        }
+        return null;
+    }
+
+    @Override
+    public Object getInternalProperty(String key) {
+        try {
+            return configService.getConfig(key, DEFAULT_GROUP, getDefaultTimeout());
+        } catch (NacosException e) {
+            logger.error(e.getMessage());
+        }
+        return null;
+    }
+
+    @Override
+    public boolean publishConfig(String key, String group, String content) {
+        boolean published = false;
+        String resolvedGroup = resolveGroup(group);
+        try {
+            String value = configService.getConfig(key, resolvedGroup, getDefaultTimeout());
+            if (StringUtils.isNotEmpty(value)) {
+                content = value + "," + content;
+            }
+            published = configService.publishConfig(key, resolvedGroup, content);
+        } catch (NacosException e) {
+            logger.error(e.getErrMsg());
+        }
+        return published;
+    }
+
+    @Override
+    public long getDefaultTimeout() {
+        return DEFAULT_TIMEOUT;
+    }
+
+    /**
+     * TODO Nacos does not support atomic update of the value mapped to a key.
+     *
+     * @param key
+     * @param group the specified group
+     * @return
+     */
+    @Override
+    public SortedSet<String> getConfigKeys(String group) {
+        // TODO use Nacos Client API to replace HTTP Open API
+        SortedSet<String> keys = new TreeSet<>();
+        try {
+            List<String> paramsValues = asList(
+                    "search", "accurate",
+                    "dataId", "",
+                    "group", resolveGroup(group),
+                    "pageNo", "1",
+                    "pageSize", String.valueOf(Integer.MAX_VALUE)
+            );
+            String encoding = getProperty(ENCODE, "UTF-8");
+            HttpSimpleClient.HttpResult result = httpAgent.httpGet(GET_CONFIG_KEYS_PATH, emptyList(), paramsValues, encoding, 5 * 1000);
+            Stream<String> keysStream = toKeysStream(result.content);
+            keysStream.forEach(keys::add);
+        } catch (IOException e) {
+            if (logger.isErrorEnabled()) {
+                logger.error(e.getMessage(), e);
+            }
+        }
+        return keys;
+    }
+
+    private Stream<String> toKeysStream(String content) {
+        JSONObject jsonObject = JSON.parseObject(content);
+        JSONArray pageItems = jsonObject.getJSONArray("pageItems");
+        return pageItems.stream()
+                .map(object -> (JSONObject) object)
+                .map(json -> json.getString("dataId"));
+    }
+
+    private String getProperty(String name, String defaultValue) {
+        return nacosProperties.getProperty(name, defaultValue);
+    }
+
+    public class NacosConfigListener extends AbstractSharedListener {
+
+        private Set<ConfigurationListener> listeners = new CopyOnWriteArraySet<>();
+        /**
+         * cache data to store old value
+         */
+        private Map<String, String> cacheData = new ConcurrentHashMap<>();
+
+        @Override
+        public Executor getExecutor() {
+            return null;
+        }
+
+        /**
+         * receive
+         *
+         * @param dataId     data ID
+         * @param group      group
+         * @param configInfo content
+         */
+        @Override
+        public void innerReceive(String dataId, String group, String configInfo) {
+            String oldValue = cacheData.get(dataId);
+            ConfigChangedEvent event = new ConfigChangedEvent(dataId, group, configInfo, getChangeType(configInfo, oldValue));
+            if (configInfo == null) {
+                cacheData.remove(dataId);
+            } else {
+                cacheData.put(dataId, configInfo);
+            }
+            listeners.forEach(listener -> listener.process(event));
+        }
+
+        void addListener(ConfigurationListener configurationListener) {
+
+            this.listeners.add(configurationListener);
+        }
+
+        void removeListener(ConfigurationListener configurationListener) {
+            this.listeners.remove(configurationListener);
+        }
+
+        private ConfigChangeType getChangeType(String configInfo, String oldValue) {
+            if (StringUtils.isBlank(configInfo)) {
+                return ConfigChangeType.DELETED;
+            }
+            if (StringUtils.isBlank(oldValue)) {
+                return ConfigChangeType.ADDED;
+            }
+            return ConfigChangeType.MODIFIED;
+        }
+    }
+
+    protected String buildListenerKey(String key, String group) {
+        return key + HYPHEN_CHAR + resolveGroup(group);
+    }
+
+    protected String resolveGroup(String group) {
+        return group.replace(SLASH_CHAR, HYPHEN_CHAR);
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.java
new file mode 100644
index 0000000..61c02b4
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationFactory.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.configcenter.support.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.AbstractDynamicConfigurationFactory;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.CommonConstants;
+
+import com.alibaba.nacos.api.PropertyKeyConst;
+
+/**
+ * The nacos implementation of {@link AbstractDynamicConfigurationFactory}
+ */
+public class NacosDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {
+
+    @Override
+    protected DynamicConfiguration createDynamicConfiguration(URL url) {
+        URL nacosURL = url;
+        if (CommonConstants.DUBBO.equals(url.getParameter(PropertyKeyConst.NAMESPACE))) {
+            // Nacos use empty string as default name space, replace default namespace "dubbo" to ""
+            nacosURL = url.removeParameter(PropertyKeyConst.NAMESPACE);
+        }
+        return new NacosDynamicConfiguration(nacosURL);
+    }
+}
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
new file mode 100644
index 0000000..b9c75a4
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
@@ -0,0 +1 @@
+nacos=org.apache.dubbo.configcenter.support.nacos.NacosDynamicConfigurationFactory
\ No newline at end of file
diff --git a/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java
new file mode 100644
index 0000000..afafc1c
--- /dev/null
+++ b/dubbo-spi-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.configcenter.support.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.exception.NacosException;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.concurrent.CountDownLatch;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Unit test for nacos config center support
+ */
+//FIXME: waiting for embedded Nacos suport, then we can open the switch.
+@Disabled("https://github.com/alibaba/nacos/issues/1188")
+public class NacosDynamicConfigurationTest {
+    private static final String SESSION_TIMEOUT_KEY = "session";
+
+    private static NacosDynamicConfiguration config;
+
+    /**
+     * A test client to put data to Nacos server for testing purpose
+     */
+    private static ConfigService nacosClient;
+
+    @Test
+    public void testGetConfig() throws Exception {
+        put("org.apache.dubbo.nacos.testService.configurators", "hello");
+        Thread.sleep(200);
+        put("dubbo.properties", "test", "aaa=bbb");
+        Thread.sleep(200);
+        put("org.apache.dubbo.demo.DemoService:1.0.0.test:xxxx.configurators", "helloworld");
+        Thread.sleep(200);
+        Assertions.assertEquals("hello", config.getConfig("org.apache.dubbo.nacos.testService.configurators", DynamicConfiguration.DEFAULT_GROUP));
+        Assertions.assertEquals("aaa=bbb", config.getConfig("dubbo.properties", "test"));
+        Assertions.assertEquals("helloworld", config.getConfig("org.apache.dubbo.demo.DemoService:1.0.0.test:xxxx.configurators", DynamicConfiguration.DEFAULT_GROUP));
+    }
+
+    @Test
+    public void testAddListener() throws Exception {
+        CountDownLatch latch = new CountDownLatch(4);
+        TestListener listener1 = new TestListener(latch);
+        TestListener listener2 = new TestListener(latch);
+        TestListener listener3 = new TestListener(latch);
+        TestListener listener4 = new TestListener(latch);
+
+
+        config.addListener("AService.configurators", listener1);
+        config.addListener("AService.configurators", listener2);
+        config.addListener("testapp.tag-router", listener3);
+        config.addListener("testapp.tag-router", listener4);
+
+        put("AService.configurators", "new value1");
+        Thread.sleep(200);
+        put("testapp.tag-router", "new value2");
+        Thread.sleep(200);
+        put("testapp", "new value3");
+        Thread.sleep(5000);
+
+        latch.await();
+
+        Assertions.assertEquals(1, listener1.getCount("AService.configurators"));
+        Assertions.assertEquals(1, listener2.getCount("AService.configurators"));
+        Assertions.assertEquals(1, listener3.getCount("testapp.tag-router"));
+        Assertions.assertEquals(1, listener4.getCount("testapp.tag-router"));
+
+        Assertions.assertEquals("new value1", listener1.getValue());
+        Assertions.assertEquals("new value1", listener2.getValue());
+        Assertions.assertEquals("new value2", listener3.getValue());
+        Assertions.assertEquals("new value2", listener4.getValue());
+
+    }
+
+    @Test
+    public void testGetConfigKeys() {
+
+        put("key1", "a");
+        put("key2", "b");
+
+        SortedSet<String> keys = config.getConfigKeys(DynamicConfiguration.DEFAULT_GROUP);
+
+        Assertions.assertFalse(keys.isEmpty());
+
+    }
+
+    private void put(String key, String value) {
+        put(key, DynamicConfiguration.DEFAULT_GROUP, value);
+    }
+
+    private void put(String key, String group, String value) {
+        try {
+            nacosClient.publishConfig(key, group, value);
+        } catch (Exception e) {
+            System.out.println("Error put value to nacos.");
+        }
+    }
+
+    @BeforeAll
+    public static void setUp() {
+        String urlForDubbo = "nacos://" + "127.0.0.1:8848" + "/org.apache.dubbo.nacos.testService";
+        // timeout in 15 seconds.
+        URL url = URL.valueOf(urlForDubbo)
+                .addParameter(SESSION_TIMEOUT_KEY, 15000);
+        config = new NacosDynamicConfiguration(url);
+
+
+        try {
+            nacosClient = NacosFactory.createConfigService("127.0.0.1:8848");
+        } catch (NacosException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testPublishConfig() {
+        String key = "user-service";
+        String group = "org.apache.dubbo.service.UserService";
+        String content = "test";
+
+        assertTrue(config.publishConfig(key, group, content));
+        assertEquals("test", config.getProperties(key, group));
+    }
+
+    @AfterAll
+    public static void tearDown() {
+
+    }
+
+    private class TestListener implements ConfigurationListener {
+        private CountDownLatch latch;
+        private String value;
+        private Map<String, Integer> countMap = new HashMap<>();
+
+        public TestListener(CountDownLatch latch) {
+            this.latch = latch;
+        }
+
+        @Override
+        public void process(ConfigChangedEvent event) {
+            System.out.println(this + ": " + event);
+            Integer count = countMap.computeIfAbsent(event.getKey(), k -> 0);
+            countMap.put(event.getKey(), ++count);
+            value = event.getContent();
+            latch.countDown();
+        }
+
+        public int getCount(String key) {
+            return countMap.get(key);
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}
diff --git a/dubbo-spi-configcenter/pom.xml b/dubbo-spi-configcenter/pom.xml
new file mode 100644
index 0000000..0e3813a
--- /dev/null
+++ b/dubbo-spi-configcenter/pom.xml
@@ -0,0 +1,38 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo.spi</groupId>
+        <artifactId>dubbo-spi-extensions</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <artifactId>dubbo-spi-configcenter</artifactId>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <description>The service config-center module of the Dubbo project</description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+
+    <modules>
+        <module>dubbo-configcenter-apollo</module>
+        <module>dubbo-configcenter-consul</module>
+        <module>dubbo-configcenter-etcd</module>
+        <module>dubbo-configcenter-nacos</module>
+    </modules>
+</project>
diff --git a/dubbo-spi-container/dubbo-container-log4j/pom.xml b/dubbo-spi-container/dubbo-container-log4j/pom.xml
new file mode 100644
index 0000000..a92d9b4
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-log4j/pom.xml
@@ -0,0 +1,38 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-container</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <artifactId>dubbo-container-log4j</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The log4j container module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-container-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-spi-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java b/dubbo-spi-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java
new file mode 100644
index 0000000..4ba9c1c
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.container.log4j;
+
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.container.Container;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfigurator;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Log4jContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * The container class implementation for Log4j
+ */
+public class Log4jContainer implements Container {
+
+    public static final String LOG4J_FILE = "dubbo.log4j.file";
+
+    public static final String LOG4J_LEVEL = "dubbo.log4j.level";
+
+    public static final String LOG4J_SUBDIRECTORY = "dubbo.log4j.subdirectory";
+
+    public static final String DEFAULT_LOG4J_LEVEL = "ERROR";
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void start() {
+        String file = ConfigurationUtils.getProperty(LOG4J_FILE);
+        if (file != null && file.length() > 0) {
+            String level = ConfigurationUtils.getProperty(LOG4J_LEVEL);
+            if (StringUtils.isEmpty(level)) {
+                level = DEFAULT_LOG4J_LEVEL;
+            }
+            Properties properties = new Properties();
+            properties.setProperty("log4j.rootLogger", level + ",application");
+            properties.setProperty("log4j.appender.application", "org.apache.log4j.DailyRollingFileAppender");
+            properties.setProperty("log4j.appender.application.File", file);
+            properties.setProperty("log4j.appender.application.Append", "true");
+            properties.setProperty("log4j.appender.application.DatePattern", "'.'yyyy-MM-dd");
+            properties.setProperty("log4j.appender.application.layout", "org.apache.log4j.PatternLayout");
+            properties.setProperty("log4j.appender.application.layout.ConversionPattern", "%d [%t] %-5p %C{6} (%F:%L) - %m%n");
+            PropertyConfigurator.configure(properties);
+        }
+        String subdirectory = ConfigurationUtils.getProperty(LOG4J_SUBDIRECTORY);
+        if (subdirectory != null && subdirectory.length() > 0) {
+            Enumeration<org.apache.log4j.Logger> ls = LogManager.getCurrentLoggers();
+            while (ls.hasMoreElements()) {
+                org.apache.log4j.Logger l = ls.nextElement();
+                if (l != null) {
+                    Enumeration<Appender> as = l.getAllAppenders();
+                    while (as.hasMoreElements()) {
+                        Appender a = as.nextElement();
+                        if (a instanceof FileAppender) {
+                            FileAppender fa = (FileAppender) a;
+                            String f = fa.getFile();
+                            if (f != null && f.length() > 0) {
+                                int i = f.replace('\\', '/').lastIndexOf('/');
+                                String path;
+                                if (i == -1) {
+                                    path = subdirectory;
+                                } else {
+                                    path = f.substring(0, i);
+                                    if (!path.endsWith(subdirectory)) {
+                                        path = path + "/" + subdirectory;
+                                    }
+                                    f = f.substring(i + 1);
+                                }
+                                fa.setFile(path + "/" + f);
+                                fa.activateOptions();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void stop() {
+    }
+
+}
diff --git a/dubbo-spi-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container b/dubbo-spi-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
new file mode 100644
index 0000000..0b4c162
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
@@ -0,0 +1 @@
+log4j=org.apache.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-spi-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java b/dubbo-spi-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java
new file mode 100644
index 0000000..b75b305
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.container.log4j;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.container.Container;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * StandaloneContainerTest
+ */
+public class Log4jContainerTest {
+
+    @Test
+    public void testContainer() {
+        Log4jContainer container = (Log4jContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("log4j");
+        container.start();
+        container.stop();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-spi-container/dubbo-container-logback/pom.xml b/dubbo-spi-container/dubbo-container-logback/pom.xml
new file mode 100644
index 0000000..dec8f54
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-logback/pom.xml
@@ -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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-container</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <artifactId>dubbo-container-logback</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The logback container module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-container-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-spi-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.java b/dubbo-spi-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.java
new file mode 100644
index 0000000..430e2e2
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.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.container.logback;
+
+import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.container.Container;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.rolling.RollingFileAppender;
+import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
+import org.slf4j.LoggerFactory;
+
+/**
+ * LogbackContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * The container class implementation for Logback
+ */
+public class LogbackContainer implements Container {
+
+    public static final String LOGBACK_FILE = "dubbo.logback.file";
+
+    public static final String LOGBACK_LEVEL = "dubbo.logback.level";
+
+    public static final String LOGBACK_MAX_HISTORY = "dubbo.logback.maxhistory";
+
+    public static final String DEFAULT_LOGBACK_LEVEL = "ERROR";
+
+    @Override
+    public void start() {
+        String file = ConfigUtils.getProperty(LOGBACK_FILE);
+        if (file != null && file.length() > 0) {
+            String level = ConfigUtils.getProperty(LOGBACK_LEVEL);
+            if (StringUtils.isEmpty(level)) {
+                level = DEFAULT_LOGBACK_LEVEL;
+            }
+            // maxHistory=0 Infinite history
+            int maxHistory = StringUtils.parseInteger(ConfigUtils.getProperty(LOGBACK_MAX_HISTORY));
+
+            doInitializer(file, level, maxHistory);
+        }
+    }
+
+    @Override
+    public void stop() {
+    }
+
+    /**
+     * Initializer logback
+     *
+     * @param file
+     * @param level
+     * @param maxHistory
+     */
+    private void doInitializer(String file, String level, int maxHistory) {
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
+        rootLogger.detachAndStopAllAppenders();
+
+        // appender
+        RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();
+        fileAppender.setContext(loggerContext);
+        fileAppender.setName("application");
+        fileAppender.setFile(file);
+        fileAppender.setAppend(true);
+
+        // policy
+        TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>();
+        policy.setContext(loggerContext);
+        policy.setMaxHistory(maxHistory);
+        policy.setFileNamePattern(file + ".%d{yyyy-MM-dd}");
+        policy.setParent(fileAppender);
+        policy.start();
+        fileAppender.setRollingPolicy(policy);
+
+        // encoder
+        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
+        encoder.setContext(loggerContext);
+        encoder.setPattern("%date [%thread] %-5level %logger (%file:%line\\) - %msg%n");
+        encoder.start();
+        fileAppender.setEncoder(encoder);
+
+        fileAppender.start();
+
+        rootLogger.addAppender(fileAppender);
+        rootLogger.setLevel(Level.toLevel(level));
+        rootLogger.setAdditive(false);
+    }
+
+}
diff --git a/dubbo-spi-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container b/dubbo-spi-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
new file mode 100644
index 0000000..04c4eaa
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
@@ -0,0 +1 @@
+logback=org.apache.dubbo.container.logback.LogbackContainer
\ No newline at end of file
diff --git a/dubbo-spi-container/dubbo-container-logback/src/test/java/org/apache/dubbo/container/logback/LogbackContainerTest.java b/dubbo-spi-container/dubbo-container-logback/src/test/java/org/apache/dubbo/container/logback/LogbackContainerTest.java
new file mode 100644
index 0000000..d82e8a4
--- /dev/null
+++ b/dubbo-spi-container/dubbo-container-logback/src/test/java/org/apache/dubbo/container/logback/LogbackContainerTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.container.logback;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.container.Container;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * StandaloneContainerTest
+ */
+public class LogbackContainerTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(LogbackContainerTest.class);
+
+    @Test
+    public void testContainer() {
+        LogbackContainer container = (LogbackContainer) ExtensionLoader.getExtensionLoader(Container.class)
+                .getExtension("logback");
+        container.start();
+
+        logger.debug("Test debug:" + this.getClass().getName());
+        logger.warn("Test warn:" + this.getClass().getName());
+        logger.info("Test info:" + this.getClass().getName());
+        logger.error("Test error:" + this.getClass().getName());
+
+        container.stop();
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-spi-container/pom.xml b/dubbo-spi-container/pom.xml
new file mode 100644
index 0000000..e5f81c0
--- /dev/null
+++ b/dubbo-spi-container/pom.xml
@@ -0,0 +1,35 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo.spi</groupId>
+        <artifactId>dubbo-spi-extensions</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <artifactId>dubbo-spi-container</artifactId>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <description>The container module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+    <modules>
+        <module>dubbo-container-log4j</module>
+        <module>dubbo-container-logback</module>
+    </modules>
+</project>
diff --git a/dubbo-spi-filter/dubbo-filter-cache/pom.xml b/dubbo-spi-filter/dubbo-filter-cache/pom.xml
new file mode 100644
index 0000000..49f99cf
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/pom.xml
@@ -0,0 +1,48 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-filter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <artifactId>dubbo-filter-cache</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The cache module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.cache</groupId>
+            <artifactId>cache-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.hazelcast</groupId>
+            <artifactId>hazelcast</artifactId>
+            <scope>test</scope>
+            <version>${hazelcast_version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/Cache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/Cache.java
new file mode 100644
index 0000000..69ae671
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/Cache.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache;
+
+/**
+ * Cache interface to support storing and retrieval of value against a lookup key. It has two operation <b>get</b> and <b>put</b>.
+ * <li><b>put</b>-Storing value against a key.</li>
+ * <li><b>get</b>-Retrieval of object.</li>
+ * @see org.apache.dubbo.cache.support.lru.LruCache
+ * @see org.apache.dubbo.cache.support.jcache.JCache
+ * @see org.apache.dubbo.cache.support.expiring.ExpiringCache
+ * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
+ */
+public interface Cache {
+    /**
+     * API to store value against a key
+     * @param key  Unique identifier for the object being store.
+     * @param value Value getting store
+     */
+    void put(Object key, Object value);
+
+    /**
+     * API to return stored value using a key.
+     * @param key Unique identifier for cache lookup
+     * @return Return stored object against key
+     */
+    Object get(Object key);
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/CacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/CacheFactory.java
new file mode 100644
index 0000000..66b5b59
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/CacheFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.Invocation;
+
+/**
+ * Interface needs to be implemented by all the cache store provider.Along with implementing <b>CacheFactory</b> interface
+ * entry needs to be added in org.apache.dubbo.cache.CacheFactory file in a classpath META-INF sub directories.
+ *
+ * @see Cache
+ */
+@SPI("lru")
+public interface CacheFactory {
+
+    /**
+     * CacheFactory implementation class needs to implement this return underlying cache instance for method against
+     * url and invocation.
+     * @param url
+     * @param invocation
+     * @return Instance of Cache containing cached value against method url and invocation.
+     */
+    @Adaptive("cache")
+    Cache getCache(URL url, Invocation invocation);
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java
new file mode 100644
index 0000000..02d0e29
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/filter/CacheFilter.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.filter;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.CacheFactory;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+
+import java.io.Serializable;
+
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
+import static org.apache.dubbo.common.constants.FilterConstants.CACHE_KEY;
+
+/**
+ * CacheFilter is a core component of dubbo.Enabling <b>cache</b> key of service,method,consumer or provider dubbo will cache method return value.
+ * Along with cache key we need to configure cache type. Dubbo default implemented cache types are
+ * <li>lur</li>
+ * <li>threadlocal</li>
+ * <li>jcache</li>
+ * <li>expiring</li>
+ *
+ * <pre>
+ *   e.g. 1)&lt;dubbo:service cache="lru" /&gt;
+ *        2)&lt;dubbo:service /&gt; &lt;dubbo:method name="method2" cache="threadlocal" /&gt; &lt;dubbo:service/&gt;
+ *        3)&lt;dubbo:provider cache="expiring" /&gt;
+ *        4)&lt;dubbo:consumer cache="jcache" /&gt;
+ *
+ *If cache type is defined in method level then method level type will get precedence. According to above provided
+ *example, if service has two method, method1 and method2, method2 will have cache type as <b>threadlocal</b> where others will
+ *be backed by <b>lru</b>
+ *</pre>
+ *
+ * @see org.apache.dubbo.rpc.Filter
+ * @see org.apache.dubbo.cache.support.lru.LruCacheFactory
+ * @see org.apache.dubbo.cache.support.lru.LruCache
+ * @see org.apache.dubbo.cache.support.jcache.JCacheFactory
+ * @see org.apache.dubbo.cache.support.jcache.JCache
+ * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
+ * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCache
+ * @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
+ * @see org.apache.dubbo.cache.support.expiring.ExpiringCache
+ *
+ */
+@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
+public class CacheFilter implements Filter {
+
+    private CacheFactory cacheFactory;
+
+    /**
+     * Dubbo will populate and set the cache factory instance based on service/method/consumer/provider configured
+     * cache attribute value. Dubbo will search for the class name implementing configured <b>cache</b> in file org.apache.dubbo.cache.CacheFactory
+     * under META-INF sub folders.
+     *
+     * @param cacheFactory instance of CacheFactory based on <b>cache</b> type
+     */
+    public void setCacheFactory(CacheFactory cacheFactory) {
+        this.cacheFactory = cacheFactory;
+    }
+
+    /**
+     * If cache is configured, dubbo will invoke method on each method call. If cache value is returned by cache store
+     * then it will return otherwise call the remote method and return value. If remote method's return value has error
+     * then it will not cache the value.
+     * @param invoker    service
+     * @param invocation invocation.
+     * @return Cache returned value if found by the underlying cache store. If cache miss it will call target method.
+     * @throws RpcException
+     */
+    @Override
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), CACHE_KEY))) {
+            Cache cache = cacheFactory.getCache(invoker.getUrl(), invocation);
+            if (cache != null) {
+                String key = StringUtils.toArgumentString(invocation.getArguments());
+                Object value = cache.get(key);
+                if (value != null) {
+                    if (value instanceof ValueWrapper) {
+                        return AsyncRpcResult.newDefaultAsyncResult(((ValueWrapper) value).get(), invocation);
+                    } else {
+                        return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
+                    }
+                }
+                Result result = invoker.invoke(invocation);
+                if (!result.hasException()) {
+                    cache.put(key, new ValueWrapper(result.getValue()));
+                }
+                return result;
+            }
+        }
+        return invoker.invoke(invocation);
+    }
+
+    /**
+     * Cache value wrapper.
+     */
+    static class ValueWrapper implements Serializable {
+
+        private static final long serialVersionUID = -1777337318019193256L;
+
+        private final Object value;
+
+        public ValueWrapper (Object value) {
+            this.value = value;
+        }
+
+        public Object get() {
+            return this.value;
+        }
+    }
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java
new file mode 100644
index 0000000..966d923
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/AbstractCacheFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.CacheFactory;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
+
+/**
+ * AbstractCacheFactory is a default implementation of {@link CacheFactory}. It abstract out the key formation from URL along with
+ * invocation method. It initially check if the value for key already present in own local in-memory store then it won't check underlying storage cache {@link Cache}.
+ * Internally it used {@link ConcurrentHashMap} to store do level-1 caching.
+ *
+ * @see CacheFactory
+ * @see org.apache.dubbo.cache.support.jcache.JCacheFactory
+ * @see org.apache.dubbo.cache.support.lru.LruCacheFactory
+ * @see org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
+ * @see org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
+ */
+public abstract class AbstractCacheFactory implements CacheFactory {
+
+    /**
+     * This is used to store factory level-1 cached data.
+     */
+    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
+
+    /**
+     *  Takes URL and invocation instance and return cache instance for a given url.
+     * @param url url of the method
+     * @param invocation invocation context.
+     * @return Instance of cache store used as storage for caching return values.
+     */
+    @Override
+    public Cache getCache(URL url, Invocation invocation) {
+        url = url.addParameter(METHOD_KEY, invocation.getMethodName());
+        String key = url.toFullString();
+        Cache cache = caches.get(key);
+        if (cache == null) {
+            caches.put(key, createCache(url));
+            cache = caches.get(key);
+        }
+        return cache;
+    }
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by AbstractCacheFactory subclass.
+     * @param url url of the method
+     * @return Create and return new instance of cache store used as storage for caching return values.
+     */
+    protected abstract Cache createCache(URL url);
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCache.java
new file mode 100644
index 0000000..afaf8c6
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCache.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.expiring;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.common.URL;
+
+import java.util.Map;
+
+/**
+ * ExpiringCache - With the characteristic of expiration time.
+ */
+
+/**
+ * This class store the cache value with the characteristic of expiration time. If a service,method,consumer or provided is configured with key <b>cache</b>
+ * with value <b>expiring</b>, dubbo initialize the instance of this class using {@link ExpiringCacheFactory} to store method's returns value
+ * to server from store without making method call.
+ * <pre>
+ *     e.g. 1) &lt;dubbo:service cache="expiring" cache.seconds="60" cache.interval="10"/&gt;
+ *          2) &lt;dubbo:consumer cache="expiring" /&gt;
+ * </pre>
+ * <li>It used constructor argument url instance <b>cache.seconds</b> value to decide time to live of cached object.Default value of it is 180 second.</li>
+ * <li>It used constructor argument url instance <b>cache.interval</b> value for cache value expiration interval.Default value of this is 4 second</li>
+ * @see Cache
+ * @see ExpiringCacheFactory
+ * @see org.apache.dubbo.cache.support.AbstractCacheFactory
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ */
+public class ExpiringCache implements Cache {
+    private final Map<Object, Object> store;
+
+    public ExpiringCache(URL url) {
+        // cache time (second)
+        final int secondsToLive = url.getParameter("cache.seconds", 180);
+        // Cache check interval (second)
+        final int intervalSeconds = url.getParameter("cache.interval", 4);
+        ExpiringMap<Object, Object> expiringMap = new ExpiringMap<>(secondsToLive, intervalSeconds);
+        expiringMap.getExpireThread().startExpiryIfNotStarted();
+        this.store = expiringMap;
+    }
+
+    /**
+     * API to store value against a key in the calling thread scope.
+     * @param key  Unique identifier for the object being store.
+     * @param value Value getting store
+     */
+    @Override
+    public void put(Object key, Object value) {
+        store.put(key, value);
+    }
+
+    /**
+     * API to return stored value using a key against the calling thread specific store.
+     * @param key Unique identifier for cache lookup
+     * @return Return stored object against key
+     */
+
+    @Override
+    public Object get(Object key) {
+        return store.get(key);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactory.java
new file mode 100644
index 0000000..5259d74
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.expiring;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.common.URL;
+
+
+/**
+ * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
+ * instance of new {@link ExpiringCache}.
+ *
+ * @see AbstractCacheFactory
+ * @see ExpiringCache
+ * @see Cache
+ */
+
+public class ExpiringCacheFactory extends AbstractCacheFactory {
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by JCache.
+     * @param url url of the method
+     * @return ExpiringCache instance of cache
+     */
+    @Override
+    protected Cache createCache(URL url) {
+        return new ExpiringCache(url);
+    }
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java
new file mode 100644
index 0000000..895f114
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/expiring/ExpiringMap.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.expiring;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * can be expired map
+ * Contains a background thread that periodically checks if the data is out of date
+ */
+public class ExpiringMap<K, V> implements Map<K, V> {
+
+    /**
+     * default time to live (second)
+     */
+    private static final int DEFAULT_TIME_TO_LIVE = 180;
+
+    /**
+     * default expire check interval (second)
+     */
+    private static final int DEFAULT_EXPIRATION_INTERVAL = 1;
+
+    private static AtomicInteger expireCount = new AtomicInteger(1);
+
+    private final ConcurrentHashMap<K, ExpiryObject> delegateMap;
+
+    private final ExpireThread expireThread;
+
+    public ExpiringMap() {
+        this(DEFAULT_TIME_TO_LIVE, DEFAULT_EXPIRATION_INTERVAL);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param timeToLive time to live (second)
+     */
+    public ExpiringMap(int timeToLive) {
+        this(timeToLive, DEFAULT_EXPIRATION_INTERVAL);
+    }
+
+    public ExpiringMap(int timeToLive, int expirationInterval) {
+        this(new ConcurrentHashMap<>(), timeToLive, expirationInterval);
+    }
+
+    private ExpiringMap(ConcurrentHashMap<K, ExpiryObject> delegateMap, int timeToLive, int expirationInterval) {
+        this.delegateMap = delegateMap;
+        this.expireThread = new ExpireThread();
+        expireThread.setTimeToLive(timeToLive);
+        expireThread.setExpirationInterval(expirationInterval);
+    }
+
+    @Override
+    public V put(K key, V value) {
+        ExpiryObject answer = delegateMap.put(key, new ExpiryObject(key, value, System.currentTimeMillis()));
+        if (answer == null) {
+            return null;
+        }
+        return answer.getValue();
+    }
+
+    @Override
+    public V get(Object key) {
+        ExpiryObject object = delegateMap.get(key);
+        if (object != null) {
+            object.setLastAccessTime(System.currentTimeMillis());
+            return object.getValue();
+        }
+        return null;
+    }
+
+    @Override
+    public V remove(Object key) {
+        ExpiryObject answer = delegateMap.remove(key);
+        if (answer == null) {
+            return null;
+        }
+        return answer.getValue();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return delegateMap.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return delegateMap.containsValue(value);
+    }
+
+    @Override
+    public int size() {
+        return delegateMap.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegateMap.isEmpty();
+    }
+
+    @Override
+    public void clear() {
+        delegateMap.clear();
+        expireThread.stopExpiring();
+    }
+
+    @Override
+    public int hashCode() {
+        return delegateMap.hashCode();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return delegateMap.keySet();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return delegateMap.equals(obj);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> inMap) {
+        for (Entry<? extends K, ? extends V> e : inMap.entrySet()) {
+            this.put(e.getKey(), e.getValue());
+        }
+    }
+
+    @Override
+    public Collection<V> values() {
+        List<V> list = new ArrayList<V>();
+        Set<Entry<K, ExpiryObject>> delegatedSet = delegateMap.entrySet();
+        for (Entry<K, ExpiryObject> entry : delegatedSet) {
+            ExpiryObject value = entry.getValue();
+            list.add(value.getValue());
+        }
+        return list;
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ExpireThread getExpireThread() {
+        return expireThread;
+    }
+
+    public int getExpirationInterval() {
+        return expireThread.getExpirationInterval();
+    }
+
+    public void setExpirationInterval(int expirationInterval) {
+        expireThread.setExpirationInterval(expirationInterval);
+    }
+
+    public int getTimeToLive() {
+        return expireThread.getTimeToLive();
+    }
+
+    public void setTimeToLive(int timeToLive) {
+        expireThread.setTimeToLive(timeToLive);
+    }
+
+    @Override
+    public String toString() {
+        return "ExpiringMap{" +
+                "delegateMap=" + delegateMap.toString() +
+                ", expireThread=" + expireThread.toString() +
+                '}';
+    }
+
+    /**
+     * can be expired object
+     */
+    private class ExpiryObject {
+        private K key;
+        private V value;
+        private AtomicLong lastAccessTime;
+
+        ExpiryObject(K key, V value, long lastAccessTime) {
+            if (value == null) {
+                throw new IllegalArgumentException("An expiring object cannot be null.");
+            }
+            this.key = key;
+            this.value = value;
+            this.lastAccessTime = new AtomicLong(lastAccessTime);
+        }
+
+        public long getLastAccessTime() {
+            return lastAccessTime.get();
+        }
+
+        public void setLastAccessTime(long lastAccessTime) {
+            this.lastAccessTime.set(lastAccessTime);
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return value;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return value.equals(obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return value.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "ExpiryObject{" +
+                    "key=" + key +
+                    ", value=" + value +
+                    ", lastAccessTime=" + lastAccessTime +
+                    '}';
+        }
+    }
+
+    /**
+     * Background thread, periodically checking if the data is out of date
+     */
+    public class ExpireThread implements Runnable {
+        private long timeToLiveMillis;
+        private long expirationIntervalMillis;
+        private volatile boolean running = false;
+        private final Thread expirerThread;
+
+        @Override
+        public String toString() {
+            return "ExpireThread{" +
+                    ", timeToLiveMillis=" + timeToLiveMillis +
+                    ", expirationIntervalMillis=" + expirationIntervalMillis +
+                    ", running=" + running +
+                    ", expirerThread=" + expirerThread +
+                    '}';
+        }
+
+        public ExpireThread() {
+            expirerThread = new Thread(this, "ExpiryMapExpire-" + expireCount.getAndIncrement());
+            expirerThread.setDaemon(true);
+        }
+
+        @Override
+        public void run() {
+            while (running) {
+                processExpires();
+                try {
+                    Thread.sleep(expirationIntervalMillis);
+                } catch (InterruptedException e) {
+                    running = false;
+                }
+            }
+        }
+
+        private void processExpires() {
+            long timeNow = System.currentTimeMillis();
+            for (ExpiryObject o : delegateMap.values()) {
+                if (timeToLiveMillis <= 0) {
+                    continue;
+                }
+                long timeIdle = timeNow - o.getLastAccessTime();
+                if (timeIdle >= timeToLiveMillis) {
+                    delegateMap.remove(o.getKey());
+                }
+            }
+        }
+
+        /**
+         * start expiring Thread
+         */
+        public void startExpiring() {
+            if (!running) {
+                running = true;
+                expirerThread.start();
+            }
+        }
+
+        /**
+         * start thread
+         */
+        public void startExpiryIfNotStarted() {
+            if (running) {
+                return;
+            }
+            startExpiring();
+        }
+
+        /**
+         * stop thread
+         */
+        public void stopExpiring() {
+            if (running) {
+                running = false;
+                expirerThread.interrupt();
+            }
+        }
+
+        /**
+         * get thread state
+         *
+         * @return thread state
+         */
+        public boolean isRunning() {
+            return running;
+        }
+
+        /**
+         * get time to live
+         *
+         * @return time to live
+         */
+        public int getTimeToLive() {
+            return (int) timeToLiveMillis / 1000;
+        }
+
+        /**
+         * update time to live
+         *
+         * @param timeToLive time to live
+         */
+        public void setTimeToLive(long timeToLive) {
+            this.timeToLiveMillis = timeToLive * 1000;
+        }
+
+        /**
+         * get expiration interval
+         *
+         * @return expiration interval (second)
+         */
+        public int getExpirationInterval() {
+            return (int) expirationIntervalMillis / 1000;
+        }
+
+        /**
+         * set expiration interval
+         *
+         * @param expirationInterval expiration interval (second)
+         */
+        public void setExpirationInterval(long expirationInterval) {
+            this.expirationIntervalMillis = expirationInterval * 1000;
+        }
+    }
+}
+
+
+
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCache.java
new file mode 100644
index 0000000..7972a9d
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCache.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.jcache;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+import javax.cache.spi.CachingProvider;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.constants.CommonConstants.METHOD_KEY;
+
+/**
+ * This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
+ * with value <b>jcache</b>, dubbo initialize the instance of this class using {@link JCacheFactory} to store method's returns value
+ * to server from store without making method call.
+ *
+ * @see Cache
+ * @see JCacheFactory
+ * @see org.apache.dubbo.cache.support.AbstractCacheFactory
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ */
+public class JCache implements org.apache.dubbo.cache.Cache {
+
+    private final Cache<Object, Object> store;
+
+    public JCache(URL url) {
+        String method = url.getParameter(METHOD_KEY, "");
+        String key = url.getAddress() + "." + url.getServiceKey() + "." + method;
+        // jcache parameter is the full-qualified class name of SPI implementation
+        String type = url.getParameter("jcache");
+
+        CachingProvider provider = StringUtils.isEmpty(type) ? Caching.getCachingProvider() : Caching.getCachingProvider(type);
+        CacheManager cacheManager = provider.getCacheManager();
+        Cache<Object, Object> cache = cacheManager.getCache(key);
+        if (cache == null) {
+            try {
+                //configure the cache
+                MutableConfiguration config =
+                        new MutableConfiguration<>()
+                                .setTypes(Object.class, Object.class)
+                                .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MILLISECONDS, url.getMethodParameter(method, "cache.write.expire", 60 * 1000))))
+                                .setStoreByValue(false)
+                                .setManagementEnabled(true)
+                                .setStatisticsEnabled(true);
+                cache = cacheManager.createCache(key, config);
+            } catch (CacheException e) {
+                // concurrent cache initialization
+                cache = cacheManager.getCache(key);
+            }
+        }
+
+        this.store = cache;
+    }
+
+    @Override
+    public void put(Object key, Object value) {
+        store.put(key, value);
+    }
+
+    @Override
+    public Object get(Object key) {
+        return store.get(key);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCacheFactory.java
new file mode 100644
index 0000000..aba9a2f
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/jcache/JCacheFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.jcache;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.common.URL;
+
+import javax.cache.spi.CachingProvider;
+
+/**
+ * JCacheFactory is factory class to provide instance of javax spi cache.Implement {@link org.apache.dubbo.cache.CacheFactory} by
+ * extending {@link AbstractCacheFactory} and provide
+ * @see AbstractCacheFactory
+ * @see JCache
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ * @see Cache
+ * @see CachingProvider
+ * @see javax.cache.Cache
+ * @see javax.cache.CacheManager
+ */
+public class JCacheFactory extends AbstractCacheFactory {
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by JCache.
+     * @param url url of the method
+     * @return JCache instance of cache
+     */
+    @Override
+    protected Cache createCache(URL url) {
+        return new JCache(url);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCache.java
new file mode 100644
index 0000000..9ccc979
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCache.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.lfu;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.LFUCache;
+
+/**
+ * This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
+ * with value <b>lfu</b>, dubbo initialize the instance of this class using {@link LfuCacheFactory} to store method's returns value
+ * to server from store without making method call.
+ * <pre>
+ *     e.g. 1) &lt;dubbo:service cache="lfu" cache.size="5000" cache.evictionFactor="0.3"/&gt;
+ *          2) &lt;dubbo:consumer cache="lfu" /&gt;
+ * </pre>
+ * <pre>
+ * LfuCache uses url's <b>cache.size</b> value for its max store size, url's <b>cache.evictionFactor</b> value for its eviction factor,
+ * default store size value will be 1000, default eviction factor will be 0.3
+ * </pre>
+ *
+ * @see Cache
+ * @see LfuCacheFactory
+ * @see org.apache.dubbo.cache.support.AbstractCacheFactory
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ */
+public class LfuCache implements Cache {
+
+    /**
+     * This is used to store cache records
+     */
+    private final LFUCache store;
+
+    /**
+     *  Initialize LfuCache, it uses constructor argument <b>cache.size</b> value as its storage max size.
+     *  If nothing is provided then it will use 1000 as default size value. <b>cache.evictionFactor</b> value as its eviction factor.
+     *  If nothing is provided then it will use 0.3 as default value.
+     * @param url A valid URL instance
+     */
+    public LfuCache (URL url) {
+        final int max = url.getParameter("cache.size", 1000);
+        final float factor = url.getParameter("cache.evictionFactor", 0.75f);
+        this.store = new LFUCache(max, factor);
+    }
+
+    /**
+     * API to store value against a key in the calling thread scope.
+     * @param key  Unique identifier for the object being store.
+     * @param value Value getting store
+     */
+    @Override
+    public void put(Object key, Object value) {
+        store.put(key, value);
+    }
+
+    /**
+     * API to return stored value using a key against the calling thread specific store.
+     * @param key Unique identifier for cache lookup
+     * @return Return stored object against key
+     */
+    @Override
+    public Object get(Object key) {
+        return store.get(key);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCacheFactory.java
new file mode 100644
index 0000000..f04edca
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lfu/LfuCacheFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.lfu;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.common.URL;
+
+/**
+ * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
+ * instance of new {@link LfuCache}.
+ *
+ * @see AbstractCacheFactory
+ * @see LfuCache
+ * @see Cache
+ */
+public class LfuCacheFactory extends AbstractCacheFactory {
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by LfuCache.
+     * @param url url of the method
+     * @return ThreadLocalCache instance of cache
+     */
+    @Override
+    protected Cache createCache(URL url) {
+        return new LfuCache(url);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCache.java
new file mode 100644
index 0000000..1b8022f
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCache.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.lru;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.LRUCache;
+
+import java.util.Map;
+
+/**
+ * This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
+ * with value <b>lru</b>, dubbo initialize the instance of this class using {@link LruCacheFactory} to store method's returns value
+ * to server from store without making method call.
+ * <pre>
+ *     e.g. 1) &lt;dubbo:service cache="lru" cache.size="5000"/&gt;
+ *          2) &lt;dubbo:consumer cache="lru" /&gt;
+ * </pre>
+ * <pre>
+ * LruCache uses url's <b>cache.size</b> value for its max store size, if nothing is provided then
+ * default value will be 1000
+ * </pre>
+ *
+ * @see Cache
+ * @see LruCacheFactory
+ * @see org.apache.dubbo.cache.support.AbstractCacheFactory
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ */
+public class LruCache implements Cache {
+
+    /**
+     * This is used to store cache records
+     */
+    private final Map<Object, Object> store;
+
+    /**
+     * Initialize LruCache, it uses constructor argument <b>cache.size</b> value as its storage max size.
+     *  If nothing is provided then it will use 1000 as default value.
+     * @param url A valid URL instance
+     */
+    public LruCache(URL url) {
+        final int max = url.getParameter("cache.size", 1000);
+        this.store = new LRUCache<>(max);
+    }
+
+    /**
+     * API to store value against a key in the calling thread scope.
+     * @param key  Unique identifier for the object being store.
+     * @param value Value getting store
+     */
+    @Override
+    public void put(Object key, Object value) {
+        store.put(key, value);
+    }
+
+    /**
+     * API to return stored value using a key against the calling thread specific store.
+     * @param key Unique identifier for cache lookup
+     * @return Return stored object against key
+     */
+    @Override
+    public Object get(Object key) {
+        return store.get(key);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCacheFactory.java
new file mode 100644
index 0000000..9ec9486
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/lru/LruCacheFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.lru;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.common.URL;
+
+/**
+ * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
+ * instance of new {@link LruCache}.
+ *
+ * @see AbstractCacheFactory
+ * @see LruCache
+ * @see Cache
+ */
+public class LruCacheFactory extends AbstractCacheFactory {
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by LruCache.
+     * @param url url of the method
+     * @return ThreadLocalCache instance of cache
+     */
+    @Override
+    protected Cache createCache(URL url) {
+        return new LruCache(url);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCache.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCache.java
new file mode 100644
index 0000000..12b3b5c
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCache.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.threadlocal;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.common.URL;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class store the cache value per thread. If a service,method,consumer or provided is configured with key <b>cache</b>
+ * with value <b>threadlocal</b>, dubbo initialize the instance of this class using {@link ThreadLocalCacheFactory} to store method's returns value
+ * to server from store without making method call.
+ *  <pre>
+ *      e.g. &lt;dubbo:service cache="threadlocal" /&gt;
+ *  </pre>
+ * <pre>
+ * As this ThreadLocalCache stores key-value in memory without any expiry or delete support per thread wise, if number threads and number of key-value are high then jvm should be
+ * configured with appropriate memory.
+ * </pre>
+ *
+ * @see org.apache.dubbo.cache.support.AbstractCacheFactory
+ * @see org.apache.dubbo.cache.filter.CacheFilter
+ * @see Cache
+ */
+public class ThreadLocalCache implements Cache {
+
+    /**
+     * Thread local variable to store cached data.
+     */
+    private final ThreadLocal<Map<Object, Object>> store;
+
+    /**
+     * Taken URL as an argument to create an instance of ThreadLocalCache. In this version of implementation constructor
+     * argument is not getting used in the scope of this class.
+     * @param url
+     */
+    public ThreadLocalCache(URL url) {
+        this.store = ThreadLocal.withInitial(HashMap::new);
+    }
+
+    /**
+     * API to store value against a key in the calling thread scope.
+     * @param key  Unique identifier for the object being store.
+     * @param value Value getting store
+     */
+    @Override
+    public void put(Object key, Object value) {
+        store.get().put(key, value);
+    }
+
+    /**
+     * API to return stored value using a key against the calling thread specific store.
+     * @param key Unique identifier for cache lookup
+     * @return Return stored object against key
+     */
+    @Override
+    public Object get(Object key) {
+        return store.get().get(key);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
new file mode 100644
index 0000000..cdda6cf
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.threadlocal;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.common.URL;
+
+/**
+ * Implement {@link org.apache.dubbo.cache.CacheFactory} by extending {@link AbstractCacheFactory} and provide
+ * instance of new {@link ThreadLocalCache}. Note about this class is, each thread does not have a local copy of factory.
+ *
+ * @see AbstractCacheFactory
+ * @see ThreadLocalCache
+ * @see Cache
+ */
+public class ThreadLocalCacheFactory extends AbstractCacheFactory {
+
+    /**
+     * Takes url as an method argument and return new instance of cache store implemented by ThreadLocalCache.
+     * @param url url of the method
+     * @return ThreadLocalCache instance of cache
+     */
+    @Override
+    protected Cache createCache(URL url) {
+        return new ThreadLocalCache(url);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory b/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory
new file mode 100644
index 0000000..1ea180a
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory
@@ -0,0 +1,4 @@
+threadlocal=org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
+lru=org.apache.dubbo.cache.support.lru.LruCacheFactory
+jcache=org.apache.dubbo.cache.support.jcache.JCacheFactory
+expiring=org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
new file mode 100644
index 0000000..8d6259b
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -0,0 +1 @@
+cache=org.apache.dubbo.cache.filter.CacheFilter
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java
new file mode 100644
index 0000000..3326833
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/filter/CacheFilterTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.filter;
+
+import org.apache.dubbo.cache.CacheFactory;
+import org.apache.dubbo.cache.support.expiring.ExpiringCacheFactory;
+import org.apache.dubbo.cache.support.jcache.JCacheFactory;
+import org.apache.dubbo.cache.support.lru.LruCacheFactory;
+import org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcInvocation;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+public class CacheFilterTest {
+    private RpcInvocation invocation;
+    private CacheFilter cacheFilter = new CacheFilter();
+    private Invoker<?> invoker = mock(Invoker.class);
+    private Invoker<?> invoker1 = mock(Invoker.class);
+    private Invoker<?> invoker2 = mock(Invoker.class);
+    private Invoker<?> invoker3 = mock(Invoker.class);
+    private Invoker<?> invoker4 = mock(Invoker.class);
+
+    static Stream<Arguments> cacheFactories() {
+        return Stream.of(
+                Arguments.of("lru", new LruCacheFactory()),
+                Arguments.of("jcache", new JCacheFactory()),
+                Arguments.of("threadlocal", new ThreadLocalCacheFactory()),
+                Arguments.of("expiring", new ExpiringCacheFactory())
+        );
+    }
+
+    public void setUp(String cacheType, CacheFactory cacheFactory) {
+        invocation = new RpcInvocation();
+        cacheFilter.setCacheFactory(cacheFactory);
+
+        URL url = URL.valueOf("test://test:11/test?cache=" + cacheType);
+
+        given(invoker.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value", invocation));
+        given(invoker.getUrl()).willReturn(url);
+
+        given(invoker1.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value1", invocation));
+        given(invoker1.getUrl()).willReturn(url);
+
+        given(invoker2.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult("value2", invocation));
+        given(invoker2.getUrl()).willReturn(url);
+
+        given(invoker3.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(new RuntimeException(), invocation));
+        given(invoker3.getUrl()).willReturn(url);
+
+        given(invoker4.invoke(invocation)).willReturn(AsyncRpcResult.newDefaultAsyncResult(invocation));
+        given(invoker4.getUrl()).willReturn(url);
+    }
+
+    @ParameterizedTest
+    @MethodSource("cacheFactories")
+    public void testNonArgsMethod(String cacheType, CacheFactory cacheFactory) {
+        setUp(cacheType, cacheFactory);
+        invocation.setMethodName("echo");
+        invocation.setParameterTypes(new Class<?>[]{});
+        invocation.setArguments(new Object[]{});
+
+        cacheFilter.invoke(invoker, invocation);
+        Result rpcResult1 = cacheFilter.invoke(invoker1, invocation);
+        Result rpcResult2 = cacheFilter.invoke(invoker2, invocation);
+        Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue());
+        Assertions.assertEquals(rpcResult1.getValue(), "value");
+    }
+
+    @ParameterizedTest
+    @MethodSource("cacheFactories")
+    public void testMethodWithArgs(String cacheType, CacheFactory cacheFactory) {
+        setUp(cacheType, cacheFactory);
+        invocation.setMethodName("echo1");
+        invocation.setParameterTypes(new Class<?>[]{String.class});
+        invocation.setArguments(new Object[]{"arg1"});
+
+        cacheFilter.invoke(invoker, invocation);
+        Result rpcResult1 = cacheFilter.invoke(invoker1, invocation);
+        Result rpcResult2 = cacheFilter.invoke(invoker2, invocation);
+        Assertions.assertEquals(rpcResult1.getValue(), rpcResult2.getValue());
+        Assertions.assertEquals(rpcResult1.getValue(), "value");
+    }
+
+    @ParameterizedTest
+    @MethodSource("cacheFactories")
+    public void testException(String cacheType, CacheFactory cacheFactory) {
+        setUp(cacheType, cacheFactory);
+        invocation.setMethodName("echo1");
+        invocation.setParameterTypes(new Class<?>[]{String.class});
+        invocation.setArguments(new Object[]{"arg2"});
+
+        cacheFilter.invoke(invoker3, invocation);
+        Result rpcResult = cacheFilter.invoke(invoker2, invocation);
+        Assertions.assertEquals(rpcResult.getValue(), "value2");
+    }
+
+    @ParameterizedTest
+    @MethodSource("cacheFactories")
+    public void testNull(String cacheType, CacheFactory cacheFactory) {
+        setUp(cacheType, cacheFactory);
+        invocation.setMethodName("echo1");
+        invocation.setParameterTypes(new Class<?>[]{String.class});
+        invocation.setArguments(new Object[]{"arg3"});
+
+        cacheFilter.invoke(invoker4, invocation);
+        Result result1 = cacheFilter.invoke(invoker1, invocation);
+        Result result2 = cacheFilter.invoke(invoker2, invocation);
+        Assertions.assertNull(result1.getValue());
+        Assertions.assertNull(result2.getValue());
+    }
+}
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/AbstractCacheFactoryTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/AbstractCacheFactoryTest.java
new file mode 100644
index 0000000..f93a453
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/AbstractCacheFactoryTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.RpcInvocation;
+
+public abstract class AbstractCacheFactoryTest {
+
+    protected Cache constructCache() {
+        URL url = URL.valueOf("test://test:11/test?cache=lru");
+        Invocation invocation = new RpcInvocation();
+        return getCacheFactory().getCache(url, invocation);
+    }
+
+    protected abstract AbstractCacheFactory getCacheFactory();
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java
new file mode 100644
index 0000000..23484c6
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/expiring/ExpiringCacheFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.expiring;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.cache.support.AbstractCacheFactoryTest;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class ExpiringCacheFactoryTest extends AbstractCacheFactoryTest {
+    @Test
+    public void testLruCacheFactory() throws Exception {
+        Cache cache = super.constructCache();
+        assertThat(cache instanceof ExpiringCache, is(true));
+    }
+
+    @Override
+    protected AbstractCacheFactory getCacheFactory() {
+        return new ExpiringCacheFactory();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/jcache/JCacheFactoryTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/jcache/JCacheFactoryTest.java
new file mode 100644
index 0000000..3260fa6
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/jcache/JCacheFactoryTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.jcache;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.cache.support.AbstractCacheFactoryTest;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.RpcInvocation;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class JCacheFactoryTest extends AbstractCacheFactoryTest {
+
+    @Test
+    public void testJCacheFactory() throws Exception {
+        Cache cache = super.constructCache();
+        assertThat(cache instanceof JCache, is(true));
+    }
+
+    @Test
+    public void testJCacheGetExpired() throws Exception {
+        URL url = URL.valueOf("test://test:12/test?cache=jacache&cache.write.expire=1");
+        AbstractCacheFactory cacheFactory = getCacheFactory();
+        Invocation invocation = new RpcInvocation();
+        Cache cache = cacheFactory.getCache(url, invocation);
+        cache.put("testKey", "testValue");
+        Thread.sleep(10);
+        assertNull(cache.get("testKey"));
+    }
+
+    @Override
+    protected AbstractCacheFactory getCacheFactory() {
+        return new JCacheFactory();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/lru/LruCacheFactoryTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/lru/LruCacheFactoryTest.java
new file mode 100644
index 0000000..149c122
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/lru/LruCacheFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.lru;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.cache.support.AbstractCacheFactoryTest;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class LruCacheFactoryTest extends AbstractCacheFactoryTest{
+    @Test
+    public void testLruCacheFactory() throws Exception {
+        Cache cache = super.constructCache();
+        assertThat(cache instanceof LruCache, is(true));
+    }
+
+    @Override
+    protected AbstractCacheFactory getCacheFactory() {
+        return new LruCacheFactory();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactoryTest.java b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactoryTest.java
new file mode 100644
index 0000000..800f5ad
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-cache/src/test/java/org/apache/dubbo/cache/support/threadlocal/ThreadLocalCacheFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.cache.support.threadlocal;
+
+import org.apache.dubbo.cache.Cache;
+import org.apache.dubbo.cache.support.AbstractCacheFactory;
+import org.apache.dubbo.cache.support.AbstractCacheFactoryTest;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class ThreadLocalCacheFactoryTest extends AbstractCacheFactoryTest {
+    @Test
+    public void testThreadLocalCacheFactory() throws Exception {
+        Cache cache = super.constructCache();
+        assertThat(cache instanceof ThreadLocalCache, is(true));
+    }
+
+    @Override
+    protected AbstractCacheFactory getCacheFactory() {
+        return new ThreadLocalCacheFactory();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/pom.xml b/dubbo-spi-filter/dubbo-filter-validation/pom.xml
new file mode 100644
index 0000000..933f1c9
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/pom.xml
@@ -0,0 +1,72 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-filter</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <artifactId>dubbo-filter-validation</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The validation module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <scope>test</scope>
+            <version>${hibernate_validator_version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.el</groupId>
+            <artifactId>javax.el-api</artifactId>
+            <scope>test</scope>
+            <version>${el_api_version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <scope>test</scope>
+            <version>${jaxb_api_version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <scope>test</scope>
+            <version>${jaxb_api_version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-core</artifactId>
+            <scope>test</scope>
+            <version>${jaxb_api_version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/MethodValidated.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/MethodValidated.java
new file mode 100644
index 0000000..ca41a52
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/MethodValidated.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Method grouping validation.
+ * <p>
+ * Scenario: this annotation can be used on interface's method when need to check against group before invoke the method
+ * For example: <pre> @MethodValidated({Save.class, Update.class})
+ * void relatedQuery(ValidationParameter parameter);
+ * </pre>
+ * It means both Save group and Update group are needed to check when method relatedQuery is invoked.
+ * </p>
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface MethodValidated {
+    Class<?>[] value() default {};
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java
new file mode 100644
index 0000000..73c4cd8
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.SPI;
+
+import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
+
+/**
+ * Instance of Validation interface provide instance of {@link Validator} based on the value of <b>validation</b> attribute.
+ */
+@SPI("jvalidation")
+public interface Validation {
+
+    /**
+     * Return the instance of {@link Validator} for a given url.
+     * @param url Invocation url
+     * @return Instance of {@link Validator}
+     */
+    @Adaptive(VALIDATION_KEY)
+    Validator getValidator(URL url);
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validator.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validator.java
new file mode 100644
index 0000000..c78c62a
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation;
+
+/**
+ * Instance of validator class is an extension to perform validation on method input parameter before the actual method invocation.
+ *
+ */
+public interface Validator {
+
+    void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception;
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java
new file mode 100644
index 0000000..0df2752
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.filter;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.validation.Validation;
+import org.apache.dubbo.validation.Validator;
+
+import javax.validation.ValidationException;
+
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
+import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;
+
+/**
+ * ValidationFilter invoke the validation by finding the right {@link Validator} instance based on the
+ * configured <b>validation</b> attribute value of invoker url before the actual method invocation.
+ *
+ * <pre>
+ *     e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
+ *     In the above configuration a validation has been configured of type jvalidation. On invocation of method <b>save</b>
+ *     dubbo will invoke {@link org.apache.dubbo.validation.support.jvalidation.JValidator}
+ * </pre>
+ *
+ * To add a new type of validation
+ * <pre>
+ *     e.g. &lt;dubbo:method name="save" validation="special" /&gt;
+ *     where "special" is representing a validator for special character.
+ * </pre>
+ *
+ * developer needs to do
+ * <br/>
+ * 1)Implement a SpecialValidation.java class (package name xxx.yyy.zzz) either by implementing {@link Validation} or extending {@link org.apache.dubbo.validation.support.AbstractValidation} <br/>
+ * 2)Implement a SpecialValidator.java class (package name xxx.yyy.zzz) <br/>
+ * 3)Add an entry <b>special</b>=<b>xxx.yyy.zzz.SpecialValidation</b> under <b>META-INF folders org.apache.dubbo.validation.Validation file</b>.
+ *
+ * @see Validation
+ * @see Validator
+ * @see Filter
+ * @see org.apache.dubbo.validation.support.AbstractValidation
+ */
+@Activate(group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = 10000)
+public class ValidationFilter implements Filter {
+
+    private Validation validation;
+
+    /**
+     * Sets the validation instance for ValidationFilter
+     * @param validation Validation instance injected by dubbo framework based on "validation" attribute value.
+     */
+    public void setValidation(Validation validation) {
+        this.validation = validation;
+    }
+
+    /**
+     * Perform the validation of before invoking the actual method based on <b>validation</b> attribute value.
+     * @param invoker    service
+     * @param invocation invocation.
+     * @return Method invocation result
+     * @throws RpcException Throws RpcException if  validation failed or any other runtime exception occurred.
+     */
+    @Override
+    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        if (validation != null && !invocation.getMethodName().startsWith("$")
+                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
+            try {
+                Validator validator = validation.getValidator(invoker.getUrl());
+                if (validator != null) {
+                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
+                }
+            } catch (RpcException e) {
+                throw e;
+            } catch (ValidationException e) {
+                // only use exception's message to avoid potential serialization issue
+                return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
+            } catch (Throwable t) {
+                return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
+            }
+        }
+        return invoker.invoke(invocation);
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/AbstractValidation.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/AbstractValidation.java
new file mode 100644
index 0000000..a6f9c8e
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/AbstractValidation.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.validation.Validation;
+import org.apache.dubbo.validation.Validator;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * AbstractValidation is abstract class for Validation interface. It helps low level Validation implementation classes
+ * by performing common task e.g. key formation, storing instance of validation class to avoid creation of unnecessary
+ * copy of validation instance and faster execution.
+ *
+ * @see Validation
+ * @see Validator
+ */
+public abstract class AbstractValidation implements Validation {
+
+    private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<>();
+
+    @Override
+    public Validator getValidator(URL url) {
+        String key = url.toFullString();
+        Validator validator = validators.get(key);
+        if (validator == null) {
+            validators.put(key, createValidator(url));
+            validator = validators.get(key);
+        }
+        return validator;
+    }
+
+    protected abstract Validator createValidator(URL url);
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidation.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidation.java
new file mode 100644
index 0000000..e8b48cf
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidation.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support.jvalidation;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.validation.Validator;
+import org.apache.dubbo.validation.support.AbstractValidation;
+
+/**
+ * Creates a new instance of {@link Validator} using input argument url.
+ * @see AbstractValidation
+ * @see Validator
+ */
+public class JValidation extends AbstractValidation {
+
+    /**
+     * Return new instance of {@link JValidator}
+     * @param url Valid URL instance
+     * @return Instance of JValidator
+     */
+    @Override
+    protected Validator createValidator(URL url) {
+        return new JValidator(url);
+    }
+
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java
new file mode 100644
index 0000000..ea8c6db
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support.jvalidation;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.bytecode.ClassGenerator;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ReflectUtils;
+import org.apache.dubbo.validation.MethodValidated;
+import org.apache.dubbo.validation.Validator;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtNewConstructor;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.annotation.ArrayMemberValue;
+import javassist.bytecode.annotation.BooleanMemberValue;
+import javassist.bytecode.annotation.ByteMemberValue;
+import javassist.bytecode.annotation.CharMemberValue;
+import javassist.bytecode.annotation.ClassMemberValue;
+import javassist.bytecode.annotation.DoubleMemberValue;
+import javassist.bytecode.annotation.EnumMemberValue;
+import javassist.bytecode.annotation.FloatMemberValue;
+import javassist.bytecode.annotation.IntegerMemberValue;
+import javassist.bytecode.annotation.LongMemberValue;
+import javassist.bytecode.annotation.MemberValue;
+import javassist.bytecode.annotation.ShortMemberValue;
+import javassist.bytecode.annotation.StringMemberValue;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.ValidatorFactory;
+import javax.validation.groups.Default;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of JValidation. JValidation is invoked if configuration validation attribute value is 'jvalidation'.
+ * <pre>
+ *     e.g. &lt;dubbo:method name="save" validation="jvalidation" /&gt;
+ * </pre>
+ */
+public class JValidator implements Validator {
+
+    private static final Logger logger = LoggerFactory.getLogger(JValidator.class);
+
+    private final Class<?> clazz;
+
+    private final Map<String, Class> methodClassMap;
+
+    private final javax.validation.Validator validator;
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public JValidator(URL url) {
+        this.clazz = ReflectUtils.forName(url.getServiceInterface());
+        String jvalidation = url.getParameter("jvalidation");
+        ValidatorFactory factory;
+        if (jvalidation != null && jvalidation.length() > 0) {
+            factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
+        } else {
+            factory = Validation.buildDefaultValidatorFactory();
+        }
+        this.validator = factory.getValidator();
+        this.methodClassMap = new ConcurrentHashMap<>();
+    }
+
+    private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
+        if (!hasConstraintParameter(method)) {
+            return null;
+        }
+        try {
+            String parameterClassName = generateMethodParameterClassName(clazz, method);
+            Class<?> parameterClass;
+            try {
+                parameterClass = Class.forName(parameterClassName, true, clazz.getClassLoader());
+            } catch (ClassNotFoundException e) {
+                parameterClass = generateMethodParameterClass(clazz, method, parameterClassName);
+            }
+            Object parameterBean = parameterClass.newInstance();
+            for (int i = 0; i < args.length; i++) {
+                Field field = parameterClass.getField(method.getName() + "Argument" + i);
+                field.set(parameterBean, args[i]);
+            }
+            return parameterBean;
+        } catch (Throwable e) {
+            logger.warn(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * try to generate methodParameterClass.
+     *
+     * @param clazz interface class
+     * @param method invoke method
+     * @param parameterClassName generated parameterClassName
+     * @return Class<?> generated methodParameterClass
+     * @throws Exception
+     */
+    private static Class<?> generateMethodParameterClass(Class<?> clazz, Method method, String parameterClassName)
+        throws Exception {
+        ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
+        synchronized (parameterClassName.intern()) {
+            CtClass ctClass = null;
+            try {
+                ctClass = pool.getCtClass(parameterClassName);
+            } catch (NotFoundException ignore) {
+            }
+
+            if (null == ctClass) {
+                ctClass = pool.makeClass(parameterClassName);
+                ClassFile classFile = ctClass.getClassFile();
+                classFile.setVersionToJava5();
+                ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));
+                // parameter fields
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+                for (int i = 0; i < parameterTypes.length; i++) {
+                    Class<?> type = parameterTypes[i];
+                    Annotation[] annotations = parameterAnnotations[i];
+                    AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);
+                    for (Annotation annotation : annotations) {
+                        if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
+                            javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(
+                                classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName()));
+                            Method[] members = annotation.annotationType().getMethods();
+                            for (Method member : members) {
+                                if (Modifier.isPublic(member.getModifiers())
+                                    && member.getParameterTypes().length == 0
+                                    && member.getDeclaringClass() == annotation.annotationType()) {
+                                    Object value = member.invoke(annotation);
+                                    if (null != value) {
+                                        MemberValue memberValue = createMemberValue(
+                                            classFile.getConstPool(), pool.get(member.getReturnType().getName()), value);
+                                        ja.addMemberValue(member.getName(), memberValue);
+                                    }
+                                }
+                            }
+                            attribute.addAnnotation(ja);
+                        }
+                    }
+                    String fieldName = method.getName() + "Argument" + i;
+                    CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));
+                    ctField.getFieldInfo().addAttribute(attribute);
+                    ctClass.addField(ctField);
+                }
+                return ctClass.toClass(clazz.getClassLoader(), null);
+            } else {
+                return Class.forName(parameterClassName, true, clazz.getClassLoader());
+            }
+        }
+    }
+
+    private static String generateMethodParameterClassName(Class<?> clazz, Method method) {
+        StringBuilder builder = new StringBuilder().append(clazz.getName())
+                .append("_")
+                .append(toUpperMethoName(method.getName()))
+                .append("Parameter");
+
+        Class<?>[] parameterTypes = method.getParameterTypes();
+        for (Class<?> parameterType : parameterTypes) {
+            builder.append("_").append(parameterType.getName());
+        }
+
+        return builder.toString();
+    }
+
+    private static boolean hasConstraintParameter(Method method) {
+        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+        if (parameterAnnotations != null && parameterAnnotations.length > 0) {
+            for (Annotation[] annotations : parameterAnnotations) {
+                for (Annotation annotation : annotations) {
+                    if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static String toUpperMethoName(String methodName) {
+        return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
+    }
+
+    // Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);
+    private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {
+        MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type);
+        if (memberValue instanceof BooleanMemberValue) {
+            ((BooleanMemberValue) memberValue).setValue((Boolean) value);
+        } else if (memberValue instanceof ByteMemberValue) {
+            ((ByteMemberValue) memberValue).setValue((Byte) value);
+        } else if (memberValue instanceof CharMemberValue) {
+            ((CharMemberValue) memberValue).setValue((Character) value);
+        } else if (memberValue instanceof ShortMemberValue) {
+            ((ShortMemberValue) memberValue).setValue((Short) value);
+        } else if (memberValue instanceof IntegerMemberValue) {
+            ((IntegerMemberValue) memberValue).setValue((Integer) value);
+        } else if (memberValue instanceof LongMemberValue) {
+            ((LongMemberValue) memberValue).setValue((Long) value);
+        } else if (memberValue instanceof FloatMemberValue) {
+            ((FloatMemberValue) memberValue).setValue((Float) value);
+        } else if (memberValue instanceof DoubleMemberValue) {
+            ((DoubleMemberValue) memberValue).setValue((Double) value);
+        } else if (memberValue instanceof ClassMemberValue) {
+            ((ClassMemberValue) memberValue).setValue(((Class<?>) value).getName());
+        } else if (memberValue instanceof StringMemberValue) {
+            ((StringMemberValue) memberValue).setValue((String) value);
+        } else if (memberValue instanceof EnumMemberValue) {
+            ((EnumMemberValue) memberValue).setValue(((Enum<?>) value).name());
+        }
+        /* else if (memberValue instanceof AnnotationMemberValue) */
+        else if (memberValue instanceof ArrayMemberValue) {
+            CtClass arrayType = type.getComponentType();
+            int len = Array.getLength(value);
+            MemberValue[] members = new MemberValue[len];
+            for (int i = 0; i < len; i++) {
+                members[i] = createMemberValue(cp, arrayType, Array.get(value, i));
+            }
+            ((ArrayMemberValue) memberValue).setValue(members);
+        }
+        return memberValue;
+    }
+
+    @Override
+    public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
+        List<Class<?>> groups = new ArrayList<>();
+        Class<?> methodClass = methodClass(methodName);
+        if (methodClass != null) {
+            groups.add(methodClass);
+        }
+        Set<ConstraintViolation<?>> violations = new HashSet<>();
+        Method method = clazz.getMethod(methodName, parameterTypes);
+        Class<?>[] methodClasses;
+        if (method.isAnnotationPresent(MethodValidated.class)){
+            methodClasses = method.getAnnotation(MethodValidated.class).value();
+            groups.addAll(Arrays.asList(methodClasses));
+        }
+        // add into default group
+        groups.add(0, Default.class);
+        groups.add(1, clazz);
+
+        // convert list to array
+        Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);
+
+        Object parameterBean = getMethodParameterBean(clazz, method, arguments);
+        if (parameterBean != null) {
+            violations.addAll(validator.validate(parameterBean, classgroups ));
+        }
+
+        for (Object arg : arguments) {
+            validate(violations, arg, classgroups);
+        }
+
+        if (!violations.isEmpty()) {
+            logger.error("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations);
+            throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
+        }
+    }
+
+    private Class methodClass(String methodName) {
+        Class<?> methodClass = null;
+        String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);
+        Class cached = methodClassMap.get(methodClassName);
+        if (cached != null) {
+            return cached == clazz ? null : cached;
+        }
+        try {
+            methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
+            methodClassMap.put(methodClassName, methodClass);
+        } catch (ClassNotFoundException e) {
+            methodClassMap.put(methodClassName, clazz);
+        }
+        return methodClass;
+    }
+
+    private void validate(Set<ConstraintViolation<?>> violations, Object arg, Class<?>... groups) {
+        if (arg != null && !ReflectUtils.isPrimitives(arg.getClass())) {
+            if (arg instanceof Object[]) {
+                for (Object item : (Object[]) arg) {
+                    validate(violations, item, groups);
+                }
+            } else if (arg instanceof Collection) {
+                for (Object item : (Collection<?>) arg) {
+                    validate(violations, item, groups);
+                }
+            } else if (arg instanceof Map) {
+                for (Map.Entry<?, ?> entry : ((Map<?, ?>) arg).entrySet()) {
+                    validate(violations, entry.getKey(), groups);
+                    validate(violations, entry.getValue(), groups);
+                }
+            } else {
+                violations.addAll(validator.validate(arg, groups));
+            }
+        }
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter b/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
new file mode 100644
index 0000000..65bf329
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
@@ -0,0 +1 @@
+validation=org.apache.dubbo.validation.filter.ValidationFilter
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.validation.Validation b/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.validation.Validation
new file mode 100644
index 0000000..ae3dc96
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.validation.Validation
@@ -0,0 +1 @@
+jvalidation=org.apache.dubbo.validation.support.jvalidation.JValidation
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java
new file mode 100644
index 0000000..f2efdd5
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.validation.Validation;
+import org.apache.dubbo.validation.Validator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+public class ValidationFilterTest {
+    private Invoker<?> invoker = mock(Invoker.class);
+    private Validation validation = mock(Validation.class);
+    private Validator validator = mock(Validator.class);
+    private RpcInvocation invocation = mock(RpcInvocation.class);
+
+    private ValidationFilter validationFilter;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        this.validationFilter = new ValidationFilter();
+    }
+
+    @Test
+    public void testItWithNotExistClass() throws Exception {
+        URL url = URL.valueOf("test://test:11/test?validation=true");
+
+        given(validation.getValidator(url)).willThrow(new IllegalStateException("Not found class test, cause: test"));
+        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+        given(invoker.getUrl()).willReturn(url);
+        given(invocation.getMethodName()).willReturn("echo1");
+        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
+        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});
+
+        validationFilter.setValidation(validation);
+        Result result = validationFilter.invoke(invoker, invocation);
+
+        assertThat(result.getException().getMessage(), is("Not found class test, cause: test"));
+
+    }
+
+    @Test
+    public void testItWithExistClass() throws Exception {
+        URL url = URL.valueOf("test://test:11/test?validation=true");
+
+        given(validation.getValidator(url)).willReturn(validator);
+        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+        given(invoker.getUrl()).willReturn(url);
+        given(invocation.getMethodName()).willReturn("echo1");
+        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
+        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});
+
+        validationFilter.setValidation(validation);
+        Result result = validationFilter.invoke(invoker, invocation);
+
+        assertThat(String.valueOf(result.getValue()), is("success"));
+    }
+
+    @Test
+    public void testItWithoutUrlParameters() throws Exception {
+        URL url = URL.valueOf("test://test:11/test");
+
+        given(validation.getValidator(url)).willReturn(validator);
+        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+        given(invoker.getUrl()).willReturn(url);
+        given(invocation.getMethodName()).willReturn("echo1");
+        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
+        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});
+
+        validationFilter.setValidation(validation);
+        Result result = validationFilter.invoke(invoker, invocation);
+
+        assertThat(String.valueOf(result.getValue()), is("success"));
+    }
+
+    @Test
+    public void testItWhileMethodNameStartWithDollar() throws Exception {
+        URL url = URL.valueOf("test://test:11/test");
+
+        given(validation.getValidator(url)).willReturn(validator);
+        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+        given(invoker.getUrl()).willReturn(url);
+        given(invocation.getMethodName()).willReturn("$echo1");
+        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
+        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});
+
+        validationFilter.setValidation(validation);
+        Result result = validationFilter.invoke(invoker, invocation);
+
+        assertThat(String.valueOf(result.getValue()), is("success"));
+
+    }
+
+
+    @Test
+    public void testItWhileThrowoutRpcException() throws Exception {
+        Assertions.assertThrows(RpcException.class, () -> {
+            URL url = URL.valueOf("test://test:11/test?validation=true");
+
+            given(validation.getValidator(url)).willThrow(new RpcException("rpc exception"));
+            given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
+            given(invoker.getUrl()).willReturn(url);
+            given(invocation.getMethodName()).willReturn("echo1");
+            given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
+            given(invocation.getArguments()).willReturn(new Object[]{"arg1"});
+
+            validationFilter.setValidation(validation);
+            validationFilter.invoke(invoker, invocation);
+        });
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidationTest.java b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidationTest.java
new file mode 100644
index 0000000..fbace3f
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidationTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support.jvalidation;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.validation.Validation;
+import org.apache.dubbo.validation.Validator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.validation.ValidationException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class JValidationTest {
+    @Test
+    public void testReturnTypeWithInvalidValidationProvider() {
+        Assertions.assertThrows(ValidationException.class, () -> {
+            Validation jValidation = new JValidation();
+            URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.JValidation?" +
+                    "jvalidation=org.apache.dubbo.validation.Validation");
+            jValidation.getValidator(url);
+        });
+
+    }
+
+    @Test
+    public void testReturnTypeWithDefaultValidatorProvider() {
+        Validation jValidation = new JValidation();
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.JValidation");
+        Validator validator = jValidation.getValidator(url);
+        assertThat(validator instanceof JValidator, is(true));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidatorTest.java b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidatorTest.java
new file mode 100644
index 0000000..94768d2
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/JValidatorTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support.jvalidation;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.validation.support.jvalidation.mock.ValidationParameter;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.validation.ConstraintViolationException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JValidatorTest {
+    @Test
+    public void testItWithNonExistMethod() throws Exception {
+        Assertions.assertThrows(NoSuchMethodException.class, () -> {
+            URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+            JValidator jValidator = new JValidator(url);
+            jValidator.validate("nonExistingMethod", new Class<?>[]{String.class}, new Object[]{"arg1"});
+        });
+    }
+
+    @Test
+    public void testItWithExistMethod() throws Exception {
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+        JValidator jValidator = new JValidator(url);
+        jValidator.validate("someMethod1", new Class<?>[]{String.class}, new Object[]{"anything"});
+    }
+
+    @Test
+    public void testItWhenItViolatedConstraint() throws Exception {
+        Assertions.assertThrows(ConstraintViolationException.class, () -> {
+            URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+            JValidator jValidator = new JValidator(url);
+            jValidator.validate("someMethod2", new Class<?>[]{ValidationParameter.class}, new Object[]{new ValidationParameter()});
+        });
+    }
+
+    @Test
+    public void testItWhenItMeetsConstraint() throws Exception {
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+        JValidator jValidator = new JValidator(url);
+        jValidator.validate("someMethod2", new Class<?>[]{ValidationParameter.class}, new Object[]{new ValidationParameter("NotBeNull")});
+    }
+
+    @Test
+    public void testItWithArrayArg() throws Exception {
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+        JValidator jValidator = new JValidator(url);
+        jValidator.validate("someMethod3", new Class<?>[]{ValidationParameter[].class}, new Object[]{new ValidationParameter[]{new ValidationParameter("parameter")}});
+    }
+
+    @Test
+    public void testItWithCollectionArg() throws Exception {
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+        JValidator jValidator = new JValidator(url);
+        jValidator.validate("someMethod4", new Class<?>[]{List.class}, new Object[]{Arrays.asList("parameter")});
+    }
+
+    @Test
+    public void testItWithMapArg() throws Exception {
+        URL url = URL.valueOf("test://test:11/org.apache.dubbo.validation.support.jvalidation.mock.JValidatorTestTarget");
+        JValidator jValidator = new JValidator(url);
+        Map<String, String> map = new HashMap<>();
+        map.put("key", "value");
+        jValidator.validate("someMethod5", new Class<?>[]{Map.class}, new Object[]{map});
+    }
+}
\ No newline at end of file
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java
new file mode 100644
index 0000000..f65eb05
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.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.validation.support.jvalidation.mock;
+
+import org.apache.dubbo.validation.MethodValidated;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Map;
+
+public interface JValidatorTestTarget {
+    @MethodValidated
+    void someMethod1(String anything);
+
+    @MethodValidated(Test2.class)
+    void someMethod2(@NotNull ValidationParameter validationParameter);
+
+    void someMethod3(ValidationParameter[] parameters);
+
+    void someMethod4(List<String> strings);
+
+    void someMethod5(Map<String, String> map);
+
+    @interface Test2 {
+    }
+
+}
diff --git a/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/ValidationParameter.java b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/ValidationParameter.java
new file mode 100644
index 0000000..0b54a04
--- /dev/null
+++ b/dubbo-spi-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/ValidationParameter.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.validation.support.jvalidation.mock;
+
+import javax.validation.constraints.NotNull;
+
+public class ValidationParameter {
+    @NotNull
+    private String parameter;
+
+    public ValidationParameter() {
+    }
+
+    public ValidationParameter(String parameter) {
+        this.parameter = parameter;
+    }
+}
diff --git a/dubbo-spi-filter/pom.xml b/dubbo-spi-filter/pom.xml
new file mode 100644
index 0000000..248be53
--- /dev/null
+++ b/dubbo-spi-filter/pom.xml
@@ -0,0 +1,35 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo.spi</groupId>
+        <artifactId>dubbo-spi-extensions</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <artifactId>dubbo-spi-filter</artifactId>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <description>The filter module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+    <modules>
+        <module>dubbo-filter-cache</module>
+        <module>dubbo-filter-validation</module>
+    </modules>
+</project>
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-consul/pom.xml b/dubbo-spi-metadata/dubbo-metadata-report-consul/pom.xml
new file mode 100644
index 0000000..7a76f17
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-consul/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-metadata</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-metadata-report-consul</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ecwid.consul</groupId>
+            <artifactId>consul-api</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java
new file mode 100644
index 0000000..c1e0a30
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReport.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.consul;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
+import org.apache.dubbo.rpc.RpcException;
+
+import com.ecwid.consul.v1.ConsulClient;
+import com.ecwid.consul.v1.Response;
+import com.ecwid.consul.v1.kv.model.GetValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * metadata report impl for consul
+ */
+public class ConsulMetadataReport extends AbstractMetadataReport {
+    private static final Logger logger = LoggerFactory.getLogger(ConsulMetadataReport.class);
+    private static final int DEFAULT_PORT = 8500;
+
+    private ConsulClient client;
+
+    public ConsulMetadataReport(URL url) {
+        super(url);
+
+        String host = url.getHost();
+        int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
+        client = new ConsulClient(host, port);
+    }
+
+    @Override
+    protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
+        this.storeMetadata(providerMetadataIdentifier, serviceDefinitions);
+    }
+
+    @Override
+    protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
+        this.storeMetadata(consumerMetadataIdentifier, value);
+    }
+
+    @Override
+    protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
+        this.storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()));
+    }
+
+    @Override
+    protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
+        this.deleteMetadata(serviceMetadataIdentifier);
+    }
+
+    @Override
+    protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
+        //todo encode and decode
+        String content = getMetadata(metadataIdentifier);
+        if (StringUtils.isEmpty(content)) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<String>(Arrays.asList(URL.decode(content)));
+    }
+
+    @Override
+    protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
+        this.storeMetadata(subscriberMetadataIdentifier, urlListStr);
+    }
+
+    @Override
+    protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
+        return getMetadata(subscriberMetadataIdentifier);
+    }
+
+    private void storeMetadata(BaseMetadataIdentifier identifier, String v) {
+        try {
+            client.setKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v);
+        } catch (Throwable t) {
+            logger.error("Failed to put " + identifier + " to consul " + v + ", cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to put " + identifier + " to consul " + v + ", cause: " + t.getMessage(), t);
+        }
+    }
+
+    private void deleteMetadata(BaseMetadataIdentifier identifier) {
+        try {
+            client.deleteKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
+        } catch (Throwable t) {
+            logger.error("Failed to delete " + identifier + " from consul , cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to delete " + identifier + " from consul , cause: " + t.getMessage(), t);
+        }
+    }
+
+    private String getMetadata(BaseMetadataIdentifier identifier) {
+        try {
+            Response<GetValue> value = client.getKVValue(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
+            //FIXME CHECK
+            if (value != null && value.getValue() != null) {
+                //todo check decode value and value diff
+                return value.getValue().getValue();
+            }
+            return null;
+        } catch (Throwable t) {
+            logger.error("Failed to get " + identifier + " from consul , cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to get " + identifier + " from consul , cause: " + t.getMessage(), t);
+        }
+    }
+
+    @Override
+    public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
+        return getMetadata(metadataIdentifier);
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReportFactory.java b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReportFactory.java
new file mode 100644
index 0000000..1d1f5bb
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/java/org/apache/dubbo/metadata/store/consul/ConsulMetadataReportFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.consul;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
+
+/**
+ * metadata report factory impl for consul
+ */
+public class ConsulMetadataReportFactory extends AbstractMetadataReportFactory {
+    @Override
+    protected MetadataReport createMetadataReport(URL url) {
+        return new ConsulMetadataReport(url);
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
new file mode 100644
index 0000000..1f27535
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
@@ -0,0 +1 @@
+consul=org.apache.dubbo.metadata.store.consul.ConsulMetadataReportFactory
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/pom.xml b/dubbo-spi-metadata/dubbo-metadata-report-etcd/pom.xml
new file mode 100644
index 0000000..9199ca5
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-metadata</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-metadata-report-etcd</artifactId>
+
+    <properties>
+        <skipIntegrationTests>true</skipIntegrationTests>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-remoting-etcd3</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.etcd</groupId>
+            <artifactId>jetcd-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipIntegrationTests}</skipTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java
new file mode 100644
index 0000000..a80c6e8
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReport.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.store.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
+import org.apache.dubbo.remoting.etcd.jetcd.JEtcdClient;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
+
+/**
+ * Report Metadata to Etcd
+ */
+public class EtcdMetadataReport extends AbstractMetadataReport {
+
+    private final static Logger logger = LoggerFactory.getLogger(EtcdMetadataReport.class);
+
+    private final String root;
+
+    /**
+     * The etcd client
+     */
+    private final JEtcdClient etcdClient;
+
+    public EtcdMetadataReport(URL url) {
+        super(url);
+        if (url.isAnyHost()) {
+            throw new IllegalStateException("registry address == null");
+        }
+        String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
+        if (!group.startsWith(PATH_SEPARATOR)) {
+            group = PATH_SEPARATOR + group;
+        }
+        this.root = group;
+        etcdClient = new JEtcdClient(url);
+    }
+
+    @Override
+    protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
+        storeMetadata(providerMetadataIdentifier, serviceDefinitions);
+    }
+
+    @Override
+    protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
+        storeMetadata(consumerMetadataIdentifier, value);
+    }
+
+    @Override
+    protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
+        String key = getNodeKey(serviceMetadataIdentifier);
+        if (!etcdClient.put(key, URL.encode(url.toFullString()))) {
+            logger.error("Failed to put " + serviceMetadataIdentifier + " to etcd, value: " + url);
+        }
+    }
+
+    @Override
+    protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
+        etcdClient.delete(getNodeKey(serviceMetadataIdentifier));
+    }
+
+    @Override
+    protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
+        String content = etcdClient.getKVValue(getNodeKey(metadataIdentifier));
+        if (StringUtils.isEmpty(content)) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<String>(Arrays.asList(URL.decode(content)));
+    }
+
+    @Override
+    protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
+        String key = getNodeKey(subscriberMetadataIdentifier);
+        if (!etcdClient.put(key, urlListStr)) {
+            logger.error("Failed to put " + subscriberMetadataIdentifier + " to etcd, value: " + urlListStr);
+        }
+    }
+
+    @Override
+    protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
+        return etcdClient.getKVValue(getNodeKey(subscriberMetadataIdentifier));
+    }
+
+    @Override
+    public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
+        return etcdClient.getKVValue(getNodeKey(metadataIdentifier));
+    }
+
+    private void storeMetadata(MetadataIdentifier identifier, String v) {
+        String key = getNodeKey(identifier);
+        if (!etcdClient.put(key, v)) {
+            logger.error("Failed to put " + identifier + " to etcd, value: " + v);
+        }
+    }
+
+    String getNodeKey(BaseMetadataIdentifier identifier) {
+        return toRootDir() + identifier.getUniqueKey(KeyTypeEnum.PATH);
+    }
+
+    String toRootDir() {
+        if (root.equals(PATH_SEPARATOR)) {
+            return root;
+        }
+        return root + PATH_SEPARATOR;
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportFactory.java b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportFactory.java
new file mode 100644
index 0000000..3bb9e92
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.store.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
+
+/**
+ * MetadataReportFactory to create an Etcd based {@link MetadataReport}.
+ */
+public class EtcdMetadataReportFactory extends AbstractMetadataReportFactory {
+
+    @Override
+    public MetadataReport createMetadataReport(URL url) {
+        return new EtcdMetadataReport(url);
+    }
+
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
new file mode 100644
index 0000000..9a3c98c
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
@@ -0,0 +1 @@
+etcd=org.apache.dubbo.metadata.store.etcd.EtcdMetadataReportFactory
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadata4TstService.java b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadata4TstService.java
new file mode 100644
index 0000000..1de21ce
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadata4TstService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.etcd;
+
+/**
+ * Test interface for Etcd metadata report
+ */
+public interface EtcdMetadata4TstService {
+
+    int getCounter();
+
+    void printResult(String var);
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java
new file mode 100644
index 0000000..2d2efd5
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.etcd;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+
+import com.google.gson.Gson;
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.kv.GetResponse;
+import io.etcd.jetcd.launcher.EtcdCluster;
+import io.etcd.jetcd.launcher.EtcdClusterFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
+
+/**
+ * Unit test for etcd metadata report
+ */
+public class EtcdMetadataReportTest {
+
+    private static final String TEST_SERVICE = "org.apache.dubbo.metadata.store.etcd.EtcdMetadata4TstService";
+
+    private EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 1, false, false);
+    private Client etcdClientForTest;
+    private EtcdMetadataReport etcdMetadataReport;
+    private URL registryUrl;
+    private EtcdMetadataReportFactory etcdMetadataReportFactory;
+
+    @BeforeEach
+    public void setUp() {
+        etcdCluster.start();
+        etcdClientForTest = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
+        List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
+        this.registryUrl = URL.valueOf("etcd://" + clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort());
+        etcdMetadataReportFactory = new EtcdMetadataReportFactory();
+        this.etcdMetadataReport = (EtcdMetadataReport) etcdMetadataReportFactory.createMetadataReport(registryUrl);
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception {
+        etcdCluster.close();
+    }
+
+    @Test
+    public void testStoreProvider() throws Exception {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etcd-metdata-report-test";
+
+        String r = etcdMetadataReport.getServiceDefinition(new MetadataIdentifier(TEST_SERVICE, version, group, "provider", application));
+        Assertions.assertNull(r);
+        MetadataIdentifier providerIdentifier =
+                storeProvider(etcdMetadataReport, TEST_SERVICE, version, group, application);
+
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(providerIdentifier), StandardCharsets.UTF_8));
+        String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
+        Assertions.assertNotNull(fileContent);
+
+        Gson gson = new Gson();
+        FullServiceDefinition fullServiceDefinition = gson.fromJson(fileContent, FullServiceDefinition.class);
+        Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "etcdTest");
+
+        r = etcdMetadataReport.getServiceDefinition(new MetadataIdentifier(TEST_SERVICE, version, group, "provider", application));
+        Assertions.assertNotNull(r);
+    }
+
+    @Test
+    public void testStoreConsumer() throws Exception {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        MetadataIdentifier consumerIdentifier = storeConsumer(etcdMetadataReport, TEST_SERVICE, version, group, application);
+
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(consumerIdentifier), StandardCharsets.UTF_8));
+        String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
+        Assertions.assertNotNull(fileContent);
+        Assertions.assertEquals(fileContent, "{\"paramConsumerTest\":\"etcdConsumer\"}");
+    }
+
+    @Test
+    public void testDoSaveMetadata() throws ExecutionException, InterruptedException {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        String revision = "90980";
+        String protocol = "xxx";
+        URL url = generateURL(TEST_SERVICE, version, group, application);
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
+                group, "provider", revision, protocol);
+        etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
+        String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
+        Assertions.assertNotNull(fileContent);
+
+        Assertions.assertEquals(fileContent, URL.encode(url.toFullString()));
+    }
+
+    @Test
+    public void testDoRemoveMetadata() throws ExecutionException, InterruptedException {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        String revision = "90980";
+        String protocol = "xxx";
+        URL url = generateURL(TEST_SERVICE, version, group, application);
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
+                group, "provider", revision, protocol);
+        etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
+        String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
+        Assertions.assertNotNull(fileContent);
+
+
+        etcdMetadataReport.doRemoveMetadata(serviceMetadataIdentifier);
+
+        response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(serviceMetadataIdentifier), StandardCharsets.UTF_8));
+        Assertions.assertTrue(response.get().getKvs().isEmpty());
+    }
+
+    @Test
+    public void testDoGetExportedURLs() throws ExecutionException, InterruptedException {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        String revision = "90980";
+        String protocol = "xxx";
+        URL url = generateURL(TEST_SERVICE, version, group, application);
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, version,
+                group, "provider", revision, protocol);
+        etcdMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+
+        List<String> r = etcdMetadataReport.doGetExportedURLs(serviceMetadataIdentifier);
+        Assertions.assertTrue(r.size() == 1);
+
+        String fileContent = r.get(0);
+        Assertions.assertNotNull(fileContent);
+
+        Assertions.assertEquals(fileContent, url.toFullString());
+    }
+
+    @Test
+    public void testDoSaveSubscriberData() throws ExecutionException, InterruptedException {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        String revision = "90980";
+        String protocol = "xxx";
+        URL url = generateURL(TEST_SERVICE, version, group, application);
+        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision);
+        Gson gson = new Gson();
+        String r = gson.toJson(Arrays.asList(url));
+        etcdMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r);
+
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(subscriberMetadataIdentifier), StandardCharsets.UTF_8));
+        String fileContent = response.get().getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
+        Assertions.assertNotNull(fileContent);
+
+        Assertions.assertEquals(fileContent, r);
+    }
+
+    @Test
+    public void testDoGetSubscribedURLs() throws ExecutionException, InterruptedException {
+        String version = "1.0.0";
+        String group = null;
+        String application = "etc-metadata-report-consumer-test";
+        String revision = "90980";
+        String protocol = "xxx";
+        URL url = generateURL(TEST_SERVICE, version, group, application);
+        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(application, revision);
+        Gson gson = new Gson();
+        String r = gson.toJson(Arrays.asList(url));
+        etcdMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, r);
+
+        CompletableFuture<GetResponse> response = etcdClientForTest.getKVClient().get(ByteSequence.from(
+                etcdMetadataReport.getNodeKey(subscriberMetadataIdentifier), StandardCharsets.UTF_8));
+        String fileContent = etcdMetadataReport.doGetSubscribedURLs(subscriberMetadataIdentifier);
+        Assertions.assertNotNull(fileContent);
+
+        Assertions.assertEquals(fileContent, r);
+    }
+
+    private MetadataIdentifier storeProvider(EtcdMetadataReport etcdMetadataReport, String interfaceName, String version,
+                                             String group, String application)
+            throws ClassNotFoundException, InterruptedException {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName +
+                "?paramTest=etcdTest&version=" + version + "&application="
+                + application + (group == null ? "" : "&group=" + group));
+
+        MetadataIdentifier providerMetadataIdentifier =
+                new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application);
+        Class interfaceClass = Class.forName(interfaceName);
+        FullServiceDefinition fullServiceDefinition =
+                ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters());
+
+        etcdMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition);
+        Thread.sleep(1000);
+        return providerMetadataIdentifier;
+    }
+
+    private URL generateURL(String interfaceName, String version, String group, String application) {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":8989/" + interfaceName +
+                "?paramTest=etcdTest&version=" + version + "&application="
+                + application + (group == null ? "" : "&group=" + group));
+        return url;
+    }
+
+    private MetadataIdentifier storeConsumer(EtcdMetadataReport etcdMetadataReport, String interfaceName,
+                                             String version, String group, String application) throws InterruptedException {
+
+        MetadataIdentifier consumerIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application);
+        Map<String, String> tmp = new HashMap<>();
+        tmp.put("paramConsumerTest", "etcdConsumer");
+        etcdMetadataReport.storeConsumerMetadata(consumerIdentifier, tmp);
+        Thread.sleep(1000);
+        return consumerIdentifier;
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/pom.xml b/dubbo-spi-metadata/dubbo-metadata-report-nacos/pom.xml
new file mode 100644
index 0000000..7885a91
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-metadata</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-metadata-report-nacos</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java
new file mode 100644
index 0000000..707d2b8
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReport.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
+import org.apache.dubbo.rpc.RpcException;
+
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.exception.NacosException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+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.CONFIG_LONG_POLL_TIMEOUT;
+import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_RETRY_TIME;
+import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT;
+import static com.alibaba.nacos.api.PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING;
+import static com.alibaba.nacos.api.PropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE;
+import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_LOAD_CACHE_AT_START;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_POLLING_THREAD_COUNT;
+import static com.alibaba.nacos.api.PropertyKeyConst.RAM_ROLE_NAME;
+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.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
+
+/**
+ * metadata report impl for nacos
+ */
+public class NacosMetadataReport extends AbstractMetadataReport {
+
+    private static final Logger logger = LoggerFactory.getLogger(NacosMetadataReport.class);
+
+    private ConfigService configService;
+
+    /**
+     * The group used to store metadata in Nacos
+     */
+    private String group;
+
+
+    public NacosMetadataReport(URL url) {
+        super(url);
+        this.configService = buildConfigService(url);
+        group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
+    }
+
+    public ConfigService buildConfigService(URL url) {
+        Properties nacosProperties = buildNacosProperties(url);
+        try {
+            configService = NacosFactory.createConfigService(nacosProperties);
+        } catch (NacosException e) {
+            if (logger.isErrorEnabled()) {
+                logger.error(e.getErrMsg(), e);
+            }
+            throw new IllegalStateException(e);
+        }
+        return configService;
+    }
+
+    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 static void setProperties(URL url, Properties properties) {
+        putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
+        putPropertyIfAbsent(url, properties, IS_USE_CLOUD_NAMESPACE_PARSING);
+        putPropertyIfAbsent(url, properties, IS_USE_ENDPOINT_PARSING_RULE);
+        putPropertyIfAbsent(url, properties, ENDPOINT);
+        putPropertyIfAbsent(url, properties, ENDPOINT_PORT);
+        putPropertyIfAbsent(url, properties, NAMESPACE);
+        putPropertyIfAbsent(url, properties, ACCESS_KEY);
+        putPropertyIfAbsent(url, properties, SECRET_KEY);
+        putPropertyIfAbsent(url, properties, RAM_ROLE_NAME);
+        putPropertyIfAbsent(url, properties, CONTEXT_PATH);
+        putPropertyIfAbsent(url, properties, CLUSTER_NAME);
+        putPropertyIfAbsent(url, properties, ENCODE);
+        putPropertyIfAbsent(url, properties, CONFIG_LONG_POLL_TIMEOUT);
+        putPropertyIfAbsent(url, properties, CONFIG_RETRY_TIME);
+        putPropertyIfAbsent(url, properties, MAX_RETRY);
+        putPropertyIfAbsent(url, properties, ENABLE_REMOTE_SYNC_CONFIG);
+        putPropertyIfAbsent(url, properties, NAMING_LOAD_CACHE_AT_START, "true");
+        putPropertyIfAbsent(url, properties, NAMING_CLIENT_BEAT_THREAD_COUNT);
+        putPropertyIfAbsent(url, properties, NAMING_POLLING_THREAD_COUNT);
+    }
+
+    private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
+        String propertyValue = url.getParameter(propertyName);
+        if (StringUtils.isNotEmpty(propertyValue)) {
+            properties.setProperty(propertyName, propertyValue);
+        }
+    }
+
+    private static void putPropertyIfAbsent(URL url, Properties properties, String propertyName, String defaultValue) {
+        String propertyValue = url.getParameter(propertyName);
+        if (StringUtils.isNotEmpty(propertyValue)) {
+            properties.setProperty(propertyName, propertyValue);
+        } else {
+            properties.setProperty(propertyName, defaultValue);
+        }
+    }
+
+    @Override
+    protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
+        this.storeMetadata(providerMetadataIdentifier, serviceDefinitions);
+    }
+
+    @Override
+    protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
+        this.storeMetadata(consumerMetadataIdentifier, value);
+    }
+
+    @Override
+    protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
+        storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()));
+    }
+
+    @Override
+    protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
+        deleteMetadata(serviceMetadataIdentifier);
+    }
+
+    @Override
+    protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
+        String content = getConfig(metadataIdentifier);
+        if (StringUtils.isEmpty(content)) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<String>(Arrays.asList(URL.decode(content)));
+    }
+
+    @Override
+    protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
+        storeMetadata(subscriberMetadataIdentifier, urlListStr);
+    }
+
+    @Override
+    protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
+        return getConfig(subscriberMetadataIdentifier);
+    }
+
+    @Override
+    public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
+        return getConfig(metadataIdentifier);
+    }
+
+    private void storeMetadata(BaseMetadataIdentifier identifier, String value) {
+        try {
+            boolean publishResult = configService.publishConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, value);
+            if (!publishResult) {
+                throw new RuntimeException("publish nacos metadata failed");
+            }
+        } catch (Throwable t) {
+            logger.error("Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to put " + identifier + " to nacos " + value + ", cause: " + t.getMessage(), t);
+        }
+    }
+
+    private void deleteMetadata(BaseMetadataIdentifier identifier) {
+        try {
+            boolean publishResult = configService.removeConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group);
+            if (!publishResult) {
+                throw new RuntimeException("remove nacos metadata failed");
+            }
+        } catch (Throwable t) {
+            logger.error("Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to remove " + identifier + " from nacos , cause: " + t.getMessage(), t);
+        }
+    }
+
+    private String getConfig(BaseMetadataIdentifier identifier) {
+        try {
+            return configService.getConfig(identifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), group, 300);
+        } catch (Throwable t) {
+            logger.error("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t);
+            throw new RpcException("Failed to get " + identifier + " from nacos , cause: " + t.getMessage(), t);
+        }
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportFactory.java b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportFactory.java
new file mode 100644
index 0000000..2cff74c
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
+
+/**
+ * metadata report factory impl for nacos
+ */
+public class NacosMetadataReportFactory extends AbstractMetadataReportFactory {
+    @Override
+    protected MetadataReport createMetadataReport(URL url) {
+        return new NacosMetadataReport(url);
+    }
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
new file mode 100644
index 0000000..de3b50a
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
@@ -0,0 +1 @@
+nacos=org.apache.dubbo.metadata.store.nacos.NacosMetadataReportFactory
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java
new file mode 100644
index 0000000..e84efc5
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.metadata.store.nacos;
+
+/**
+ * Test interface for Nacos metadata report
+ */
+public interface NacosMetadata4TstService {
+
+    int getCounter();
+
+    void printResult(String var);
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportTest.java b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportTest.java
new file mode 100644
index 0000000..88fc75a
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadataReportTest.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.store.nacos;
+
+import com.alibaba.nacos.api.exception.NacosException;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+
+import com.alibaba.nacos.api.config.ConfigService;
+import com.google.gson.Gson;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
+
+//FIXME: waiting for embedded Nacos suport, then we can open the switch.
+@Disabled("https://github.com/alibaba/nacos/issues/1188")
+public class NacosMetadataReportTest {
+
+    private static final String SESSION_TIMEOUT_KEY = "session";
+
+    private static final String TEST_SERVICE = "org.apache.dubbo.metadata.store.nacos.NacosMetadata4TstService";
+
+    private NacosMetadataReport nacosMetadataReport;
+
+    private NacosMetadataReportFactory nacosMetadataReportFactory;
+
+    private ConfigService configService;
+
+    private static final String NACOS_GROUP = "metadata_test";
+
+    /**
+     * timeout(ms) for nacos session
+     */
+    private static final int SESSION_TIMEOUT = 15 * 1000;
+
+    /**
+     * timeout(ms) for query operation on nacos
+     */
+    private static final int NACOS_READ_TIMEOUT = 5 * 1000;
+
+    /**
+     * interval(ms) to make nacos cache refresh
+     */
+    private static final int INTERVAL_TO_MAKE_NACOS_REFRESH = 1000;
+
+    /**
+     * version for test
+     */
+    private static final String VERSION = "1.0.0";
+
+    /**
+     * group for test
+     */
+    private static final String METADATA_GROUP = null;
+
+    /**
+     * application name for test
+     */
+    private static final String APPLICATION_NAME = "nacos-metdata-report-test";
+
+    /**
+     * revision for test
+     */
+    private static final String REVISION = "90980";
+
+    /**
+     * protocol for test
+     */
+    private static final String PROTOCOL = "xxx";
+
+    @BeforeEach
+    public void setUp() {
+        URL url = URL.valueOf("nacos://127.0.0.1:8848?group=" + NACOS_GROUP)
+                .addParameter(SESSION_TIMEOUT_KEY, SESSION_TIMEOUT);
+        nacosMetadataReportFactory = new NacosMetadataReportFactory();
+        this.nacosMetadataReport = (NacosMetadataReport) nacosMetadataReportFactory.createMetadataReport(url);
+        this.configService = nacosMetadataReport.buildConfigService(url);
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception {
+    }
+
+
+    @Test
+    public void testStoreProvider() throws Exception {
+        MetadataIdentifier providerIdentifier =
+                storeProvider(nacosMetadataReport, TEST_SERVICE, VERSION, METADATA_GROUP, APPLICATION_NAME);
+        String serverContent = configService.getConfig(providerIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNotNull(serverContent);
+
+        Gson gson = new Gson();
+        FullServiceDefinition fullServiceDefinition = gson.fromJson(serverContent, FullServiceDefinition.class);
+        Assertions.assertEquals(fullServiceDefinition.getParameters().get("paramTest"), "nacosTest");
+
+        //Clear test data
+        configService.removeConfig(providerIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP);
+    }
+
+    @Test
+    public void testStoreConsumer() throws Exception {
+        MetadataIdentifier consumerIdentifier = storeConsumer(nacosMetadataReport, TEST_SERVICE, VERSION, METADATA_GROUP, APPLICATION_NAME);
+
+        String serverContent = configService.getConfig(consumerIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNotNull(serverContent);
+        Assertions.assertEquals(serverContent, "{\"paramConsumerTest\":\"nacosConsumer\"}");
+
+        //clear test data
+        configService.removeConfig(consumerIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP);
+    }
+
+    @Test
+    public void testDoSaveServiceMetadata() throws Exception {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + TEST_SERVICE +
+                "?paramTest=nacosTest&version=" + VERSION + "&application="
+                + APPLICATION_NAME + (METADATA_GROUP == null ? "" : "&group=" + METADATA_GROUP));
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, VERSION,
+                METADATA_GROUP, "provider", REVISION, PROTOCOL);
+        nacosMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+        String serviceMetaData = configService.getConfig(serviceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNotNull(serviceMetaData);
+        Assertions.assertEquals(serviceMetaData, URL.encode(url.toFullString()));
+
+        //clear test data
+        configService.removeConfig(serviceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP);
+    }
+
+    @Test
+    public void testDoRemoveServiceMetadata() throws Exception {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + TEST_SERVICE +
+                "?paramTest=nacosTest&version=" + VERSION + "&application="
+                + APPLICATION_NAME + (METADATA_GROUP == null ? "" : "&group=" + METADATA_GROUP));
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, VERSION,
+                METADATA_GROUP, "provider", REVISION, PROTOCOL);
+        nacosMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+        String serviceMetaData = configService.getConfig(serviceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNotNull(serviceMetaData);
+
+        nacosMetadataReport.doRemoveMetadata(serviceMetadataIdentifier);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+        serviceMetaData = configService.getConfig(serviceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNull(serviceMetaData);
+    }
+
+    @Test
+    public void testDoGetExportedURLs() throws InterruptedException, NacosException {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + TEST_SERVICE +
+                "?paramTest=nacosTest&version=" + VERSION + "&application="
+                + APPLICATION_NAME + (METADATA_GROUP == null ? "" : "&group=" + METADATA_GROUP));
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(TEST_SERVICE, VERSION,
+                METADATA_GROUP, "provider", REVISION, PROTOCOL);
+
+        nacosMetadataReport.doSaveMetadata(serviceMetadataIdentifier, url);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+
+        List<String> exportedURLs = nacosMetadataReport.doGetExportedURLs(serviceMetadataIdentifier);
+        Assertions.assertTrue(exportedURLs.size() == 1);
+
+        String exportedUrl = exportedURLs.get(0);
+        Assertions.assertNotNull(exportedUrl);
+        Assertions.assertEquals(exportedUrl, url.toFullString());
+
+        //clear test data
+        configService.removeConfig(serviceMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP);
+    }
+
+    @Test
+    public void testDoSaveSubscriberData() throws InterruptedException, NacosException {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + TEST_SERVICE +
+                "?paramTest=nacosTest&version=" + VERSION + "&application="
+                + APPLICATION_NAME + (METADATA_GROUP == null ? "" : "&group=" + METADATA_GROUP));
+        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier(APPLICATION_NAME, REVISION);
+        Gson gson = new Gson();
+        String urlListJsonString = gson.toJson(Arrays.asList(url));
+        nacosMetadataReport.doSaveSubscriberData(subscriberMetadataIdentifier, urlListJsonString);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+
+        String subscriberMetadata = configService.getConfig(subscriberMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP, NACOS_READ_TIMEOUT);
+        Assertions.assertNotNull(subscriberMetadata);
+        Assertions.assertEquals(subscriberMetadata, urlListJsonString);
+
+        //clear test data
+        configService.removeConfig(subscriberMetadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), NACOS_GROUP);
+
+    }
+
+    private MetadataIdentifier storeProvider(NacosMetadataReport nacosMetadataReport, String interfaceName, String version,
+                                             String group, String application)
+            throws ClassNotFoundException, InterruptedException {
+        URL url = URL.valueOf("xxx://" + NetUtils.getLocalAddress().getHostName() + ":4444/" + interfaceName +
+                "?paramTest=nacosTest&version=" + version + "&application="
+                + application + (group == null ? "" : "&group=" + group));
+
+        MetadataIdentifier providerMetadataIdentifier =
+                new MetadataIdentifier(interfaceName, version, group, PROVIDER_SIDE, application);
+        Class interfaceClass = Class.forName(interfaceName);
+        FullServiceDefinition fullServiceDefinition =
+                ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, url.getParameters());
+
+        nacosMetadataReport.storeProviderMetadata(providerMetadataIdentifier, fullServiceDefinition);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+        return providerMetadataIdentifier;
+    }
+
+    private MetadataIdentifier storeConsumer(NacosMetadataReport nacosMetadataReport, String interfaceName,
+                                             String version, String group, String application) throws InterruptedException {
+        MetadataIdentifier consumerIdentifier = new MetadataIdentifier(interfaceName, version, group, CONSUMER_SIDE, application);
+        Map<String, String> tmp = new HashMap<>();
+        tmp.put("paramConsumerTest", "nacosConsumer");
+        nacosMetadataReport.storeConsumerMetadata(consumerIdentifier, tmp);
+        Thread.sleep(INTERVAL_TO_MAKE_NACOS_REFRESH);
+        return consumerIdentifier;
+    }
+
+}
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-redis/pom.xml b/dubbo-spi-metadata/dubbo-metadata-report-redis/pom.xml
new file mode 100644
index 0000000..70bd32d
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-redis/pom.xml
@@ -0,0 +1,57 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<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-metadata</artifactId>
+        <version>2.7.7-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-metadata-report-redis</artifactId>
+    <properties>
+        <jedis.version>2.9.0</jedis.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>${jedis.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.kstyrc</groupId>
+            <artifactId>embedded-redis</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-spi-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java b/dubbo-spi-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
new file mode 100644
index 0000000..c00be04
--- /dev/null
+++ b/dubbo-spi-metadata/dubbo-metadata-report-redis/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
... 70896 lines suppressed ...