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/07 09:36:12 UTC

[dubbo] branch 3.0-k8s updated: Migrate 3.0 branch to 3.0-k8s branch (#7076)

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

liujun pushed a commit to branch 3.0-k8s
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0-k8s by this push:
     new 79bb595  Migrate 3.0 branch to 3.0-k8s branch (#7076)
79bb595 is described below

commit 79bb595c8194d05f7ab7aa7bdd32a62e437510fe
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Thu Jan 7 17:35:52 2021 +0800

    Migrate 3.0 branch to 3.0-k8s branch (#7076)
---
 .../apache/dubbo/rpc/cluster/ClusterInvoker.java   |   17 +
 .../org/apache/dubbo/rpc/cluster/Constants.java    |    2 +
 .../org/apache/dubbo/rpc/cluster/Directory.java    |   12 +
 .../org/apache/dubbo/rpc/cluster/RouterChain.java  |    4 +
 .../cluster/configurator/AbstractConfigurator.java |   13 +-
 .../rpc/cluster/directory/AbstractDirectory.java   |   44 +-
 .../cluster/filter/DefaultFilterChainBuilder.java  |   62 +
 .../rpc/cluster/filter/FilterChainBuilder.java     |  148 +
 .../filter/InvocationInterceptorBuilder.java       |   15 +-
 .../rpc/cluster/filter/ProtocolFilterWrapper.java  |  171 +-
 .../cluster/interceptor/ClusterInterceptor.java    |    4 +-
 .../ConsumerContextClusterInterceptor.java         |    6 +
 .../cluster/support/AbstractClusterInvoker.java    |   50 +-
 .../cluster/support/AvailableClusterInvoker.java   |    2 +-
 .../cluster/support/BroadcastClusterInvoker.java   |    2 +-
 .../dubbo/rpc/cluster/support/ClusterUtils.java    |   19 +
 .../cluster/support/FailbackClusterInvoker.java    |    4 +-
 .../cluster/support/FailfastClusterInvoker.java    |    2 +-
 .../cluster/support/FailoverClusterInvoker.java    |    2 +-
 .../cluster/support/FailsafeClusterInvoker.java    |    2 +-
 .../rpc/cluster/support/ForkingClusterInvoker.java |    2 +-
 .../cluster/support/MergeableClusterInvoker.java   |    9 +-
 .../cluster/support/ProviderURLMergeProcessor.java |   15 +-
 .../cluster/support/registry/ZoneAwareCluster.java |    3 -
 .../cluster/support/wrapper/AbstractCluster.java   |  125 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 +
 ...che.dubbo.rpc.cluster.filter.FilterChainBuilder |    1 +
 .../configurator/parser/ConfigParserTest.java      |   17 +-
 .../support/AbstractClusterInvokerTest.java        |   30 +-
 .../rpc/cluster/support/ClusterUtilsTest.java      |   17 +-
 .../src/main/java/org/apache/dubbo/common/URL.java |  794 ++-
 .../java/org/apache/dubbo/common/URLBuilder.java   |   37 +-
 .../java/org/apache/dubbo/common/URLStrParser.java |  103 +-
 .../dubbo/common/bytecode/ClassGenerator.java      |   12 +-
 .../common/bytecode/CustomizedLoaderClassPath.java |  104 +
 .../common/config/CompositeConfiguration.java      |   12 +
 .../dubbo/common/config/ConfigurationUtils.java    |   12 +
 .../apache/dubbo/common/config/Environment.java    |   24 +-
 .../dubbo/common/constants/CommonConstants.java    |   13 +
 .../dubbo/common/constants/RegistryConstants.java  |   12 +-
 .../apache/dubbo/common/extension/Activate.java    |    2 +
 .../dubbo/common/extension/ExtensionLoader.java    |   28 +-
 .../manager/DefaultExecutorRepository.java         |   28 +-
 .../threadpool/manager/ExecutorRepository.java     |   12 +
 .../url/component/DubboServiceAddressURL.java      |  150 +
 .../dubbo/common/url/component/PathURLAddress.java |  175 +
 .../common/url/component/ServiceAddressURL.java    |  218 +
 .../common/url/component/ServiceConfigURL.java     |  134 +
 .../dubbo/common/url/component/URLAddress.java     |  260 +
 .../dubbo/common/url/component/URLItemCache.java   |   67 +
 .../dubbo/common/url/component/URLParam.java       |  286 ++
 .../org/apache/dubbo/common/utils/UrlUtils.java    |  135 +-
 .../org/apache/dubbo/config/AbstractConfig.java    |    6 +
 .../dubbo/config/AbstractInterfaceConfig.java      |    3 +
 .../org/apache/dubbo/config/ApplicationConfig.java |   40 +
 .../org/apache/dubbo/config/RegistryConfig.java    |   24 +
 .../apache/dubbo/config/context/ConfigManager.java |   31 +-
 .../apache/dubbo/rpc/model/ApplicationModel.java   |    5 +
 .../apache/dubbo/rpc/support/ProtocolUtils.java    |    6 +-
 .../dubbo/common/InterfaceAddressURLTest.java      |  107 +
 .../org/apache/dubbo/common/URLBuilderTest.java    |    6 +-
 .../test/java/org/apache/dubbo/common/URLTest.java |   56 +-
 .../support/AbortPolicyWithReportTest.java         |    4 +-
 .../apache/dubbo/common/utils/UrlUtilsTest.java    |    2 +-
 dubbo-compatible/pom.xml                           |   13 +-
 .../rpc/protocol/thrift/ClassNameGenerator.java    |   22 -
 dubbo-config/dubbo-config-api/pom.xml              |   83 +-
 .../org/apache/dubbo/config/ReferenceConfig.java   |   11 +-
 .../org/apache/dubbo/config/ServiceConfig.java     |   20 +-
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   97 +-
 .../ConfigurableMetadataServiceExporter.java       |   13 +-
 ...java => ServiceInstanceHostPortCustomizer.java} |   49 +-
 .../dubbo/config/utils/ConfigValidationUtils.java  |   49 +-
 ...dubbo.registry.client.ServiceInstanceCustomizer |    2 +-
 .../org/apache/dubbo/config/ServiceConfigTest.java |    4 +-
 .../config/consumer/DemoActionByAnnotation.java    |   35 -
 .../dubbo/config/consumer/DemoActionBySetter.java  |   36 -
 .../dubbo/config/consumer/DemoInterceptor.java     |   31 -
 dubbo-config/dubbo-config-spring/pom.xml           |   12 -
 .../src/main/resources/META-INF/compat/dubbo.xsd   |   11 +-
 .../src/main/resources/META-INF/dubbo.xsd          |   20 +
 .../dubbo-configcenter-apollo/pom.xml              |    3 +-
 .../support/apollo/ApolloDynamicConfiguration.java |    8 +-
 .../dubbo-configcenter-consul/pom.xml              |   47 -
 .../consul/ConsulDynamicConfiguration.java         |  181 -
 .../consul/ConsulDynamicConfigurationFactory.java  |   32 -
 ...config.configcenter.DynamicConfigurationFactory |    1 -
 .../consul/ConsulDynamicConfigurationTest.java     |  123 -
 dubbo-configcenter/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               |    1 -
 .../zookeeper/ZookeeperDynamicConfiguration.java   |    2 +-
 dubbo-configcenter/pom.xml                         |    2 -
 dubbo-container/dubbo-container-log4j/pom.xml      |   39 -
 .../dubbo/container/log4j/Log4jContainer.java      |  103 -
 .../internal/org.apache.dubbo.container.Container  |    1 -
 .../dubbo/container/log4j/Log4jContainerTest.java  |   36 -
 dubbo-container/dubbo-container-logback/pom.xml    |   43 -
 .../dubbo/container/logback/LogbackContainer.java  |  108 -
 .../internal/org.apache.dubbo.container.Container  |    1 -
 dubbo-container/pom.xml                            |    2 -
 .../dubbo-demo-annotation-consumer/pom.xml         |    4 -
 .../dubbo-demo-annotation-provider/pom.xml         |    4 -
 .../pom.xml                                        |   25 +-
 .../dubbo/demo/consumer/GenericApplication.java    |   85 +
 .../src/main}/resources/log4j.properties           |    0
 dubbo-demo/dubbo-demo-interface/pom.xml            |    7 +
 .../org/apache/dubbo/demo/RestDemoService.java     |   82 +-
 .../dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml |    4 -
 .../apache/dubbo/demo/consumer/Application.java    |   21 +-
 .../src/main/resources/spring/dubbo-consumer.xml   |    5 +-
 .../pom.xml                                        |    2 +-
 .../demo/provider/chain/ChainApplication.java      |   10 +-
 .../demo/provider/chain/ChainServiceImpl.java      |   65 +-
 .../src/main/resources/dubbo.properties            |    1 +
 .../src/main}/resources/log4j.properties           |    0
 .../main/resources/spring/dubbo-provider-chain.xml |   15 +-
 .../dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml |    6 +-
 .../dubbo/demo/provider/RestDemoServiceImpl.java   |   34 +-
 .../src/main/resources/spring/dubbo-provider.xml   |   10 +-
 dubbo-demo/pom.xml                                 |    1 +
 dubbo-dependencies-bom/pom.xml                     |    1 -
 .../dubbo-all}/pom.xml                             |  337 +-
 .../{ => dubbo-apache-release}/pom.xml             |   11 +-
 .../src/assembly/bin-release.xml                   |    4 +-
 .../src/assembly/source-release.xml                |    2 +-
 .../dubbo-bom}/pom.xml                             |  223 +-
 dubbo-distribution/dubbo-core-spi/pom.xml          |  444 ++
 dubbo-distribution/pom.xml                         |   95 +-
 dubbo-filter/dubbo-filter-cache/pom.xml            |    1 -
 .../main/java/org/apache/dubbo/cache/Cache.java    |   86 +-
 .../java/org/apache/dubbo/cache/CacheFactory.java  |   86 +-
 .../org/apache/dubbo/cache/filter/CacheFilter.java |  266 +-
 .../dubbo/cache/support/AbstractCacheFactory.java  |  144 +-
 .../apache/dubbo/cache/support/jcache/JCache.java  |  174 +-
 .../dubbo/cache/support/jcache/JCacheFactory.java  |   96 +-
 .../apache/dubbo/cache/support/lru/LruCache.java   |  160 +-
 .../dubbo/cache/support/lru/LruCacheFactory.java   |   86 +-
 .../support/threadlocal/ThreadLocalCache.java      |  154 +-
 .../threadlocal/ThreadLocalCacheFactory.java       |   86 +-
 .../internal/org.apache.dubbo.cache.CacheFactory   |    6 +-
 dubbo-filter/dubbo-filter-validation/pom.xml       |    1 -
 .../org/apache/dubbo/validation/Validation.java    |   78 +-
 .../org/apache/dubbo/validation/Validator.java     |   54 +-
 .../dubbo/validation/filter/ValidationFilter.java  |  208 +-
 .../validation/support/AbstractValidation.java     |  102 +-
 .../support/jvalidation/JValidation.java           |   78 +-
 .../validation/support/jvalidation/JValidator.java |  660 +--
 .../DynamicConfigurationServiceNameMapping.java    |   10 +-
 .../apache/dubbo/metadata/MappingChangedEvent.java |    8 -
 .../apache/dubbo/metadata/MetadataConstants.java   |    2 +-
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   61 +-
 .../org/apache/dubbo/metadata/MetadataService.java |    5 +
 .../apache/dubbo/metadata/RevisionResolver.java    |    4 +
 .../apache/dubbo/metadata/ServiceNameMapping.java  |   15 +
 .../dubbo/metadata/WritableMetadataService.java    |   15 +
 .../report/identifier/MetadataIdentifier.java      |   13 +-
 .../identifier/ServiceMetadataIdentifier.java      |    9 +-
 .../identifier/SubscriberMetadataIdentifier.java   |    3 +-
 .../report/support/AbstractMetadataReport.java     |   11 +-
 .../AbstractAbstractWritableMetadataService.java   |    6 +-
 .../metadata/test/JTestMetadataReport4Test.java    |    4 +-
 .../dubbo-metadata-report-consul/pom.xml           |   48 -
 .../store/consul/ConsulMetadataReport.java         |  139 -
 .../store/consul/ConsulMetadataReportFactory.java  |   31 -
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 -
 dubbo-metadata/dubbo-metadata-report-etcd/pom.xml  |   70 -
 .../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/dubbo-metadata-report-nacos/pom.xml |   45 -
 .../metadata/store/nacos/NacosMetadataReport.java  |  149 -
 .../store/nacos/NacosMetadataReportFactory.java    |   31 -
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 -
 dubbo-metadata/dubbo-metadata-report-redis/pom.xml |    1 -
 .../store/zookeeper/ZookeeperMetadataReport.java   |   89 +-
 dubbo-metadata/pom.xml                             |    9 +-
 .../dubbo/monitor/support/MonitorFilter.java       |   10 +-
 .../impl/{Offline.java => BaseOffline.java}        |   37 +-
 .../command/impl/{Online.java => BaseOnline.java}  |   33 +-
 .../org/apache/dubbo/qos/command/impl/Offline.java |   55 +-
 .../apache/dubbo/qos/command/impl/OfflineApp.java  |   45 +-
 .../dubbo/qos/command/impl/OfflineInterface.java   |   24 +-
 .../org/apache/dubbo/qos/command/impl/Online.java  |   61 +-
 .../apache/dubbo/qos/command/impl/OnlineApp.java   |   27 +-
 .../dubbo/qos/command/impl/OnlineInterface.java    |   19 +-
 .../dubbo/qos/command/impl/PublishMetadata.java    |    4 +-
 .../org.apache.dubbo.qos.command.BaseCommand       |    4 +
 .../java/org/apache/dubbo/registry/Constants.java  |    5 -
 .../dubbo/registry/ListenerRegistryWrapper.java    |   13 +-
 .../java/org/apache/dubbo/registry/Registry.java   |    6 +
 .../apache/dubbo/registry/RegistryNotifier.java    |   91 +
 .../dubbo/registry/RegistryServiceListener.java    |    8 +-
 ...Listener.java => AbstractServiceDiscovery.java} |   18 +-
 .../registry/client/DefaultServiceInstance.java    |   69 +
 .../dubbo/registry/client/InstanceAddressURL.java  |   68 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |   14 +
 .../registry/client/ServiceDiscoveryRegistry.java  |  211 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |  139 +-
 .../dubbo/registry/client/ServiceInstance.java     |    3 +
 .../event/RetryServiceInstancesChangedEvent.java   |   19 +-
 .../client/event/ServiceInstancesChangedEvent.java |    7 +
 .../listener/ServiceInstancesChangedListener.java  |  273 +-
 .../metadata/MetadataServiceNameMapping.java       |   10 +-
 ...MetadataServiceURLParamsMetadataCustomizer.java |   14 +-
 .../registry/client/metadata/MetadataUtils.java    |   14 +-
 .../metadata/ProtocolPortsMetadataCustomizer.java  |   11 +-
 .../ServiceInstanceMetadataCustomizer.java         |   16 +-
 .../metadata/ServiceInstanceMetadataUtils.java     |  107 +-
 .../StandardMetadataServiceURLBuilder.java         |   17 +-
 .../store/InMemoryWritableMetadataService.java     |   68 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |   21 +-
 .../DefaultMigrationAddressComparator.java         |   85 +
 .../client/migration/InvokersChangedListener.java} |    9 +-
 .../migration/MigrationAddressComparator.java      |   17 +-
 .../client/migration/MigrationClusterInvoker.java  |  106 +-
 .../client/migration/MigrationInvoker.java         |  418 ++
 .../client/migration/MigrationRuleHandler.java     |  153 +
 .../client/migration/MigrationRuleListener.java    |  227 +
 .../ServiceDiscoveryMigrationInvoker.java          |   61 +
 .../migration/model/ApplicationMigrationRule.java  |   37 +-
 .../migration/model/InterfaceMigrationRule.java    |   44 +-
 .../client/migration/model/MigrationRule.java      |  246 +
 .../client/migration/model/MigrationStep.java      |    9 +-
 .../registry/integration/DynamicDirectory.java     |  113 +-
 .../InterfaceCompatibleRegistryProtocol.java       |  134 +-
 .../registry/integration/RegistryDirectory.java    |  199 +-
 .../{client => integration}/RegistryProtocol.java  |  113 +-
 .../integration/RegistryProtocolListener.java      |    6 +-
 .../dubbo/registry/support/AbstractRegistry.java   |   32 +-
 .../support/CacheableFailbackRegistry.java         |  314 ++
 .../dubbo/registry/support/FailbackRegistry.java   |    5 +-
 ...try.client.migration.MigrationAddressComparator |    1 +
 ...o.registry.integration.RegistryProtocolListener |    2 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    2 +-
 .../registry/CacheableFailbackRegistryTest.java    |   21 +-
 .../registry/DelayedRegistryNotifierTest.java      |    6 +-
 ...stener2.java => MockCacheableRegistryImpl.java} |   45 +-
 .../dubbo/registry/RegistryFactoryWrapperTest.java |   17 +-
 .../dubbo/registry/RegistryServiceListener1.java   |   16 +-
 .../dubbo/registry/RegistryServiceListener2.java   |   16 +-
 .../java/org/apache/dubbo/registry/ZKTools.java    |   43 +-
 .../metadata/ServiceInstanceMetadataUtilsTest.java |    4 +-
 dubbo-registry/dubbo-registry-consul/pom.xml       |   63 -
 .../registry/consul/AbstractConsulRegistry.java    |   39 -
 .../dubbo/registry/consul/ConsulRegistry.java      |  380 --
 .../registry/consul/ConsulServiceDiscovery.java    |  474 --
 .../org.apache.dubbo.registry.RegistryFactory      |    1 -
 .../dubbo/registry/consul/ConsulRegistryTest.java  |  135 -
 .../consul/ConsulServiceDiscoveryTest.java         |  108 -
 dubbo-registry/dubbo-registry-default/pom.xml      |   69 -
 .../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      | 1152 -----
 .../dubbo/registry/dubbo/RegistryProtocolTest.java |  232 -
 .../registry/dubbo/RegistryStatusCheckerTest.java  |   71 -
 .../registry/dubbo/SimpleRegistryExporter.java     |   87 -
 .../registry/dubbo/SimpleRegistryService.java      |  146 -
 .../src/test/resources/log4j.xml                   |   38 -
 .../org/apache/dubbo/registry/dns/DNSRegistry.java |   47 +-
 .../dubbo/registry/dns/DNSRegistryFactory.java}    |   68 +-
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 dubbo-registry/dubbo-registry-etcd3/pom.xml        |   52 -
 .../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-registry/dubbo-registry-eureka/pom.xml       |   86 -
 .../eureka/ConfigurableEurekaInstanceConfig.java   |  369 --
 .../registry/eureka/EurekaServiceDiscovery.java    |  278 --
 .../apache/dubbo/registry/eureka/package-info.java |   22 -
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 -
 .../eureka/EurekaServiceDiscoveryTest.java         |   67 -
 .../registry/kubernetes/KubernetesRegistry.java    |   47 +-
 .../kubernetes/KubernetesRegistryFactory.java}     |   16 +-
 .../org.apache.dubbo.registry.RegistryFactory      |    1 +
 dubbo-registry/dubbo-registry-multiple/pom.xml     |    6 -
 .../multiple/MultipleServiceDiscovery.java         |  177 +
 .../multiple/MultipleServiceDiscoveryFactory.java} |   18 +-
 ...g.apache.dubbo.registry.client.ServiceDiscovery |    1 +
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |    1 +
 .../multiple/MultipleRegistry2S2RTest.java         |   54 +-
 .../multiple/MultipleRegistryTestUtil.java         |   47 +-
 dubbo-registry/dubbo-registry-nacos/pom.xml        |    1 -
 .../apache/dubbo/registry/nacos/NacosRegistry.java |   90 +-
 .../registry/nacos/NacosServiceDiscovery.java      |   22 +-
 .../dubbo/registry/nacos/NacosServiceName.java     |    9 +-
 .../nacos/util/NacosNamingServiceUtils.java        |    2 +-
 dubbo-registry/dubbo-registry-redis/pom.xml        |   53 -
 .../apache/dubbo/registry/redis/RedisRegistry.java |  661 ---
 .../org.apache.dubbo.registry.RegistryFactory      |    1 -
 .../dubbo/registry/redis/RedisRegistryTest.java    |  130 -
 dubbo-registry/dubbo-registry-sofa/pom.xml         |  134 -
 .../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 -
 .../dubbo/registry/sofa/HelloServiceImpl.java      |   44 -
 .../registry/zookeeper/ZookeeperRegistry.java      |  108 +-
 .../zookeeper/ZookeeperServiceDiscovery.java       |   75 +-
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |   28 +-
 .../zookeeper/util/CuratorFrameworkUtils.java      |    2 +-
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |    1 +
 dubbo-registry/pom.xml                             |   13 +-
 .../dubbo/remoting/transport/AbstractServer.java   |    3 +-
 dubbo-remoting/dubbo-remoting-etcd3/pom.xml        |  107 -
 .../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 ---
 .../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-remoting/dubbo-remoting-grizzly/pom.xml      |   43 -
 .../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  |  129 -
 .../transport/grizzly/GrizzlyTransporter.java      |   43 -
 .../internal/org.apache.dubbo.remoting.Transporter |    1 -
 .../transport/grizzly/GrizzlyTransporterTest.java  |   41 -
 dubbo-remoting/dubbo-remoting-http/pom.xml         |    3 +-
 .../org/apache/dubbo/remoting/http/HttpBinder.java |   78 +-
 .../org/apache/dubbo/remoting/http/HttpServer.java |  142 +-
 .../dubbo/remoting/http/jetty/JettyHttpBinder.java |   68 +-
 .../dubbo/remoting/http/jetty/JettyHttpServer.java |  224 +-
 .../remoting/http/servlet/BootstrapListener.java   |    1 -
 .../remoting/http/servlet/DispatcherServlet.java   |  130 +-
 .../remoting/http/servlet/ServletHttpServer.java   |   62 +-
 .../remoting/http/servlet/ServletManager.java      |    1 -
 .../remoting/http/support/AbstractHttpServer.java  |  268 +-
 .../org.apache.dubbo.remoting.http.HttpBinder      |    4 +-
 .../remoting/http/jetty/JettyHttpBinderTest.java   |    1 +
 dubbo-remoting/dubbo-remoting-mina/pom.xml         |   53 -
 .../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 -
 .../transport/mina/MinaClientToServerTest.java     |   41 -
 .../org/apache/remoting/transport/mina/World.java  |   45 -
 .../remoting/transport/mina/WorldHandler.java      |   36 -
 .../remoting/transport/netty4/NettyServer.java     |   20 +-
 dubbo-remoting/dubbo-remoting-p2p/pom.xml          |   45 -
 .../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        |   71 -
 .../dubbo/remoting/zookeeper/ZookeeperClient.java  |    2 +
 .../zookeeper/support/AbstractZookeeperClient.java |    2 +-
 dubbo-remoting/pom.xml                             |    6 +-
 .../main/java/org/apache/dubbo/rpc/RpcContext.java |    9 +-
 .../java/org/apache/dubbo/rpc/RpcException.java    |    1 +
 .../java/org/apache/dubbo/rpc/RpcInvocation.java   |    8 +-
 .../apache/dubbo/rpc/filter/AccessLogFilter.java   |    6 +-
 .../apache/dubbo/rpc/filter/ActiveLimitFilter.java |  244 +-
 .../apache/dubbo/rpc/filter/ClassLoaderFilter.java |   88 +-
 .../apache/dubbo/rpc/filter/CompatibleFilter.java  |  184 +-
 .../dubbo/rpc/filter/ConsumerContextFilter.java    |    8 +-
 .../apache/dubbo/rpc/filter/DeprecatedFilter.java  |  156 +-
 .../org/apache/dubbo/rpc/filter/EchoFilter.java    |   88 +-
 .../apache/dubbo/rpc/filter/ExceptionFilter.java   |  230 +-
 .../dubbo/rpc/filter/ExecuteLimitFilter.java       |  172 +-
 .../apache/dubbo/rpc/filter/GenericImplFilter.java |  458 +-
 .../org/apache/dubbo/rpc/filter/TimeoutFilter.java |  138 +-
 .../org/apache/dubbo/rpc/filter/TokenFilter.java   |  114 +-
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   49 +-
 .../dubbo/rpc/protocol/AbstractProtocol.java       |    8 +-
 .../dubbo/rpc/protocol/AsyncToSyncInvoker.java     |  102 -
 .../dubbo/rpc/protocol/ProtocolFilterWrapper.java  |  176 -
 .../rpc/protocol/ProtocolListenerWrapper.java      |  185 +-
 .../dubbo/rpc/proxy/InvokerInvocationHandler.java  |    4 +-
 .../org/apache/dubbo/rpc/support/RpcUtils.java     |   18 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Filter     |   28 +-
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 -
 .../dubbo/rpc/filter/AccessLogFilterTest.java      |  168 +-
 .../dubbo/rpc/filter/ActiveLimitFilterTest.java    |  452 +-
 .../rpc/filter/CompatibleFilterFilterTest.java     |  346 +-
 .../apache/dubbo/rpc/filter/ContextFilterTest.java |  150 +-
 .../dubbo/rpc/filter/DeprecatedFilterTest.java     |   95 +-
 .../apache/dubbo/rpc/filter/EchoFilterTest.java    |  158 +-
 .../dubbo/rpc/filter/ExceptionFilterTest.java      |  276 +-
 dubbo-rpc/dubbo-rpc-dubbo/pom.xml                  |    6 -
 .../rpc/protocol/dubbo/CallbackServiceCodec.java   |    5 +-
 .../dubbo/rpc/protocol/dubbo/DubboInvoker.java     |    8 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |    9 +-
 .../protocol/dubbo/DubboInvokerAvilableTest.java   |    6 +-
 .../rpc/protocol/dubbo/DubboProtocolTest.java      |   79 +-
 .../dubbo/ReferenceCountExchangeClientTest.java    |    3 +-
 dubbo-rpc/dubbo-rpc-hessian/pom.xml                |   63 -
 .../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 -
 .../rpc/protocol/hessian/HessianServiceImpl.java   |   75 -
 dubbo-rpc/dubbo-rpc-http/pom.xml                   |   62 -
 .../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/dubbo-rpc-memcached/pom.xml              |   43 -
 .../rpc/protocol/memcached/MemcachedProtocol.java  |  122 -
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    1 -
 dubbo-rpc/dubbo-rpc-native-thrift/pom.xml          |   50 -
 .../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-rpc/dubbo-rpc-redis/pom.xml                  |   59 -
 .../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-rpc/dubbo-rpc-rmi/pom.xml                    |   43 -
 .../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/RmiProtocolTest.java    |  227 -
 .../org/apache/dubbo/rpc/protocol/rmi/Type.java    |   21 -
 dubbo-rpc/dubbo-rpc-thrift/pom.xml                 |   74 -
 .../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 -
 .../src/test/resources/dubbo-demo-consumer.xml     |   32 -
 .../src/test/thrift/ClassNameTestDubbo.thrift      |    3 -
 .../src/test/thrift/ClassNameTestThrift.thrift     |    3 -
 .../dubbo-rpc-thrift/src/test/thrift/Demo.thrift   |   16 -
 dubbo-rpc/dubbo-rpc-webservice/pom.xml             |   77 -
 .../protocol/webservice/WebServiceProtocol.java    |  211 -
 .../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         |  167 -
 dubbo-rpc/dubbo-rpc-xml/README.md                  |   88 -
 dubbo-rpc/dubbo-rpc-xml/pom.xml                    |   67 -
 .../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-rpc/pom.xml                                  |    9 -
 .../dubbo-serialization-avro/pom.xml               |   45 -
 .../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           |   44 -
 .../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                |   43 -
 .../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               |   43 -
 .../common/serialize/gson/GsonJsonObjectInput.java |  121 -
 .../serialize/gson/GsonJsonObjectOutput.java       |  108 -
 ...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-kryo/pom.xml               |   48 -
 .../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 -
 ...org.apache.dubbo.common.serialize.Serialization |    2 -
 .../dubbo-serialization-native-hession/pom.xml     |   43 -
 .../serialize/hessian/Hessian2ObjectInput.java     |   98 -
 .../serialize/hessian/Hessian2ObjectOutput.java    |   95 -
 .../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 -
 .../dubbo-serialization-protobuf/pom.xml           |   97 -
 .../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         |   54 -
 .../protostuff/ProtostuffObjectInput.java          |  136 -
 .../protostuff/ProtostuffObjectOutput.java         |  130 -
 .../protostuff/ProtostuffSerialization.java        |   58 -
 .../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               |   87 -
 .../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/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-serialization/pom.xml                        |    9 -
 pom.xml                                            |   52 +-
 670 files changed, 11233 insertions(+), 63300 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
index a89bdf4..32187e6 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
@@ -32,9 +32,26 @@ import org.apache.dubbo.rpc.Invoker;
  * @param <T>
  */
 public interface ClusterInvoker<T> extends Invoker<T> {
+
     URL getRegistryUrl();
 
     Directory<T> getDirectory();
 
     boolean isDestroyed();
+
+    default boolean isServiceDiscovery() {
+        Directory<T> directory = getDirectory();
+        if (directory == null) {
+            return false;
+        }
+        return directory.isServiceDiscovery();
+    }
+
+    default boolean hasProxyInvokers() {
+        Directory<T> directory = getDirectory();
+        if (directory == null) {
+            return false;
+        }
+        return !directory.isEmpty();
+    }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java
index 19861ef..c1c755e 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Constants.java
@@ -99,4 +99,6 @@ public interface Constants {
      * The key name for export URL in register center
      */
     String EXPORT_KEY = "export";
+
+    String CONSUMER_URL_KEY = "CONSUMER_URL";
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
index 5d48264..9fd3ca2 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster;
 
 import org.apache.dubbo.common.Node;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcException;
@@ -53,4 +54,15 @@ public interface Directory<T> extends Node {
 
     boolean isDestroyed();
 
+    default boolean isEmpty() {
+        return CollectionUtils.isEmpty(getAllInvokers());
+    }
+
+    default boolean isServiceDiscovery() {
+        return false;
+    }
+
+    void discordAddresses();
+
+    RouterChain<T> getRouterChain();
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index e340e3d..e69a90a 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -83,6 +83,10 @@ public class RouterChain<T> {
         this.routers = newRouters;
     }
 
+    public List<Router> getRouters() {
+        return routers;
+    }
+
     private void sort() {
         Collections.sort(routers);
     }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
index 166479c..4f27b08 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/AbstractConfigurator.java
@@ -72,8 +72,8 @@ public abstract class AbstractConfigurator implements Configurator {
          */
         String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
         if (StringUtils.isNotEmpty(apiVersion)) {
-            String currentSide = url.getParameter(SIDE_KEY);
-            String configuratorSide = configuratorUrl.getParameter(SIDE_KEY);
+            String currentSide = url.getSide();
+            String configuratorSide = configuratorUrl.getSide();
             if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) {
                 url = configureIfMatch(NetUtils.getLocalHost(), url);
             } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) && url.getPort() == configuratorUrl.getPort()) {
@@ -102,10 +102,10 @@ public abstract class AbstractConfigurator implements Configurator {
              *  1.If it is a consumer ip address, the intention is to control a specific consumer instance, it must takes effect at the consumer side, any provider received this override url should ignore.
              *  2.If the ip is 0.0.0.0, this override url can be used on consumer, and also can be used on provider.
              */
-            if (url.getParameter(SIDE_KEY, PROVIDER).equals(CONSUMER)) {
+            if (url.getSide(PROVIDER).equals(CONSUMER)) {
                 // NetUtils.getLocalHost is the ip address consumer registered to registry.
                 return configureIfMatch(NetUtils.getLocalHost(), url);
-            } else if (url.getParameter(SIDE_KEY, CONSUMER).equals(PROVIDER)) {
+            } else if (url.getSide(CONSUMER).equals(PROVIDER)) {
                 // take effect on all providers, so address must be 0.0.0.0, otherwise it won't flow to this if branch
                 return configureIfMatch(ANYHOST_VALUE, url);
             }
@@ -118,9 +118,8 @@ public abstract class AbstractConfigurator implements Configurator {
             // TODO, to support wildcards
             String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
             if (StringUtils.isEmpty(providers) || providers.contains(url.getAddress()) || providers.contains(ANYHOST_VALUE)) {
-                String configApplication = configuratorUrl.getParameter(APPLICATION_KEY,
-                        configuratorUrl.getUsername());
-                String currentApplication = url.getParameter(APPLICATION_KEY, url.getUsername());
+                String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
+                String currentApplication = url.getApplication(url.getUsername());
                 if (configApplication == null || ANY_VALUE.equals(configApplication)
                         || configApplication.equals(currentApplication)) {
                     Set<String> conditionKeys = new HashSet<String>();
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index c663552..7473466 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.rpc.cluster.directory;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -36,6 +37,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.CONSUMER_URL_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
 
 /**
@@ -51,13 +53,12 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
 
     private volatile boolean destroyed = false;
 
-    private volatile URL consumerUrl;
-
-    protected final Map<String, String> queryMap; // Initialization at construction time, assertion not null
-    protected final String consumedProtocol;
+    protected volatile URL consumerUrl;
 
     protected RouterChain<T> routerChain;
 
+    protected final Map<String, String> queryMap;
+
     public AbstractDirectory(URL url) {
         this(url, null);
     }
@@ -67,17 +68,35 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
             throw new IllegalArgumentException("url == null");
         }
 
-        queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
-        String path = queryMap.get(PATH_KEY);
-        this.consumedProtocol = this.queryMap.get(PROTOCOL_KEY) == null ? DUBBO : this.queryMap.get(PROTOCOL_KEY);
-        this.url = url.removeParameter(REFER_KEY).removeParameter(MONITOR_KEY);
+        this.url = url.removeAttribute(REFER_KEY).removeParameter(MONITOR_KEY);
 
-        this.consumerUrl = this.url.setProtocol(consumedProtocol).setPath(path == null ? queryMap.get(INTERFACE_KEY) : path).addParameters(queryMap)
-                .removeParameter(MONITOR_KEY);
+        Object referParams = url.getAttribute(REFER_KEY);
+        if (referParams != null) {
+            this.queryMap = (Map<String, String>) referParams;
+            this.consumerUrl = (URL)url.getAttribute(CONSUMER_URL_KEY);
+        } else {
+            this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
+        }
+
+        if (consumerUrl == null && queryMap != null) {
+            this.consumerUrl = turnRegistryUrlToConsumerUrl(url, queryMap);
+        }
 
         setRouterChain(routerChain);
     }
 
+    private URL turnRegistryUrlToConsumerUrl(URL url, Map<String, String> queryMap) {
+        return URLBuilder.from(url)
+                .setHost(queryMap.get("register.ip"))
+                .setPort(0)
+                .setProtocol(queryMap.get(PROTOCOL_KEY) == null ? DUBBO : queryMap.get(PROTOCOL_KEY))
+                .setPath(queryMap.get(PATH_KEY) != null ? queryMap.get(PATH_KEY) : queryMap.get(INTERFACE_KEY))
+                .clearParameters()
+                .addParameters(queryMap)
+                .removeParameter(MONITOR_KEY)
+                .build();
+    }
+
     @Override
     public List<Invoker<T>> list(Invocation invocation) throws RpcException {
         if (destroyed) {
@@ -123,6 +142,11 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         destroyed = true;
     }
 
+    @Override
+    public void discordAddresses() {
+        // do nothing by default
+    }
+
     protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;
 
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
new file mode 100644
index 0000000..982008b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/DefaultFilterChainBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
+
+import java.util.List;
+
+@Activate(order = 0)
+public class DefaultFilterChainBuilder implements FilterChainBuilder {
+
+    @Override
+    public <T> Invoker<T> buildInvokerChain(final Invoker<T> originalInvoker, String key, String group) {
+        Invoker<T> last = originalInvoker;
+        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
+
+        if (!filters.isEmpty()) {
+            for (int i = filters.size() - 1; i >= 0; i--) {
+                final Filter filter = filters.get(i);
+                final Invoker<T> next = last;
+                last = new FilterChainNode<>(originalInvoker, next, filter);
+            }
+        }
+
+        return last;
+    }
+
+    @Override
+    public <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> originalInvoker, String key, String group) {
+        ClusterInvoker<T> last = originalInvoker;
+        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(originalInvoker.getUrl(), key, group);
+
+        if (!filters.isEmpty()) {
+            for (int i = filters.size() - 1; i >= 0; i--) {
+                final Filter filter = filters.get(i);
+                final Invoker<T> next = last;
+                last = new ClusterFilterChainNode<>(originalInvoker, next, filter);
+            }
+        }
+
+        return last;
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
new file mode 100644
index 0000000..20275e2
--- /dev/null
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/FilterChainBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ListenableFilter;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
+import org.apache.dubbo.rpc.cluster.Directory;
+
+@SPI("default")
+public interface FilterChainBuilder {
+    <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group);
+
+    <T> ClusterInvoker<T> buildClusterInvokerChain(final ClusterInvoker<T> invoker, String key, String group);
+
+    class FilterChainNode<T, TYPE extends Invoker<T>> implements Invoker<T>{
+        TYPE originalInvoker;
+        Invoker<T> nextNode;
+        Filter filter;
+
+        public FilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+            this.originalInvoker = originalInvoker;
+            this.nextNode = nextNode;
+            this.filter = filter;
+        }
+
+        public TYPE getOriginalInvoker() {
+            return originalInvoker;
+        }
+
+        @Override
+        public Class<T> getInterface() {
+            return originalInvoker.getInterface();
+        }
+
+        @Override
+        public URL getUrl() {
+            return originalInvoker.getUrl();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return originalInvoker.isAvailable();
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            Result asyncResult;
+            try {
+                asyncResult = filter.invoke(nextNode, invocation);
+            } catch (Exception e) {
+                if (filter instanceof ListenableFilter) {
+                    ListenableFilter listenableFilter = ((ListenableFilter) filter);
+                    try {
+                        Filter.Listener listener = listenableFilter.listener(invocation);
+                        if (listener != null) {
+                            listener.onError(e, originalInvoker, invocation);
+                        }
+                    } finally {
+                        listenableFilter.removeListener(invocation);
+                    }
+                } else if (filter instanceof Filter.Listener) {
+                    Filter.Listener listener = (Filter.Listener) filter;
+                    listener.onError(e, originalInvoker, invocation);
+                }
+                throw e;
+            } finally {
+
+            }
+            return asyncResult.whenCompleteWithContext((r, t) -> {
+                if (filter instanceof ListenableFilter) {
+                    ListenableFilter listenableFilter = ((ListenableFilter) filter);
+                    Filter.Listener listener = listenableFilter.listener(invocation);
+                    try {
+                        if (listener != null) {
+                            if (t == null) {
+                                listener.onResponse(r, originalInvoker, invocation);
+                            } else {
+                                listener.onError(t, originalInvoker, invocation);
+                            }
+                        }
+                    } finally {
+                        listenableFilter.removeListener(invocation);
+                    }
+                } else if (filter instanceof Filter.Listener) {
+                    Filter.Listener listener = (Filter.Listener) filter;
+                    if (t == null) {
+                        listener.onResponse(r, originalInvoker, invocation);
+                    } else {
+                        listener.onError(t, originalInvoker, invocation);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void destroy() {
+            originalInvoker.destroy();
+        }
+
+        @Override
+        public String toString() {
+            return originalInvoker.toString();
+        }
+    }
+
+    class ClusterFilterChainNode<T, TYPE extends ClusterInvoker<T>> extends FilterChainNode<T, TYPE> implements ClusterInvoker<T> {
+        public ClusterFilterChainNode(TYPE originalInvoker, Invoker<T> nextNode, Filter filter) {
+            super(originalInvoker, nextNode, filter);
+        }
+
+
+        @Override
+        public URL getRegistryUrl() {
+            return getOriginalInvoker().getRegistryUrl();
+        }
+
+        @Override
+        public Directory<T> getDirectory() {
+            return getOriginalInvoker().getDirectory();
+        }
+
+        @Override
+        public boolean isDestroyed() {
+            return getOriginalInvoker().isDestroyed();
+        }
+    }
+}
diff --git a/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/InvocationInterceptorBuilder.java
similarity index 71%
rename from dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/InvocationInterceptorBuilder.java
index e84efc5..6f737a4 100644
--- a/dubbo-metadata/dubbo-metadata-report-nacos/src/test/java/org/apache/dubbo/metadata/store/nacos/NacosMetadata4TstService.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/InvocationInterceptorBuilder.java
@@ -14,15 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.dubbo.rpc.cluster.filter;
 
-package org.apache.dubbo.metadata.store.nacos;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 
-/**
- * Test interface for Nacos metadata report
- */
-public interface NacosMetadata4TstService {
-
-    int getCounter();
-
-    void printResult(String var);
+@SPI("default")
+public interface InvocationInterceptorBuilder {
+    <T> ClusterInvoker<T> buildClusterInterceptorChain(final ClusterInvoker<T> invoker, String key, String group);
 }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapper.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
similarity index 65%
copy from dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapper.java
copy to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
index af943c3..cded6dd 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/ProtocolListenerWrapper.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/filter/ProtocolFilterWrapper.java
@@ -1,89 +1,82 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.rpc.protocol;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.utils.UrlUtils;
-import org.apache.dubbo.rpc.Exporter;
-import org.apache.dubbo.rpc.ExporterListener;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.InvokerListener;
-import org.apache.dubbo.rpc.Protocol;
-import org.apache.dubbo.rpc.ProtocolServer;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.listener.ListenerExporterWrapper;
-import org.apache.dubbo.rpc.listener.ListenerInvokerWrapper;
-
-import java.util.Collections;
-import java.util.List;
-
-import static org.apache.dubbo.common.constants.CommonConstants.EXPORTER_LISTENER_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.INVOKER_LISTENER_KEY;
-
-/**
- * ListenerProtocol
- */
-@Activate(order = 200)
-public class ProtocolListenerWrapper implements Protocol {
-
-    private final Protocol protocol;
-
-    public ProtocolListenerWrapper(Protocol protocol) {
-        if (protocol == null) {
-            throw new IllegalArgumentException("protocol == null");
-        }
-        this.protocol = protocol;
-    }
-
-    @Override
-    public int getDefaultPort() {
-        return protocol.getDefaultPort();
-    }
-
-    @Override
-    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
-        if (UrlUtils.isRegistry(invoker.getUrl())) {
-            return protocol.export(invoker);
-        }
-        return new ListenerExporterWrapper<T>(protocol.export(invoker),
-                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
-                        .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
-    }
-
-    @Override
-    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
-        if (UrlUtils.isRegistry(url)) {
-            return protocol.refer(type, url);
-        }
-        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
-                Collections.unmodifiableList(
-                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
-                                .getActivateExtension(url, INVOKER_LISTENER_KEY)));
-    }
-
-    @Override
-    public void destroy() {
-        protocol.destroy();
-    }
-
-    @Override
-    public List<ProtocolServer> getServers() {
-        return protocol.getServers();
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.RpcException;
+
+import java.util.List;
+
+import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_FILTER_KEY;
+
+/**
+ * ListenerProtocol
+ */
+@Activate(order = 100)
+public class ProtocolFilterWrapper implements Protocol {
+
+    private final Protocol protocol;
+    private static final FilterChainBuilder builder
+            = ExtensionLoader.getExtensionLoader(FilterChainBuilder.class).getDefaultExtension();
+
+    public ProtocolFilterWrapper(Protocol protocol) {
+        if (protocol == null) {
+            throw new IllegalArgumentException("protocol == null");
+        }
+        this.protocol = protocol;
+    }
+
+    @Override
+    public int getDefaultPort() {
+        return protocol.getDefaultPort();
+    }
+
+    @Override
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+        if (UrlUtils.isRegistry(invoker.getUrl())) {
+            return protocol.export(invoker);
+        }
+        return protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
+    }
+
+    @Override
+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+        if (UrlUtils.isRegistry(url)) {
+            return protocol.refer(type, url);
+        }
+        return protocol.refer(type, url);
+    }
+
+    @Override
+    public void destroy() {
+        protocol.destroy();
+    }
+
+    @Override
+    public List<ProtocolServer> getServers() {
+        return protocol.getServers();
+    }
+
+}
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
index e989699..199361f 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ClusterInterceptor.java
@@ -34,8 +34,8 @@ public interface ClusterInterceptor {
     void after(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation);
 
     /**
-     * Does not need to override this method, override {@link #before(AbstractClusterInvoker, Invocation)}
-     * and {@link #after(AbstractClusterInvoker, Invocation)}, methods to add your own logic expected to be
+     * Override this method or {@link #before(AbstractClusterInvoker, Invocation)}
+     * and {@link #after(AbstractClusterInvoker, Invocation)} methods to add your own logic expected to be
      * executed before and after invoke.
      *
      * @param clusterInvoker
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
index 4beb7a0..053bc87 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/interceptor/ConsumerContextClusterInterceptor.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
@@ -43,6 +44,11 @@ public class ConsumerContextClusterInterceptor implements ClusterInterceptor, Cl
     }
 
     @Override
+    public Result intercept(AbstractClusterInvoker<?> clusterInvoker, Invocation invocation) throws RpcException {
+        return clusterInvoker.invoke(invocation);
+    }
+
+    @Override
     public void onMessage(Result appResponse, AbstractClusterInvoker<?> invoker, Invocation invocation) {
         RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
     }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
index 4464763..381d894 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -81,16 +81,16 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
 
     @Override
     public Class<T> getInterface() {
-        return directory.getInterface();
+        return getDirectory().getInterface();
     }
 
     @Override
     public URL getUrl() {
-        return directory.getConsumerUrl();
+        return getDirectory().getConsumerUrl();
     }
 
     public URL getRegistryUrl() {
-        return directory.getUrl();
+        return getDirectory().getUrl();
     }
 
     @Override
@@ -99,7 +99,7 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
         if (invoker != null) {
             return invoker.isAvailable();
         }
-        return directory.isAvailable();
+        return getDirectory().isAvailable();
     }
 
     public Directory<T> getDirectory() {
@@ -109,7 +109,7 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
     @Override
     public void destroy() {
         if (destroyed.compareAndSet(false, true)) {
-            directory.destroy();
+            getDirectory().destroy();
         }
     }
 
@@ -161,6 +161,7 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
         if (sticky) {
             stickyInvoker = invoker;
         }
+
         return invoker;
     }
 
@@ -196,6 +197,7 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
                 logger.error("cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url", t);
             }
         }
+
         return invoker;
     }
 
@@ -282,19 +284,30 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
         if (CollectionUtils.isEmpty(invokers)) {
             throw new RpcException(RpcException.NO_INVOKER_AVAILABLE_AFTER_FILTER, "Failed to invoke the method "
                     + invocation.getMethodName() + " in the service " + getInterface().getName()
-                    + ". No provider available for the service " + directory.getConsumerUrl().getServiceKey()
-                    + " from registry " + directory.getUrl().getAddress()
+                    + ". No provider available for the service " + getDirectory().getConsumerUrl().getServiceKey()
+                    + " from registry " + getDirectory().getUrl().getAddress()
                     + " on the consumer " + NetUtils.getLocalHost()
                     + " using the dubbo version " + Version.getVersion()
                     + ". Please check if the providers have been started and registered.");
         }
     }
 
+    protected Result invokeWithContext(Invoker<T> invoker, Invocation invocation) {
+        setContext(invoker);
+        Result result;
+        try {
+            result = invoker.invoke(invocation);
+        } finally {
+            clearContext(invoker);
+        }
+        return result;
+    }
+
     protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
                                        LoadBalance loadbalance) throws RpcException;
 
     protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
-        return directory.list(invocation);
+        return getDirectory().list(invocation);
     }
 
     /**
@@ -310,10 +323,27 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
      */
     protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
         if (CollectionUtils.isNotEmpty(invokers)) {
-            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
-                    .getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
+            return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(
+                    invokers.get(0).getUrl().getMethodParameter(
+                            RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE
+                    )
+            );
         } else {
             return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
         }
     }
+
+
+    private void setContext(Invoker<T> invoker) {
+        RpcContext context = RpcContext.getContext();
+        context.setInvoker(invoker)
+                .setRemoteAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort())
+                .setRemoteApplicationName(invoker.getUrl().getRemoteApplication());
+    }
+
+    private void clearContext(Invoker<T> invoker) {
+        // do nothing
+        RpcContext context = RpcContext.getContext();
+        context.setInvoker(null);
+    }
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
index c75052c..81208c5 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
@@ -39,7 +39,7 @@ public class AvailableClusterInvoker<T> extends AbstractClusterInvoker<T> {
     public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
         for (Invoker<T> invoker : invokers) {
             if (invoker.isAvailable()) {
-                return invoker.invoke(invocation);
+                return invokeWithContext(invoker, invocation);
             }
         }
         throw new RpcException("No provider available in " + invokers);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
index ab0623e..92a5743 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastClusterInvoker.java
@@ -49,7 +49,7 @@ public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
         Result result = null;
         for (Invoker<T> invoker : invokers) {
             try {
-                result = invoker.invoke(invocation);
+                result = invokeWithContext(invoker, invocation);
             } catch (RpcException e) {
                 exception = e;
                 logger.warn(e.getMessage(), e);
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
index 3e5f1d2..b7a30ed 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -17,9 +17,11 @@
 package org.apache.dubbo.rpc.cluster.support;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.remoting.Constants;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.ALIVE_KEY;
@@ -117,4 +119,21 @@ public class ClusterUtils {
         return remoteUrl.clearParameters().addParameters(map);
     }
 
+    public static URL mergeProviderUrl(URL remoteUrl, Map<String, String> localMap) {
+
+        //urlprocessor => upc
+        List<ProviderURLMergeProcessor> providerURLMergeProcessors = ExtensionLoader.getExtensionLoader(ProviderURLMergeProcessor.class)
+                .getActivateExtension(remoteUrl, "upc");
+
+        if (providerURLMergeProcessors != null && providerURLMergeProcessors.size() > 0) {
+            for (ProviderURLMergeProcessor providerURLMergeProcessor : providerURLMergeProcessors) {
+                if (providerURLMergeProcessor.accept(remoteUrl, localMap)) {
+                    return providerURLMergeProcessor.mergeProviderUrl(remoteUrl, localMap);
+                }
+            }
+        }
+
+        return mergeUrl(remoteUrl, localMap);
+    }
+
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
index 1528070..8bc4b13 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
@@ -98,7 +98,7 @@ public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
         try {
             checkInvokers(invokers, invocation);
             invoker = select(loadbalance, invocation, invokers, null);
-            return invoker.invoke(invocation);
+            return invokeWithContext(invoker, invocation);
         } catch (Throwable e) {
             logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
                     + e.getMessage() + ", ", e);
@@ -141,7 +141,7 @@ public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> {
             try {
                 Invoker<T> retryInvoker = select(loadbalance, invocation, invokers, Collections.singletonList(lastInvoker));
                 lastInvoker = retryInvoker;
-                retryInvoker.invoke(invocation);
+                invokeWithContext(retryInvoker, invocation);
             } catch (Throwable e) {
                 logger.error("Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.", e);
                 if ((++retryTimes) >= retries) {
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
index a04bba0..d349808 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
@@ -45,7 +45,7 @@ public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T> {
         checkInvokers(invokers, invocation);
         Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
         try {
-            return invoker.invoke(invocation);
+            return invokeWithContext(invoker, invocation);
         } catch (Throwable e) {
             if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
                 throw (RpcException) e;
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
index 5efe0ce..cc3ed9c 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
@@ -79,7 +79,7 @@ public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {
             invoked.add(invoker);
             RpcContext.getContext().setInvokers((List) invoked);
             try {
-                Result result = invoker.invoke(invocation);
+                Result result = invokeWithContext(invoker, invocation);
                 if (le != null && logger.isWarnEnabled()) {
                     logger.warn("Although retry the method " + methodName
                             + " in the service " + getInterface().getName()
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
index 539686e..3548a67 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
@@ -47,7 +47,7 @@ public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T> {
         try {
             checkInvokers(invokers, invocation);
             Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
-            return invoker.invoke(invocation);
+            return invokeWithContext(invoker, invocation);
         } catch (Throwable e) {
             logger.error("Failsafe ignore exception: " + e.getMessage(), e);
             return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
index 53a2c34..85a661b 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
@@ -85,7 +85,7 @@ public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T> {
             for (final Invoker<T> invoker : selected) {
                 executor.execute(() -> {
                     try {
-                        Result result = invoker.invoke(invocation);
+                        Result result = invokeWithContext(invoker, invocation);
                         ref.offer(result);
                     } catch (Throwable e) {
                         int value = count.incrementAndGet();
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
index 3b815ce..7bc1f94 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvoker.java
@@ -39,7 +39,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
 import static org.apache.dubbo.rpc.Constants.MERGER_KEY;
 
@@ -63,17 +62,17 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
             for (final Invoker<T> invoker : invokers) {
                 if (invoker.isAvailable()) {
                     try {
-                        return invoker.invoke(invocation);
+                        return invokeWithContext(invoker, invocation);
                     } catch (RpcException e) {
                         if (e.isNoInvokerAvailableAfterFilter()) {
-                            log.debug("No available provider for service" + getUrl().getServiceKey() + " on group " + invoker.getUrl().getParameter(GROUP_KEY) + ", will continue to try another group.");
+                            log.debug("No available provider for service" + getUrl().getServiceKey() + " on group " + invoker.getUrl().getGroup() + ", will continue to try another group.");
                         } else {
                             throw e;
                         }
                     }
                 }
             }
-            return invokers.iterator().next().invoke(invocation);
+            return invokeWithContext(invokers.iterator().next(), invocation);
         }
 
         Class<?> returnType;
@@ -88,7 +87,7 @@ public class MergeableClusterInvoker<T> extends AbstractClusterInvoker<T> {
         for (final Invoker<T> invoker : invokers) {
             RpcInvocation subInvocation = new RpcInvocation(invocation, invoker);
             subInvocation.setAttachment(ASYNC_KEY, "true");
-            results.put(invoker.getUrl().getServiceKey(), invoker.invoke(subInvocation));
+            results.put(invoker.getUrl().getServiceKey(), invokeWithContext(invoker, subInvocation));
         }
 
         Object result = null;
diff --git a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdTransporter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java
similarity index 72%
rename from dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdTransporter.java
rename to dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java
index 5ddec8e..9c91f38 100644
--- a/dubbo-remoting/dubbo-remoting-etcd3/src/main/java/org/apache/dubbo/remoting/etcd/jetcd/JEtcdTransporter.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ProviderURLMergeProcessor.java
@@ -14,17 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.remoting.etcd.jetcd;
+package org.apache.dubbo.rpc.cluster.support;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.remoting.etcd.EtcdClient;
-import org.apache.dubbo.remoting.etcd.EtcdTransporter;
+import org.apache.dubbo.common.extension.SPI;
 
-public class JEtcdTransporter implements EtcdTransporter {
+import java.util.Map;
 
-    @Override
-    public EtcdClient connect(URL url) {
-        return new JEtcdClient(url);
-    }
+@SPI
+public interface ProviderURLMergeProcessor {
+    URL mergeProviderUrl(URL providerUrl, Map<String, String> localParametersMap);
 
+    boolean accept(URL providerUrl, Map<String, String> localParametersMap);
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareCluster.java
index c11781e..64e09b8 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/registry/ZoneAwareCluster.java
@@ -21,9 +21,6 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 import org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster;
 
-/**
- * See {@link ZoneAwareClusterInvoker}
- */
 public class ZoneAwareCluster extends AbstractCluster {
 
     public final static String NAME = "zone-aware";
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
index 0170026..9ce920d 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/AbstractCluster.java
@@ -17,26 +17,35 @@
 package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
+import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
+import org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder;
+import org.apache.dubbo.rpc.cluster.filter.InvocationInterceptorBuilder;
 import org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor;
 import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
 import java.util.List;
 
+import static org.apache.dubbo.common.constants.CommonConstants.INVOCATION_INTERCEPTOR_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_INTERCEPTOR_KEY;
 
 public abstract class AbstractCluster implements Cluster {
 
     private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
-        AbstractClusterInvoker<T> last = clusterInvoker;
-        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtension(clusterInvoker.getUrl(), key);
+//        AbstractClusterInvoker<T> last = clusterInvoker;
+        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new FilterInvoker<>(clusterInvoker));
+        List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtensions();
 
         if (!interceptors.isEmpty()) {
             for (int i = interceptors.size() - 1; i >= 0; i--) {
@@ -50,12 +59,23 @@ public abstract class AbstractCluster implements Cluster {
 
     @Override
     public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
+        if (directory instanceof StaticDirectory) {
+            return doJoin(directory);
+        }
         return buildClusterInterceptors(doJoin(directory), directory.getUrl().getParameter(REFERENCE_INTERCEPTOR_KEY));
     }
 
+    private <T> AbstractClusterInvoker<T> buildInterceptorInvoker(AbstractClusterInvoker<T> invoker) {
+        List<InvocationInterceptorBuilder> builders = ExtensionLoader.getExtensionLoader(InvocationInterceptorBuilder.class).getActivateExtensions();
+        if (CollectionUtils.isEmpty(builders)) {
+            return invoker;
+        }
+        return new InterceptorInvoker<>(invoker, builders);
+    }
+
     protected abstract <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException;
 
-    protected class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
+    static class InterceptorInvokerNode<T> extends AbstractClusterInvoker<T> {
 
         private AbstractClusterInvoker<T> clusterInvoker;
         private ClusterInterceptor interceptor;
@@ -129,4 +149,103 @@ public abstract class AbstractCluster implements Cluster {
             return null;
         }
     }
+
+    static class FilterInvoker<T> extends AbstractClusterInvoker<T> {
+        private ClusterInvoker<T> filterInvoker;
+
+        public FilterInvoker(AbstractClusterInvoker<T> invoker) {
+            List<FilterChainBuilder> builders = ExtensionLoader.getExtensionLoader(FilterChainBuilder.class).getActivateExtensions();
+            if (CollectionUtils.isEmpty(builders)) {
+                filterInvoker = invoker;
+            } else {
+                ClusterInvoker<T> tmpInvoker = invoker;
+                for (FilterChainBuilder builder : builders) {
+                    tmpInvoker = builder.buildClusterInvokerChain(tmpInvoker, REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
+                }
+                filterInvoker = tmpInvoker;
+            }
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            return filterInvoker.invoke(invocation);
+        }
+
+        @Override
+        public Directory<T> getDirectory() {
+            return filterInvoker.getDirectory();
+        }
+
+
+
+        @Override
+        public URL getRegistryUrl() {
+            return filterInvoker.getRegistryUrl();
+        }
+
+        @Override
+        public boolean isDestroyed() {
+            return filterInvoker.isDestroyed();
+        }
+
+        @Override
+        public URL getUrl() {
+            return filterInvoker.getUrl();
+        }
+
+        /**
+         * The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
+         * Use ClusterInvoker<T> to replace AbstractClusterInvoker<T> in the future.
+         */
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+           return null;
+        }
+    }
+
+    static class InterceptorInvoker<T> extends AbstractClusterInvoker<T> {
+        private ClusterInvoker<T> interceptorInvoker;
+
+        public InterceptorInvoker(AbstractClusterInvoker<T> invoker, List<InvocationInterceptorBuilder> builders) {
+            ClusterInvoker<T> tmpInvoker = invoker;
+            for (InvocationInterceptorBuilder builder : builders) {
+                tmpInvoker = builder.buildClusterInterceptorChain(tmpInvoker, INVOCATION_INTERCEPTOR_KEY, CommonConstants.CONSUMER);
+            }
+            interceptorInvoker = tmpInvoker;
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            return interceptorInvoker.invoke(invocation);
+        }
+
+        @Override
+        public Directory<T> getDirectory() {
+            return interceptorInvoker.getDirectory();
+        }
+
+        @Override
+        public URL getRegistryUrl() {
+            return interceptorInvoker.getRegistryUrl();
+        }
+
+        @Override
+        public boolean isDestroyed() {
+            return interceptorInvoker.isDestroyed();
+        }
+
+        @Override
+        public URL getUrl() {
+            return interceptorInvoker.getUrl();
+        }
+
+        /**
+         * The only purpose is to build a interceptor chain, so the cluster related logic doesn't matter.
+         * Use ClusterInvoker<T> to replace AbstractClusterInvoker<T> in the future.
+         */
+        @Override
+        protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+            return null;
+        }
+    }
 }
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
new file mode 100644
index 0000000..9a153fd
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder
new file mode 100644
index 0000000..ea1c452
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder
@@ -0,0 +1 @@
+default=org.apache.dubbo.rpc.cluster.filter.DefaultFilterChainBuilder
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
index d8d616f..7825b37 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java
@@ -30,11 +30,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
-import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
 
@@ -83,8 +80,8 @@ public class ConfigParserTest {
             Assertions.assertNotNull(urls);
             Assertions.assertEquals(1, urls.size());
             URL url = urls.get(0);
-            Assertions.assertEquals("testgroup", url.getParameter(GROUP_KEY));
-            Assertions.assertEquals("1.0.0", url.getParameter(VERSION_KEY));
+            Assertions.assertEquals("testgroup", url.getGroup());
+            Assertions.assertEquals("1.0.0", url.getVersion());
         }
     }
 
@@ -97,7 +94,7 @@ public class ConfigParserTest {
             URL url = urls.get(0);
             Assertions.assertEquals("127.0.0.1", url.getAddress());
             Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0));
-            Assertions.assertNotNull(url.getParameter(APPLICATION_KEY));
+            Assertions.assertNotNull(url.getApplication());
         }
     }
 
@@ -123,7 +120,7 @@ public class ConfigParserTest {
             Assertions.assertEquals("service1", url.getServiceInterface());
             Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0));
             Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY));
-            Assertions.assertEquals(url.getParameter(APPLICATION_KEY), "demo-consumer");
+            Assertions.assertEquals(url.getApplication(), "demo-consumer");
         }
     }
 
@@ -139,7 +136,7 @@ public class ConfigParserTest {
             Assertions.assertEquals("*", url.getServiceInterface());
             Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0));
             Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY));
-            Assertions.assertEquals(url.getParameter(APPLICATION_KEY), "demo-consumer");
+            Assertions.assertEquals(url.getApplication(), "demo-consumer");
         }
     }
 
@@ -154,7 +151,7 @@ public class ConfigParserTest {
             Assertions.assertEquals("*", url.getServiceInterface());
             Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0));
             Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY));
-            Assertions.assertEquals(url.getParameter(APPLICATION_KEY), "demo-consumer");
+            Assertions.assertEquals(url.getApplication(), "demo-consumer");
         }
     }
 
@@ -170,7 +167,7 @@ public class ConfigParserTest {
             Assertions.assertEquals(6666, url.getParameter(TIMEOUT_KEY, 0));
             Assertions.assertEquals("random", url.getParameter(LOADBALANCE_KEY));
             Assertions.assertEquals("127.0.0.1:20880", url.getParameter(OVERRIDE_PROVIDERS_KEY));
-            Assertions.assertEquals(url.getParameter(APPLICATION_KEY), "demo-consumer");
+            Assertions.assertEquals(url.getApplication(), "demo-consumer");
         }
     }
 
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
index 33e863b..6d19dd6 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.rpc.cluster.support;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.rpc.Invocation;
@@ -41,13 +42,20 @@ import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.CLUSTER_AVAILABLE_CHECK_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.INVOCATION_NEED_MOCK;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 
@@ -63,7 +71,6 @@ public class AbstractClusterInvokerTest {
     StaticDirectory<IHelloService> dic;
     RpcInvocation invocation = new RpcInvocation();
     URL url = URL.valueOf("registry://localhost:9090/org.apache.dubbo.rpc.cluster.support.AbstractClusterInvokerTest.IHelloService?refer=" + URL.encode("application=abstractClusterInvokerTest"));
-    URL consumerUrl = URL.valueOf("dubbo://localhost:9090?application=abstractClusterInvokerTest");
 
     Invoker<IHelloService> invoker1;
     Invoker<IHelloService> invoker2;
@@ -85,6 +92,10 @@ public class AbstractClusterInvokerTest {
     @SuppressWarnings({"unchecked"})
     @BeforeEach
     public void setUp() throws Exception {
+        Map<String, Object> attributes = new HashMap<>();
+        attributes.put("application", "abstractClusterInvokerTest");
+        url = url.putAttribute(REFER_KEY, attributes);
+
         invocation.setMethodName("sayHello");
 
         invoker1 = mock(Invoker.class);
@@ -121,8 +132,7 @@ public class AbstractClusterInvokerTest {
         given(mockedInvoker1.getUrl()).willReturn(turl.setPort(999).setProtocol("mock"));
 
         invokers.add(invoker1);
-        dic = new StaticDirectory<>(url, invokers, null);
-        dic.setConsumerUrl(consumerUrl);
+        dic = new StaticDirectory<IHelloService>(url, invokers, null);
         cluster = new AbstractClusterInvoker(dic) {
             @Override
             protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
@@ -225,7 +235,9 @@ public class AbstractClusterInvokerTest {
     @Test
     public void testCloseAvailablecheck() {
         LoadBalance lb = mock(LoadBalance.class);
-        given(lb.select(invokers, consumerUrl, invocation)).willReturn(invoker1);
+        Map<String, String> queryMap = (Map<String, String> )url.getAttribute(REFER_KEY);
+        URL tmpUrl = turnRegistryUrlToConsumerUrl(url, queryMap);
+        given(lb.select(invokers, tmpUrl, invocation)).willReturn(invoker1);
         initlistsize5();
 
         Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
@@ -234,6 +246,16 @@ public class AbstractClusterInvokerTest {
 
     }
 
+    private URL turnRegistryUrlToConsumerUrl(URL url, Map<String, String> queryMap) {
+        return URLBuilder.from(url)
+                .setProtocol(queryMap.get(PROTOCOL_KEY) == null ? DUBBO : queryMap.get(PROTOCOL_KEY))
+                .setPath(queryMap.get(PATH_KEY) != null ? queryMap.get(PATH_KEY) : queryMap.get(INTERFACE_KEY))
+                .clearParameters()
+                .addParameters(queryMap)
+                .removeParameter(MONITOR_KEY)
+                .build();
+    }
+
     @Test
     public void testDonotSelectAgainAndNoCheckAvailable() {
 
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ClusterUtilsTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ClusterUtilsTest.java
index 01cb530..f0cedc0 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ClusterUtilsTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ClusterUtilsTest.java
@@ -36,7 +36,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REFERENCE_FILTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
@@ -105,8 +104,8 @@ public class ClusterUtilsTest {
         Assertions.assertEquals(url.getPassword(), "password");
         Assertions.assertEquals(url.getParameter(PID_KEY), "1234");
         Assertions.assertEquals(url.getParameter(THREADPOOL_KEY), "foo");
-        Assertions.assertEquals(url.getParameter(APPLICATION_KEY), "consumer");
-        Assertions.assertEquals(url.getParameter(REMOTE_APPLICATION_KEY), "provider");
+        Assertions.assertEquals(url.getApplication(), "consumer");
+        Assertions.assertEquals(url.getRemoteApplication(), "provider");
         Assertions.assertEquals(url.getParameter(REFERENCE_FILTER_KEY), "filter1,filter2,filter3");
     }
 
@@ -119,8 +118,8 @@ public class ClusterUtilsTest {
                 "&methods=remote&tag=remote&timestamp=remote");
         URL mergedUrl = ClusterUtils.mergeUrl(remoteURL, localURL.getParameters());
 
-        Assertions.assertEquals(remoteURL.getParameter(VERSION_KEY), mergedUrl.getParameter(VERSION_KEY));
-        Assertions.assertEquals(remoteURL.getParameter(GROUP_KEY), mergedUrl.getParameter(GROUP_KEY));
+        Assertions.assertEquals(remoteURL.getVersion(), mergedUrl.getVersion());
+        Assertions.assertEquals(remoteURL.getGroup(), mergedUrl.getGroup());
         Assertions.assertEquals(remoteURL.getParameter(DUBBO_VERSION_KEY), mergedUrl.getParameter(DUBBO_VERSION_KEY));
         Assertions.assertEquals(remoteURL.getParameter(RELEASE_KEY), mergedUrl.getParameter(RELEASE_KEY));
         Assertions.assertEquals(remoteURL.getParameter(METHODS_KEY), mergedUrl.getParameter(METHODS_KEY));
@@ -133,8 +132,8 @@ public class ClusterUtilsTest {
         remoteURL = URL.valueOf("dubbo://localhost:20880/DemoService");
         mergedUrl = ClusterUtils.mergeUrl(remoteURL, localURL.getParameters());
 
-        Assertions.assertEquals(mergedUrl.getParameter(VERSION_KEY),localURL.getParameter(VERSION_KEY));
-        Assertions.assertEquals(mergedUrl.getParameter(GROUP_KEY),localURL.getParameter(GROUP_KEY));
+        Assertions.assertEquals(mergedUrl.getVersion(),localURL.getVersion());
+        Assertions.assertEquals(mergedUrl.getGroup(),localURL.getGroup());
         Assertions.assertNull(mergedUrl.getParameter(DUBBO_VERSION_KEY));
         Assertions.assertNull(mergedUrl.getParameter(RELEASE_KEY));
         Assertions.assertNull(mergedUrl.getParameter(METHODS_KEY));
@@ -147,8 +146,8 @@ public class ClusterUtilsTest {
         remoteURL = URL.valueOf("dubbo://localhost:20880/DemoService?key=value");
         mergedUrl = ClusterUtils.mergeUrl(remoteURL, localURL.getParameters());
 
-        Assertions.assertEquals(mergedUrl.getParameter(VERSION_KEY),localURL.getParameter(VERSION_KEY));
-        Assertions.assertEquals(mergedUrl.getParameter(GROUP_KEY),localURL.getParameter(GROUP_KEY));
+        Assertions.assertEquals(mergedUrl.getVersion(),localURL.getVersion());
+        Assertions.assertEquals(mergedUrl.getGroup(),localURL.getGroup());
         Assertions.assertNull(mergedUrl.getParameter(DUBBO_VERSION_KEY));
         Assertions.assertNull(mergedUrl.getParameter(RELEASE_KEY));
         Assertions.assertNull(mergedUrl.getParameter(METHODS_KEY));
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index f227353..485ce7c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -19,8 +19,13 @@ package org.apache.dubbo.common;
 import org.apache.dubbo.common.config.Configuration;
 import org.apache.dubbo.common.config.InmemoryConfiguration;
 import org.apache.dubbo.common.constants.RemotingConstants;
+import org.apache.dubbo.common.url.component.PathURLAddress;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.url.component.URLAddress;
+import org.apache.dubbo.common.url.component.URLParam;
 import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.LRUCache;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
@@ -38,25 +43,29 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
+import static org.apache.dubbo.common.BaseServiceMetadata.COLON_SEPERATOR;
 import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_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.COMMA_SPLIT_PATTERN;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PASSWORD_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
 import static org.apache.dubbo.common.convert.Converter.convertIfPossible;
 import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 
@@ -97,23 +106,10 @@ class URL implements Serializable {
 
     private static final long serialVersionUID = -1985165475234910535L;
 
-    protected String protocol;
+    private static Map<String, URL> cachedURLs = new LRUCache<>();
 
-    protected String username;
-
-    protected String password;
-
-    // by default, host to registry
-    protected String host;
-
-    // by default, port to registry
-    protected int port;
-
-    protected String path;
-
-    private final Map<String, String> parameters;
-
-    private final Map<String, Map<String, String>> methodParameters;
+    private final URLAddress urlAddress;
+    private final URLParam urlParam;
 
     // ==== cache ====
 
@@ -123,31 +119,17 @@ class URL implements Serializable {
 
     private volatile transient Map<String, URL> urls;
 
-    private volatile transient String ip;
-
-    private volatile transient String full;
-
-    private volatile transient String identity;
-
-    private volatile transient String parameter;
-
-    private volatile transient String string;
-
     private transient String serviceKey;
     private transient String protocolServiceKey;
 
-    private transient String address;
-
     protected URL() {
-        this.protocol = null;
-        this.username = null;
-        this.password = null;
-        this.host = null;
-        this.port = 0;
-        this.address = null;
-        this.path = null;
-        this.parameters = null;
-        this.methodParameters = null;
+        this.urlAddress = null;
+        this.urlParam = null;
+    }
+
+    public URL(URLAddress urlAddress, URLParam urlParam) {
+        this.urlAddress = urlAddress;
+        this.urlParam = urlParam;
     }
 
     public URL(String protocol, String host, int port) {
@@ -189,176 +171,70 @@ class URL implements Serializable {
                int port,
                String path,
                Map<String, String> parameters) {
-        this(protocol, username, password, host, port, path, parameters, toMethodParameters(parameters));
+        if (StringUtils.isEmpty(username)
+                && StringUtils.isNotEmpty(password)) {
+            throw new IllegalArgumentException("Invalid url, password without username!");
+        }
+
+        this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
+        this.urlParam = new URLParam(parameters);
     }
 
-    public URL(String protocol,
+    protected URL(String protocol,
                String username,
                String password,
                String host,
                int port,
                String path,
                Map<String, String> parameters,
-               Map<String, Map<String, String>> methodParameters) {
+               boolean modifiable) {
         if (StringUtils.isEmpty(username)
                 && StringUtils.isNotEmpty(password)) {
             throw new IllegalArgumentException("Invalid url, password without username!");
         }
-        this.protocol = protocol;
-        this.username = username;
-        this.password = password;
-        this.host = host;
-        this.port = Math.max(port, 0);
-        this.address = getAddress(this.host, this.port);
 
-        // trim the beginning "/"
-        while (path != null && path.startsWith("/")) {
-            path = path.substring(1);
-        }
-        this.path = path;
-        if (parameters == null) {
-            parameters = new HashMap<>();
-        } else {
-            parameters = new HashMap<>(parameters);
-        }
-        this.parameters = Collections.unmodifiableMap(parameters);
-        this.methodParameters = Collections.unmodifiableMap(methodParameters);
+        this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
+        this.urlParam = new URLParam(parameters);
     }
 
-    private static String getAddress(String host, int port) {
-        return port <= 0 ? host : host + ':' + port;
+    public static URL cacheableValueOf(String url) {
+        URL cachedURL =  cachedURLs.get(url);
+        if (cachedURL != null) {
+            return cachedURL;
+        }
+        cachedURL = valueOf(url, false);
+        cachedURLs.put(url, cachedURL);
+        return cachedURL;
     }
 
     /**
-     * NOTICE: This method allocate too much objects, we can use {@link URLStrParser#parseDecodedStr(String)} instead.
-     * <p>
-     * Parse url string
+     * parse decoded url string, formatted dubbo://host:port/path?param=value, into strutted URL.
      *
-     * @param url URL string
-     * @return URL instance
-     * @see URL
+     * @param url, decoded url string
+     * @return
      */
     public static URL valueOf(String url) {
-        if (url == null || (url = url.trim()).length() == 0) {
-            throw new IllegalArgumentException("url == null");
-        }
-        String protocol = null;
-        String username = null;
-        String password = null;
-        String host = null;
-        int port = 0;
-        String path = null;
-        Map<String, String> parameters = null;
-        int i = url.indexOf('?'); // separator between body and parameters
-        if (i >= 0) {
-            String[] parts = url.substring(i + 1).split("&");
-            parameters = new HashMap<>();
-            for (String part : parts) {
-                part = part.trim();
-                if (part.length() > 0) {
-                    int j = part.indexOf('=');
-                    if (j >= 0) {
-                        String key = part.substring(0, j);
-                        String value = part.substring(j + 1);
-                        parameters.put(key, value);
-                        // compatible with lower versions registering "default." keys
-                        if (key.startsWith(DEFAULT_KEY_PREFIX)) {
-                            parameters.putIfAbsent(key.substring(DEFAULT_KEY_PREFIX.length()), value);
-                        }
-                    } else {
-                        parameters.put(part, part);
-                    }
-                }
-            }
-            url = url.substring(0, i);
-        }
-        i = url.indexOf("://");
-        if (i >= 0) {
-            if (i == 0) {
-                throw new IllegalStateException("url missing protocol: \"" + url + "\"");
-            }
-            protocol = url.substring(0, i);
-            url = url.substring(i + 3);
-        } else {
-            // case: file:/path/to/file.txt
-            i = url.indexOf(":/");
-            if (i >= 0) {
-                if (i == 0) {
-                    throw new IllegalStateException("url missing protocol: \"" + url + "\"");
-                }
-                protocol = url.substring(0, i);
-                url = url.substring(i + 1);
-            }
-        }
-
-        i = url.indexOf('/');
-        if (i >= 0) {
-            path = url.substring(i + 1);
-            url = url.substring(0, i);
-        }
-        i = url.lastIndexOf('@');
-        if (i >= 0) {
-            username = url.substring(0, i);
-            int j = username.indexOf(':');
-            if (j >= 0) {
-                password = username.substring(j + 1);
-                username = username.substring(0, j);
-            }
-            url = url.substring(i + 1);
-        }
-        i = url.lastIndexOf(':');
-        if (i >= 0 && i < url.length() - 1) {
-            if (url.lastIndexOf('%') > i) {
-                // ipv6 address with scope id
-                // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
-                // see https://howdoesinternetwork.com/2013/ipv6-zone-id
-                // ignore
-            } else {
-                port = Integer.parseInt(url.substring(i + 1));
-                url = url.substring(0, i);
-            }
-        }
-        if (url.length() > 0) {
-            host = url;
-        }
+        return valueOf(url, false);
+    }
 
-        return new URL(protocol, username, password, host, port, path, parameters);
+    /**
+     * parse normal or encoded url string into strutted URL:
+     * - dubbo://host:port/path?param=value
+     * - URL.encode("dubbo://host:port/path?param=value")
+     *
+     * @param url,     url string
+     * @param encoded, encoded or decoded
+     * @return
+     */
+    public static URL valueOf(String url, boolean encoded) {
+        return valueOf(url, encoded, false);
     }
 
-    public static Map<String, Map<String, String>> toMethodParameters(Map<String, String> parameters) {
-        Map<String, Map<String, String>> methodParameters = new HashMap<>();
-        if (parameters == null) {
-            return methodParameters;
+    public static URL valueOf(String url, boolean encoded, boolean modifiable) {
+        if (encoded) {
+            return URLStrParser.parseEncodedStr(url, modifiable);
         }
-
-        String methodsString = parameters.get(METHODS_KEY);
-        if (StringUtils.isNotEmpty(methodsString)) {
-            List<String> methods = StringUtils.splitToList(methodsString, ',');
-            for (Map.Entry<String, String> entry : parameters.entrySet()) {
-                String key = entry.getKey();
-                for (int i = 0; i < methods.size(); i++) {
-                    String method = methods.get(i);
-                    int methodLen = method.length();
-                    if (key.length() > methodLen
-                            && key.startsWith(method)
-                            && key.charAt(methodLen) == '.') {//equals to: key.startsWith(method + '.')
-                        String realKey = key.substring(methodLen + 1);
-                        URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
-                    }
-                }
-            }
-        } else {
-            for (Map.Entry<String, String> entry : parameters.entrySet()) {
-                String key = entry.getKey();
-                int methodSeparator = key.indexOf('.');
-                if (methodSeparator > 0) {
-                    String method = key.substring(0, methodSeparator);
-                    String realKey = key.substring(methodSeparator + 1);
-                    URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
-                }
-            }
-        }
-        return methodParameters;
+        return URLStrParser.parseDecodedStr(url, modifiable);
     }
 
     public static URL valueOf(String url, String... reserveParams) {
@@ -398,8 +274,8 @@ class URL implements Serializable {
                 }
             }
         }
-        return newMap.isEmpty() ? new URL(url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath())
-                : new URL(url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), newMap);
+        return newMap.isEmpty() ? new ServiceConfigURL(url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), (Map<String, String>) null)
+                : new ServiceConfigURL(url.getProtocol(), url.getUsername(), url.getPassword(), url.getHost(), url.getPort(), url.getPath(), newMap);
     }
 
     public static String encode(String value) {
@@ -436,92 +312,94 @@ class URL implements Serializable {
         return address;
     }
 
+    public URLAddress getUrlAddress() {
+        return urlAddress;
+    }
+
+    public URLParam getUrlParam() {
+        return urlParam;
+    }
+
     public String getProtocol() {
-        return protocol;
+        return urlAddress == null ? null : urlAddress.getProtocol();
     }
 
     public URL setProtocol(String protocol) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setProtocol(protocol);
+        return returnURL(newURLAddress);
     }
 
     public String getUsername() {
-        return username;
+        return urlAddress == null ? null : urlAddress.getUsername();
     }
 
     public URL setUsername(String username) {
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setUsername(username);
+        return returnURL(newURLAddress);
     }
 
     public String getPassword() {
-        return password;
+        return urlAddress == null ? null : urlAddress.getPassword();
     }
 
     public URL setPassword(String password) {
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setPassword(password);
+        return returnURL(newURLAddress);
     }
 
     public String getAuthority() {
-        if (StringUtils.isEmpty(username)
-                && StringUtils.isEmpty(password)) {
+        if (StringUtils.isEmpty(getUsername())
+                && StringUtils.isEmpty(getPassword())) {
             return null;
         }
-        return (username == null ? "" : username)
-                + ":" + (password == null ? "" : password);
+        return (getUsername() == null ? "" : getUsername())
+                + ":" + (getPassword() == null ? "" : getPassword());
     }
 
     public String getHost() {
-        return host;
+        return urlAddress == null ? null : urlAddress.getHost();
     }
 
     public URL setHost(String host) {
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setHost(host);
+        return returnURL(newURLAddress);
     }
 
-    /**
-     * Fetch IP address for this URL.
-     * <p>
-     * Pls. note that IP should be used instead of Host when to compare with socket's address or to search in a map
-     * which use address as its key.
-     *
-     * @return ip in string format
-     */
-    public String getIp() {
-        if (ip == null) {
-            ip = NetUtils.getIpByHost(host);
-        }
-        return ip;
-    }
 
     public int getPort() {
-        return port;
+        return urlAddress == null ? 0 : urlAddress.getPort();
     }
 
     public URL setPort(int port) {
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setPort(port);
+        return returnURL(newURLAddress);
     }
 
     public int getPort(int defaultPort) {
+        int port = getPort();
         return port <= 0 ? defaultPort : port;
     }
 
     public String getAddress() {
-        if (address == null) {
-            address = getAddress(host, port);
-        }
-        return address;
+        return urlAddress.getAddress();
     }
 
     public URL setAddress(String address) {
         int i = address.lastIndexOf(':');
         String host;
-        int port = this.port;
+        int port = this.getPort();
         if (i >= 0) {
             host = address.substring(0, i);
             port = Integer.parseInt(address.substring(i + 1));
         } else {
             host = address;
         }
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setAddress(host, port);
+        return returnURL(newURLAddress);
+    }
+
+    public String getIp() {
+        return urlAddress.getIp();
     }
 
     public String getBackupAddress() {
@@ -553,14 +431,16 @@ class URL implements Serializable {
     }
 
     public String getPath() {
-        return path;
+        return urlAddress == null ? null : urlAddress.getPath();
     }
 
     public URL setPath(String path) {
-        return new URL(getProtocol(), username, password, host, port, path, getParameters());
+        URLAddress newURLAddress = urlAddress.setPath(path);
+        return returnURL(newURLAddress);
     }
 
     public String getAbsolutePath() {
+        String path = getPath();
         if (path != null && !path.startsWith("/")) {
             return "/" + path;
         }
@@ -568,7 +448,7 @@ class URL implements Serializable {
     }
 
     public Map<String, String> getParameters() {
-        return parameters;
+        return urlParam.getParameters();
     }
 
     /**
@@ -589,6 +469,41 @@ class URL implements Serializable {
         return Collections.unmodifiableMap(selectedParameters);
     }
 
+    public Map<String, Map<String, String>> getMethodParameters() {
+        return urlParam.getMethodParameters();
+    }
+
+    public String getParameterAndDecoded(String key) {
+        return getParameterAndDecoded(key, null);
+    }
+
+    public String getParameterAndDecoded(String key, String defaultValue) {
+        return decode(getParameter(key, defaultValue));
+    }
+
+    public String getParameter(String key) {
+        return urlParam.getParameter(key);
+    }
+
+    public String getParameter(String key, String defaultValue) {
+        String value = getParameter(key);
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String[] getParameter(String key, String[] defaultValue) {
+        String value = getParameter(key);
+        return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
+    }
+
+    public List<String> getParameter(String key, List<String> defaultValue) {
+        String value = getParameter(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        String[] strArray = COMMA_SPLIT_PATTERN.split(value);
+        return Arrays.asList(strArray);
+    }
+
     /**
      * Get parameter
      *
@@ -624,42 +539,6 @@ class URL implements Serializable {
         return result;
     }
 
-
-    public Map<String, Map<String, String>> getMethodParameters() {
-        return methodParameters;
-    }
-
-    public String getParameterAndDecoded(String key) {
-        return getParameterAndDecoded(key, null);
-    }
-
-    public String getParameterAndDecoded(String key, String defaultValue) {
-        return decode(getParameter(key, defaultValue));
-    }
-
-    public String getParameter(String key) {
-        return parameters.get(key);
-    }
-
-    public String getParameter(String key, String defaultValue) {
-        String value = getParameter(key);
-        return StringUtils.isEmpty(value) ? defaultValue : value;
-    }
-
-    public String[] getParameter(String key, String[] defaultValue) {
-        String value = getParameter(key);
-        return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
-    }
-
-    public List<String> getParameter(String key, List<String> defaultValue) {
-        String value = getParameter(key);
-        if (StringUtils.isEmpty(value)) {
-            return defaultValue;
-        }
-        String[] strArray = COMMA_SPLIT_PATTERN.split(value);
-        return Arrays.asList(strArray);
-    }
-
     protected Map<String, Number> getNumbers() {
         // concurrent initialization is tolerant
         if (numbers == null) {
@@ -852,24 +731,24 @@ class URL implements Serializable {
         return URL.decode(getMethodParameter(method, key, defaultValue));
     }
 
-    public String getMethodParameterStrict(String method, String key) {
+    public String getMethodParameter(String method, String key) {
         Map<String, String> keyMap = getMethodParameters().get(method);
         String value = null;
         if (keyMap != null) {
             value = keyMap.get(key);
         }
+        if (StringUtils.isEmpty(value)) {
+            value = urlParam.getParameter(key);
+        }
         return value;
     }
 
-    public String getMethodParameter(String method, String key) {
+    public String getMethodParameterStrict(String method, String key) {
         Map<String, String> keyMap = getMethodParameters().get(method);
         String value = null;
         if (keyMap != null) {
             value = keyMap.get(key);
         }
-        if (StringUtils.isEmpty(value)) {
-            value = parameters.get(key);
-        }
         return value;
     }
 
@@ -1036,7 +915,7 @@ class URL implements Serializable {
     public boolean hasMethodParameter(String method, String key) {
         if (method == null) {
             String suffix = "." + key;
-            for (String fullKey : parameters.keySet()) {
+            for (String fullKey : getParameters().keySet()) {
                 if (fullKey.endsWith(suffix)) {
                     return true;
                 }
@@ -1045,7 +924,7 @@ class URL implements Serializable {
         }
         if (key == null) {
             String prefix = method + ".";
-            for (String fullKey : parameters.keySet()) {
+            for (String fullKey : getParameters().keySet()) {
                 if (fullKey.startsWith(prefix)) {
                     return true;
                 }
@@ -1056,6 +935,16 @@ class URL implements Serializable {
         return StringUtils.isNotEmpty(value);
     }
 
+    public String getAnyMethodParameter(String key) {
+        String suffix = "." + key;
+        for (String fullKey : getParameters().keySet()) {
+            if (fullKey.endsWith(suffix)) {
+                return getParameter(fullKey);
+            }
+        }
+        return null;
+    }
+
     public boolean hasMethodParameter(String method) {
         if (method == null) {
             return false;
@@ -1064,11 +953,11 @@ class URL implements Serializable {
     }
 
     public boolean isLocalHost() {
-        return NetUtils.isLocalHost(host) || getParameter(LOCALHOST_KEY, false);
+        return NetUtils.isLocalHost(getHost()) || getParameter(LOCALHOST_KEY, false);
     }
 
     public boolean isAnyHost() {
-        return ANYHOST_VALUE.equals(host) || getParameter(ANYHOST_KEY, false);
+        return ANYHOST_VALUE.equals(getHost()) || getParameter(ANYHOST_KEY, false);
     }
 
     public URL addParameterAndEncoded(String key, String value) {
@@ -1132,66 +1021,13 @@ class URL implements Serializable {
     }
 
     public URL addParameter(String key, String value) {
-        if (StringUtils.isEmpty(key)
-                || StringUtils.isEmpty(value)) {
-            return this;
-        }
-        // if value doesn't change, return immediately
-        if (value.equals(getParameters().get(key))) { // value != null
-            return this;
-        }
-
-        Map<String, String> map = new HashMap<>(getParameters());
-        map.put(key, value);
-
-        return new URL(getProtocol(), username, password, host, port, path, map);
+        URLParam newParam = urlParam.addParameter(key, value);
+        return returnURL(newParam);
     }
 
     public URL addParameterIfAbsent(String key, String value) {
-        if (StringUtils.isEmpty(key)
-                || StringUtils.isEmpty(value)) {
-            return this;
-        }
-        if (hasParameter(key)) {
-            return this;
-        }
-        Map<String, String> map = new HashMap<>(getParameters());
-        map.put(key, value);
-
-        return new URL(getProtocol(), username, password, host, port, path, map);
-    }
-
-    public URL addMethodParameter(String method, String key, String value) {
-        if (StringUtils.isEmpty(method)
-                || StringUtils.isEmpty(key)
-                || StringUtils.isEmpty(value)) {
-            return this;
-        }
-
-        Map<String, String> map = new HashMap<>(getParameters());
-        map.put(method + "." + key, value);
-        Map<String, Map<String, String>> methodMap = toMethodParameters(map);
-        URL.putMethodParameter(method, key, value, methodMap);
-
-        return new URL(getProtocol(), username, password, host, port, path, map, methodMap);
-    }
-
-    public URL addMethodParameterIfAbsent(String method, String key, String value) {
-        if (StringUtils.isEmpty(method)
-                || StringUtils.isEmpty(key)
-                || StringUtils.isEmpty(value)) {
-            return this;
-        }
-        if (hasMethodParameter(method, key)) {
-            return this;
-        }
-
-        Map<String, String> map = new HashMap<>(getParameters());
-        map.put(method + "." + key, value);
-        Map<String, Map<String, String>> methodMap = toMethodParameters(map);
-        URL.putMethodParameter(method, key, value, methodMap);
-
-        return new URL(getProtocol(), username, password, host, port, path, map, methodMap);
+        URLParam newParam = urlParam.addParameterIfAbsent(key, value);
+        return returnURL(newParam);
     }
 
     /**
@@ -1201,42 +1037,13 @@ class URL implements Serializable {
      * @return A new URL
      */
     public URL addParameters(Map<String, String> parameters) {
-        if (CollectionUtils.isEmptyMap(parameters)) {
-            return this;
-        }
-
-        boolean hasAndEqual = true;
-        for (Map.Entry<String, String> entry : parameters.entrySet()) {
-            String value = getParameters().get(entry.getKey());
-            if (value == null) {
-                if (entry.getValue() != null) {
-                    hasAndEqual = false;
-                    break;
-                }
-            } else {
-                if (!value.equals(entry.getValue())) {
-                    hasAndEqual = false;
-                    break;
-                }
-            }
-        }
-        // return immediately if there's no change
-        if (hasAndEqual) {
-            return this;
-        }
-
-        Map<String, String> map = new HashMap<>(getParameters());
-        map.putAll(parameters);
-        return new URL(getProtocol(), username, password, host, port, path, map);
+        URLParam newParam = urlParam.addParameters(parameters);
+        return returnURL(newParam);
     }
 
     public URL addParametersIfAbsent(Map<String, String> parameters) {
-        if (CollectionUtils.isEmptyMap(parameters)) {
-            return this;
-        }
-        Map<String, String> map = new HashMap<>(parameters);
-        map.putAll(getParameters());
-        return new URL(getProtocol(), username, password, host, port, path, map);
+        URLParam newURLParam = urlParam.addParametersIfAbsent(parameters);
+        return returnURL(newURLParam);
     }
 
     public URL addParameters(String... pairs) {
@@ -1244,7 +1051,7 @@ class URL implements Serializable {
             return this;
         }
         if (pairs.length % 2 != 0) {
-            throw new IllegalArgumentException("Map pairs can not be odd number.");
+            throw new IllegalArgumentException("Map pairs can not be odd numrer.");
         }
         Map<String, String> map = new HashMap<>();
         int len = pairs.length / 2;
@@ -1276,74 +1083,63 @@ class URL implements Serializable {
     }
 
     public URL removeParameters(String... keys) {
-        if (keys == null || keys.length == 0) {
-            return this;
-        }
-        Map<String, String> map = new HashMap<>(getParameters());
-        for (String key : keys) {
-            map.remove(key);
-        }
-        if (map.size() == getParameters().size()) {
-            return this;
-        }
-        return new URL(getProtocol(), username, password, host, port, path, map);
+        URLParam newURLParam = urlParam.removeParameters(keys);
+        return returnURL(newURLParam);
     }
 
     public URL clearParameters() {
-        return new URL(getProtocol(), username, password, host, port, path, new HashMap<>());
+        URLParam newURLParam = urlParam.clearParameters();
+        return returnURL(newURLParam);
     }
 
     public String getRawParameter(String key) {
         if (PROTOCOL_KEY.equals(key)) {
-            return protocol;
+            return urlAddress.getProtocol();
         }
         if (USERNAME_KEY.equals(key)) {
-            return username;
+            return urlAddress.getUsername();
         }
         if (PASSWORD_KEY.equals(key)) {
-            return password;
+            return urlAddress.getPassword();
         }
         if (HOST_KEY.equals(key)) {
-            return host;
+            return urlAddress.getHost();
         }
         if (PORT_KEY.equals(key)) {
-            return String.valueOf(port);
+            return String.valueOf(urlAddress.getPort());
         }
         if (PATH_KEY.equals(key)) {
-            return path;
+            return urlAddress.getPath();
         }
-        return getParameter(key);
+        return urlParam.getParameter(key);
     }
 
     public Map<String, String> toMap() {
-        Map<String, String> map = new HashMap<>(parameters);
-        if (protocol != null) {
-            map.put(PROTOCOL_KEY, protocol);
+        Map<String, String> map = new HashMap<>(getParameters());
+        if (getProtocol() != null) {
+            map.put(PROTOCOL_KEY, getProtocol());
         }
-        if (username != null) {
-            map.put(USERNAME_KEY, username);
+        if (getUsername() != null) {
+            map.put(USERNAME_KEY, getUsername());
         }
-        if (password != null) {
-            map.put(PASSWORD_KEY, password);
+        if (getPassword() != null) {
+            map.put(PASSWORD_KEY, getPassword());
         }
-        if (host != null) {
-            map.put(HOST_KEY, host);
+        if (getHost() != null) {
+            map.put(HOST_KEY, getHost());
         }
-        if (port > 0) {
-            map.put(PORT_KEY, String.valueOf(port));
+        if (getPort() > 0) {
+            map.put(PORT_KEY, String.valueOf(getPort()));
         }
-        if (path != null) {
-            map.put(PATH_KEY, path);
+        if (getPath() != null) {
+            map.put(PATH_KEY, getPath());
         }
         return map;
     }
 
     @Override
     public String toString() {
-        if (string != null) {
-            return string;
-        }
-        return string = buildString(false, true); // no show username and password
+        return buildString(false, true); // no show username and password
     }
 
     public String toString(String... parameters) {
@@ -1351,10 +1147,7 @@ class URL implements Serializable {
     }
 
     public String toIdentityString() {
-        if (identity != null) {
-            return identity;
-        }
-        return identity = buildString(true, false); // only return identity message, see the method "equals" and "hashCode"
+        return buildString(true, false); // only return identity message, see the method "equals" and "hashCode"
     }
 
     public String toIdentityString(String... parameters) {
@@ -1362,10 +1155,7 @@ class URL implements Serializable {
     }
 
     public String toFullString() {
-        if (full != null) {
-            return full;
-        }
-        return full = buildString(true, true);
+        return buildString(true, true);
     }
 
     public String toFullString(String... parameters) {
@@ -1373,10 +1163,7 @@ class URL implements Serializable {
     }
 
     public String toParameterString() {
-        if (parameter != null) {
-            return parameter;
-        }
-        return parameter = toParameterString(new String[0]);
+        return toParameterString(new String[0]);
     }
 
     public String toParameterString(String... parameters) {
@@ -1385,7 +1172,7 @@ class URL implements Serializable {
         return buf.toString();
     }
 
-    private void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {
+    protected void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {
         if (CollectionUtils.isNotEmptyMap(getParameters())) {
             List<String> includes = (ArrayUtils.isEmpty(parameters) ? null : Arrays.asList(parameters));
             boolean first = true;
@@ -1414,29 +1201,29 @@ class URL implements Serializable {
 
     private String buildString(boolean appendUser, boolean appendParameter, boolean useIP, boolean useService, String... parameters) {
         StringBuilder buf = new StringBuilder();
-        if (StringUtils.isNotEmpty(protocol)) {
-            buf.append(protocol);
+        if (StringUtils.isNotEmpty(getProtocol())) {
+            buf.append(getProtocol());
             buf.append("://");
         }
-        if (appendUser && StringUtils.isNotEmpty(username)) {
-            buf.append(username);
-            if (StringUtils.isNotEmpty(password)) {
+        if (appendUser && StringUtils.isNotEmpty(getUsername())) {
+            buf.append(getUsername());
+            if (StringUtils.isNotEmpty(getPassword())) {
                 buf.append(":");
-                buf.append(password);
+                buf.append(getPassword());
             }
             buf.append("@");
         }
         String host;
         if (useIP) {
-            host = getIp();
+            host = urlAddress.getIp();
         } else {
             host = getHost();
         }
         if (StringUtils.isNotEmpty(host)) {
             buf.append(host);
-            if (port > 0) {
+            if (getPort() > 0) {
                 buf.append(":");
-                buf.append(port);
+                buf.append(getPort());
             }
         }
         String path;
@@ -1465,7 +1252,7 @@ class URL implements Serializable {
     }
 
     public InetSocketAddress toInetSocketAddress() {
-        return new InetSocketAddress(host, port);
+        return new InetSocketAddress(getHost(), getPort());
     }
 
     /**
@@ -1506,21 +1293,34 @@ class URL implements Serializable {
         if (inf == null) {
             return null;
         }
-        serviceKey = buildKey(inf, getParameter(GROUP_KEY), getParameter(VERSION_KEY));
+        serviceKey = buildKey(inf, getGroup(), getVersion());
         return serviceKey;
     }
 
     /**
+     * Format : interface:version
+     *
+     * @return
+     */
+    public String getDisplayServiceKey() {
+        if (StringUtils.isEmpty(getVersion())) {
+            return getServiceInterface();
+        }
+        return getServiceInterface() +
+                COLON_SEPERATOR + getVersion();
+    }
+
+    /**
      * The format of return value is '{group}/{path/interfaceName}:{version}'
      *
      * @return
      */
     public String getPathKey() {
-        String inf = StringUtils.isNotEmpty(path) ? path : getServiceInterface();
+        String inf = StringUtils.isNotEmpty(getPath()) ? getPath() : getServiceInterface();
         if (inf == null) {
             return null;
         }
-        return buildKey(inf, getParameter(GROUP_KEY), getParameter(VERSION_KEY));
+        return buildKey(inf, getGroup(), getVersion());
     }
 
     public static String buildKey(String path, String group, String version) {
@@ -1549,7 +1349,7 @@ class URL implements Serializable {
     }
 
     public String getServiceInterface() {
-        return getParameter(INTERFACE_KEY, path);
+        return getParameter(INTERFACE_KEY, getPath());
     }
 
     public URL setServiceInterface(String service) {
@@ -1648,22 +1448,13 @@ class URL implements Serializable {
 
     public Configuration toConfiguration() {
         InmemoryConfiguration configuration = new InmemoryConfiguration();
-        configuration.addProperties(parameters);
+        configuration.addProperties(getParameters());
         return configuration;
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((host == null) ? 0 : host.hashCode());
-        result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
-        result = prime * result + ((password == null) ? 0 : password.hashCode());
-        result = prime * result + ((path == null) ? 0 : path.hashCode());
-        result = prime * result + port;
-        result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
-        result = prime * result + ((username == null) ? 0 : username.hashCode());
-        return result;
+        return Objects.hash(urlAddress, urlParam);
     }
 
     @Override
@@ -1674,36 +1465,11 @@ class URL implements Serializable {
         if (obj == null) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
+        if (!(obj instanceof URL)) {
             return false;
         }
         URL other = (URL) obj;
-        if (!StringUtils.isEquals(host, other.host)) {
-            return false;
-        }
-        if (parameters == null) {
-            if (other.parameters != null) {
-                return false;
-            }
-        } else if (!parameters.equals(other.parameters)) {
-            return false;
-        }
-        if (!StringUtils.isEquals(password, other.password)) {
-            return false;
-        }
-        if (!StringUtils.isEquals(path, other.path)) {
-            return false;
-        }
-        if (port != other.port) {
-            return false;
-        }
-        if (!StringUtils.isEquals(protocol, other.protocol)) {
-            return false;
-        }
-        if (!StringUtils.isEquals(username, other.username)) {
-            return false;
-        }
-        return true;
+        return Objects.equals(this.getUrlAddress(), other.getUrlAddress()) && Objects.equals(this.getUrlParam(), other.getUrlParam());
     }
 
     public static void putMethodParameter(String method, String key, String value, Map<String, Map<String, String>> methodParameters) {
@@ -1711,6 +1477,114 @@ class URL implements Serializable {
         subParameter.put(key, value);
     }
 
+    protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
+        return (T) new ServiceConfigURL(urlAddress, urlParam, null);
+    }
+
+    /* methods introduced for CompositeURL, CompositeURL must override to make the implementations meaningful */
+
+    public String getApplication(String defaultValue) {
+        String value = getApplication();
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String getApplication() {
+        return getParameter(APPLICATION_KEY);
+    }
+
+    public String getRemoteApplication() {
+        return getParameter(REMOTE_APPLICATION_KEY);
+    }
+
+    public String getGroup() {
+        return getParameter(GROUP_KEY);
+    }
+
+    public String getGroup(String defaultValue) {
+        String value = getGroup();
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String getVersion() {
+        return getParameter(VERSION_KEY);
+    }
+
+    public String getVersion(String defaultValue) {
+        String value = getVersion();
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String getConcatenatedParameter(String key) {
+        return getParameter(key);
+    }
+
+    public String getCategory(String defaultValue) {
+        String value = getCategory();
+        if (StringUtils.isEmpty(value)) {
+            value = defaultValue;
+        }
+        return value;
+    }
+
+    public String[] getCategory(String[] defaultValue) {
+        String value = getCategory();
+        return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
+    }
+
+    public String getCategory() {
+        return getParameter(CATEGORY_KEY);
+    }
+
+    public String getSide(String defaultValue) {
+        String value = getSide();
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String getSide() {
+        return getParameter(SIDE_KEY);
+    }
+
+    /* Service Config URL, START*/
+    public Map<String, Object> getAttributes() {
+        return new HashMap<>();
+    }
+
+    public URL addAttributes(Map<String, Object> attributes) {
+        return this;
+    }
+
+    public Object getAttribute(String key) {
+        return null;
+    }
+
+    public URL putAttribute(String key, Object obj) {
+        return this;
+    }
+
+    public URL removeAttribute(String key) {
+        return this;
+    }
+
+    public boolean hasAttribute(String key) {
+        return true;
+    }
+
+    /* Service Config URL, END*/
+
+    private URL returnURL(URLAddress newURLAddress) {
+        if (urlAddress == newURLAddress) {
+            return this;
+        }
+        return newURL(newURLAddress, urlParam);
+    }
+
+    private URL returnURL(URLParam newURLParam) {
+        if (urlParam == newURLParam) {
+            return this;
+        }
+        return newURL(urlAddress, newURLParam);
+    }
+
     /* add service scope operations, see InstanceAddressURL */
     public Map<String, String> getServiceParameters(String service) {
         return getParameters();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
index 20c6c60..7ab8bb3 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLBuilder.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.common;
 
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
@@ -24,7 +25,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
-public final class URLBuilder {
+public final class URLBuilder extends ServiceConfigURL {
     private String protocol;
 
     private String username;
@@ -41,6 +42,8 @@ public final class URLBuilder {
 
     private Map<String, String> parameters;
 
+    private Map<String, Object> attributes;
+
     private Map<String, Map<String, String>> methodParameters;
 
     public URLBuilder() {
@@ -51,6 +54,7 @@ public final class URLBuilder {
         port = 0;
         path = null;
         parameters = new HashMap<>();
+        attributes = new HashMap<>();
         methodParameters = new HashMap<>();
     }
 
@@ -83,8 +87,9 @@ public final class URLBuilder {
                       String password,
                       String host,
                       int port,
-                      String path, Map<String, String> parameters) {
-        this(protocol, username, password, host, port, path, parameters, URL.toMethodParameters(parameters));
+                      String path,
+                      Map<String, String> parameters) {
+        this(protocol, username, password, host, port, path, parameters, null);
     }
 
     public URLBuilder(String protocol,
@@ -92,8 +97,9 @@ public final class URLBuilder {
                       String password,
                       String host,
                       int port,
-                      String path, Map<String, String> parameters,
-                      Map<String, Map<String, String>> methodParameters) {
+                      String path,
+                      Map<String, String> parameters,
+                      Map<String, Object> attributes) {
         this.protocol = protocol;
         this.username = username;
         this.password = password;
@@ -101,7 +107,7 @@ public final class URLBuilder {
         this.port = port;
         this.path = path;
         this.parameters = parameters != null ? parameters : new HashMap<>();
-        this.methodParameters = (methodParameters != null ? methodParameters : new HashMap<>());
+        this.attributes = attributes != null ? attributes : new HashMap<>();
     }
 
     public static URLBuilder from(URL url) {
@@ -112,7 +118,7 @@ public final class URLBuilder {
         int port = url.getPort();
         String path = url.getPath();
         Map<String, String> parameters = new HashMap<>(url.getParameters());
-        Map<String, Map<String, String>> methodParameters = new HashMap<>(url.getMethodParameters());
+        Map<String, Object> attributes = new HashMap<>(url.getAttributes());
         return new URLBuilder(
                 protocol,
                 username,
@@ -121,14 +127,14 @@ public final class URLBuilder {
                 port,
                 path,
                 parameters,
-                methodParameters);
+                attributes);
     }
 
-    public URL build() {
+    public ServiceConfigURL build() {
         if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) {
             throw new IllegalArgumentException("Invalid url, password without username!");
         }
-        port = port < 0 ? 0 : port;
+        port = Math.max(port, 0);
         // trim the leading "/"
         int firstNonSlash = 0;
         if (path != null) {
@@ -141,13 +147,14 @@ public final class URLBuilder {
                 path = path.substring(firstNonSlash);
             }
         }
-        if (CollectionUtils.isEmptyMap(methodParameters)) {
-            return new URL(protocol, username, password, host, port, path, parameters);
-        } else {
-            return new URL(protocol, username, password, host, port, path, parameters, methodParameters);
-        }
+        return new ServiceConfigURL(protocol, username, password, host, port, path, parameters, attributes);
     }
 
+    @Override
+    public URLBuilder putAttribute(String key, Object obj) {
+        attributes.put(key, obj);
+        return this;
+    }
 
     public URLBuilder setProtocol(String protocol) {
         this.protocol = protocol;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
index 37afb78..619b1bb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java
@@ -16,16 +16,26 @@
  */
 package org.apache.dubbo.common;
 
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.url.component.URLItemCache;
+
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
 import static org.apache.dubbo.common.utils.StringUtils.EMPTY_STRING;
 import static org.apache.dubbo.common.utils.StringUtils.decodeHexByte;
 import static org.apache.dubbo.common.utils.Utf8Utils.decodeUtf8;
 
 public final class URLStrParser {
-
+    private static final Logger logger = LoggerFactory.getLogger(URLStrParser.class);
+    public static final String ENCODED_QUESTION_MARK = "%3F";
+    public static final String ENCODED_TIMESTAMP_KEY = "timestamp%3D";
+    public static final String ENCODED_PID_KEY = "pid%3D";
+    public static final String ENCODED_AND_MARK = "%26";
     private static final char SPACE = 0x20;
 
     private static final ThreadLocal<TempBuf> DECODE_TEMP_BUF = ThreadLocal.withInitial(() -> new TempBuf(1024));
@@ -34,12 +44,16 @@ public final class URLStrParser {
         //empty
     }
 
+    public static URL parseDecodedStr(String decodedURLStr) {
+        return parseDecodedStr(decodedURLStr, false);
+    }
+
     /**
      * @param decodedURLStr : after {@link URL#decode} string
      *                      decodedURLStr format: protocol://username:password@host:port/path?k1=v1&k2=v2
      *                      [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2]
      */
-    public static URL parseDecodedStr(String decodedURLStr) {
+    public static URL parseDecodedStr(String decodedURLStr, boolean modifiable) {
         Map<String, String> parameters = null;
         int pathEndIdx = decodedURLStr.indexOf('?');
         if (pathEndIdx >= 0) {
@@ -49,7 +63,7 @@ public final class URLStrParser {
         }
 
         String decodedBody = decodedURLStr.substring(0, pathEndIdx);
-        return parseURLBody(decodedURLStr, decodedBody, parameters);
+        return parseURLBody(decodedURLStr, decodedBody, parameters, modifiable);
     }
 
     private static Map<String, String> parseDecodedParams(String str, int from) {
@@ -92,7 +106,7 @@ public final class URLStrParser {
      * @param parameters  :
      * @return URL
      */
-    private static URL parseURLBody(String fullURLStr, String decodedBody, Map<String, String> parameters) {
+    private static URL parseURLBody(String fullURLStr, String decodedBody, Map<String, String> parameters, boolean modifiable) {
         int starIdx = 0, endIdx = decodedBody.length();
         String protocol = null;
         int protoEndIdx = decodedBody.indexOf("://");
@@ -125,9 +139,13 @@ public final class URLStrParser {
         String password = null;
         int pwdEndIdx = lastIndexOf(decodedBody, '@', starIdx, endIdx);
         if (pwdEndIdx > 0) {
-            int userNameEndIdx = indexOf(decodedBody, ':', starIdx, pwdEndIdx);
-            username = decodedBody.substring(starIdx, userNameEndIdx);
-            password = decodedBody.substring(userNameEndIdx + 1, pwdEndIdx);
+            int passwordStartIdx = indexOf(decodedBody, ':', starIdx, pwdEndIdx);
+            if (passwordStartIdx != -1) {//tolerate incomplete user pwd input, like '1234@'
+                username = decodedBody.substring(starIdx, passwordStartIdx);
+                password = decodedBody.substring(passwordStartIdx + 1, pwdEndIdx);
+            } else {
+                username = decodedBody.substring(starIdx, pwdEndIdx);
+            }
             starIdx = pwdEndIdx + 1;
         }
 
@@ -149,7 +167,44 @@ public final class URLStrParser {
         if (endIdx > starIdx) {
             host = decodedBody.substring(starIdx, endIdx);
         }
-        return new URL(protocol, username, password, host, port, path, parameters);
+
+        // check cache
+        protocol = URLItemCache.checkProtocol(protocol);
+        path = URLItemCache.checkPath(path);
+
+        return new ServiceConfigURL(protocol, username, password, host, port, path, parameters);
+    }
+
+    public static URL parseEncodedStr(String encodedURLStr) {
+        return parseEncodedStr(encodedURLStr, false);
+    }
+
+    public static String[] parseRawURLToArrays(String rawURLStr, int pathEndIdx) {
+        String[] parts = new String[2];
+        int paramStartIdx = pathEndIdx + 3;//skip ENCODED_QUESTION_MARK
+        if (pathEndIdx == -1) {
+            pathEndIdx = rawURLStr.indexOf('?');
+            if (pathEndIdx == -1) {
+                // url with no params, decode anyway
+                rawURLStr = URL.decode(rawURLStr);
+            } else {
+                paramStartIdx = pathEndIdx + 1;
+            }
+        }
+        if (pathEndIdx >= 0) {
+            parts[0] = rawURLStr.substring(0, pathEndIdx);
+            parts[1] = rawURLStr.substring(paramStartIdx);
+        } else {
+            parts = new String[]{rawURLStr};
+        }
+        return parts;
+    }
+
+    public static Map<String, String> parseParams(String rawParams, boolean encoded) {
+        if (encoded) {
+            return parseEncodedParams(rawParams, 0);
+        }
+        return parseDecodedParams(rawParams, 0);
     }
 
     /**
@@ -157,7 +212,7 @@ public final class URLStrParser {
      *                      encodedURLStr after decode format: protocol://username:password@host:port/path?k1=v1&k2=v2
      *                      [protocol://][username:password@][host:port]/[path][?k1=v1&k2=v2]
      */
-    public static URL parseEncodedStr(String encodedURLStr) {
+    public static URL parseEncodedStr(String encodedURLStr, boolean modifiable) {
         Map<String, String> parameters = null;
         int pathEndIdx = encodedURLStr.indexOf("%3F");// '?'
         if (pathEndIdx >= 0) {
@@ -168,7 +223,7 @@ public final class URLStrParser {
 
         //decodedBody format: [protocol://][username:password@][host:port]/[path]
         String decodedBody = decodeComponent(encodedURLStr, 0, pathEndIdx, false, DECODE_TEMP_BUF.get());
-        return parseURLBody(encodedURLStr, decodedBody, parameters);
+        return parseURLBody(encodedURLStr, decodedBody, parameters, modifiable);
     }
 
     private static Map<String, String> parseEncodedParams(String str, int from) {
@@ -225,12 +280,30 @@ public final class URLStrParser {
 
         if (isEncoded) {
             String name = decodeComponent(str, nameStart, valueStart - 3, false, tempBuf);
-            String value = decodeComponent(str, valueStart, valueEnd, false, tempBuf);
-            params.put(name, value);
+            String value;
+            if (valueStart == valueEnd) {
+                value = name;
+            } else {
+                value = decodeComponent(str, valueStart, valueEnd, false, tempBuf);
+            }
+            URLItemCache.putParams(params,name, value);
+            // compatible with lower versions registering "default." keys
+            if (name.startsWith(DEFAULT_KEY_PREFIX)) {
+                params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value);
+            }
         } else {
-            String name = str.substring(nameStart, valueStart -1);
-            String value = str.substring(valueStart, valueEnd);
-            params.put(name, value);
+            String name = str.substring(nameStart, valueStart - 1);
+            String value;
+            if (valueStart == valueEnd) {
+                value = name;
+            } else {
+                value = str.substring(valueStart, valueEnd);
+            }
+            URLItemCache.putParams(params, name, value);
+            // compatible with lower versions registering "default." keys
+            if (name.startsWith(DEFAULT_KEY_PREFIX)) {
+                params.putIfAbsent(name.substring(DEFAULT_KEY_PREFIX.length()), value);
+            }
         }
         return true;
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java
index c4ecea6..b81b7a3 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/ClassGenerator.java
@@ -16,6 +16,11 @@
  */
 package org.apache.dubbo.common.bytecode;
 
+import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.ReflectUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
 import javassist.CannotCompileException;
 import javassist.ClassPool;
 import javassist.CtClass;
@@ -24,12 +29,7 @@ import javassist.CtField;
 import javassist.CtMethod;
 import javassist.CtNewConstructor;
 import javassist.CtNewMethod;
-import javassist.LoaderClassPath;
 import javassist.NotFoundException;
-import org.apache.dubbo.common.utils.ArrayUtils;
-import org.apache.dubbo.common.utils.ClassUtils;
-import org.apache.dubbo.common.utils.ReflectUtils;
-import org.apache.dubbo.common.utils.StringUtils;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
@@ -91,7 +91,7 @@ public final class ClassGenerator {
         ClassPool pool = POOL_MAP.get(loader);
         if (pool == null) {
             pool = new ClassPool(true);
-            pool.appendClassPath(new LoaderClassPath(loader));
+            pool.appendClassPath(new CustomizedLoaderClassPath(loader));
             POOL_MAP.put(loader, pool);
         }
         return pool;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.java
new file mode 100644
index 0000000..b1ef491
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/CustomizedLoaderClassPath.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.common.bytecode;
+
+import javassist.ClassPath;
+
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+
+/**
+ * A class search-path representing a class loader.
+ *
+ * <p>It is used for obtaining a class file from the given
+ * class loader by <code>getResourceAsStream()</code>.
+ * The <code>LoaderClassPath</code> refers to the class loader through
+ * <code>WeakReference</code>.  If the class loader is garbage collected,
+ * the other search pathes are examined.
+ *
+ * <p>The given class loader must have both <code>getResourceAsStream()</code>
+ * and <code>getResource()</code>.
+ *
+ * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
+ * @author Shigeru Chiba
+ */
+public class CustomizedLoaderClassPath implements ClassPath {
+    private WeakReference clref;
+
+    /**
+     * Creates a search path representing a class loader.
+     */
+    public CustomizedLoaderClassPath(ClassLoader cl) {
+        clref = new WeakReference(cl);
+    }
+
+    public String toString() {
+        Object cl = null;
+        if (clref != null)
+            cl = clref.get();
+
+        return cl == null ? "<null>" : cl.toString();
+    }
+
+    /**
+     * Obtains a class file from the class loader.
+     * This method calls <code>getResourceAsStream(String)</code>
+     * on the class loader.
+     */
+    public InputStream openClassfile(String classname) {
+        String cname = classname.replace('.', '/') + ".class";
+        ClassLoader cl = (ClassLoader) clref.get();
+        if (cl == null) {
+            return null;        // not found
+        } else {
+            InputStream result = cl.getResourceAsStream(cname);
+            if (result == null && (cl != this.getClass().getClassLoader())) {
+                return this.getClass().getClassLoader().getResourceAsStream(cname);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Obtains the URL of the specified class file.
+     * This method calls <code>getResource(String)</code>
+     * on the class loader.
+     *
+     * @return null if the class file could not be found.
+     */
+    public URL find(String classname) {
+        String cname = classname.replace('.', '/') + ".class";
+        ClassLoader cl = (ClassLoader) clref.get();
+        if (cl == null) {
+            return null;        // not found
+        } else {
+            URL url = cl.getResource(cname);
+            if (url == null && (cl != this.getClass().getClassLoader())) {
+                return this.getClass().getClassLoader().getResource(cname);
+            }
+            return url;
+        }
+    }
+
+    /**
+     * Closes this class path.
+     */
+    public void close() {
+        clref = null;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java
index eebf5a0..75a79a5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/CompositeConfiguration.java
@@ -38,6 +38,9 @@ public class CompositeConfiguration implements Configuration {
      */
     private List<Configuration> configList = new LinkedList<Configuration>();
 
+    //FIXME, consider change configList to SortedMap to replace this boolean status.
+    private boolean dynamicIncluded;
+
     public CompositeConfiguration() {
         this(null, null);
     }
@@ -58,6 +61,15 @@ public class CompositeConfiguration implements Configuration {
         }
     }
 
+    public void setDynamicIncluded(boolean dynamicIncluded) {
+        this.dynamicIncluded = dynamicIncluded;
+    }
+
+    //FIXME, consider change configList to SortedMap to replace this boolean status.
+    public boolean isDynamicIncluded() {
+        return dynamicIncluded;
+    }
+
     public void addConfiguration(Configuration configuration) {
         if (configList.contains(configuration)) {
             return;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
index a7c0693..ed79f15 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
@@ -66,6 +66,10 @@ public class ConfigurationUtils {
         return ApplicationModel.getEnvironment().getConfiguration();
     }
 
+    public static Configuration getDynamicGlobalConfiguration() {
+        return ApplicationModel.getEnvironment().getDynamicGlobalConfiguration();
+    }
+
     // FIXME
     @SuppressWarnings("deprecation")
     public static int getServerShutdownTimeout() {
@@ -92,6 +96,14 @@ public class ConfigurationUtils {
         return timeout;
     }
 
+    public static String getDynamicProperty(String property) {
+        return getDynamicProperty(property, null);
+    }
+
+    public static String getDynamicProperty(String property, String defaultValue) {
+        return StringUtils.trim(getDynamicGlobalConfiguration().getString(property, defaultValue));
+    }
+
     public static String getProperty(String property) {
         return getProperty(property, null);
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
index b5f24f7..ea4b4e5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/Environment.java
@@ -20,6 +20,8 @@ import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.context.LifecycleAdapter;
 import org.apache.dubbo.common.extension.DisableInject;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.config.AbstractConfig;
 import org.apache.dubbo.config.ConfigCenterConfig;
 import org.apache.dubbo.config.context.ConfigConfigurationAdapter;
@@ -32,6 +34,8 @@ import java.util.Map;
 import java.util.Optional;
 
 public class Environment extends LifecycleAdapter implements FrameworkExt {
+    private static final Logger logger = LoggerFactory.getLogger(Environment.class);
+
     public static final String NAME = "environment";
 
     private final PropertiesConfiguration propertiesConfiguration;
@@ -41,6 +45,8 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
     private final InmemoryConfiguration appExternalConfiguration;
 
     private CompositeConfiguration globalConfiguration;
+    private CompositeConfiguration dynamicGlobalConfiguration;
+
 
     private Map<String, String> externalConfigurationMap = new HashMap<>();
     private Map<String, String> appExternalConfigurationMap = new HashMap<>();
@@ -146,9 +152,6 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
     public Configuration getConfiguration() {
         if (globalConfiguration == null) {
             globalConfiguration = new CompositeConfiguration();
-            if (dynamicConfiguration != null) {
-                globalConfiguration.addConfiguration(dynamicConfiguration);
-            }
             globalConfiguration.addConfiguration(systemConfiguration);
             globalConfiguration.addConfiguration(environmentConfiguration);
             globalConfiguration.addConfiguration(appExternalConfiguration);
@@ -158,6 +161,21 @@ public class Environment extends LifecycleAdapter implements FrameworkExt {
         return globalConfiguration;
     }
 
+    public Configuration getDynamicGlobalConfiguration() {
+        if (dynamicGlobalConfiguration == null) {
+            if (dynamicConfiguration == null) {
+                if (logger.isWarnEnabled()) {
+                    logger.warn("dynamicConfiguration is null , return globalConfiguration.");
+                }
+                return globalConfiguration;
+            }
+            dynamicGlobalConfiguration = new CompositeConfiguration();
+            dynamicGlobalConfiguration.addConfiguration(dynamicConfiguration);
+            dynamicGlobalConfiguration.addConfiguration(getConfiguration());
+        }
+        return dynamicGlobalConfiguration;
+    }
+
     public boolean isConfigCenterFirst() {
         return configCenterFirst;
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 14ede43..1145f9e 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -263,6 +263,8 @@ public interface CommonConstants {
 
     String REFERENCE_FILTER_KEY = "reference.filter";
 
+    String INVOCATION_INTERCEPTOR_KEY = "invocation.interceptor";
+
     String INVOKER_LISTENER_KEY = "invoker.listener";
 
     String DUBBO_VERSION_KEY = "dubbo";
@@ -306,6 +308,12 @@ public interface CommonConstants {
     String GENERIC_WITH_CLZ_KEY = "generic.include.class";
 
     /**
+     * Whether to cache locally, default is true
+     */
+    String REGISTRY_LOCAL_FILE_CACHE_ENABLED = "file.cache";
+
+
+    /**
      * The limit of callback service instances for one interface on every client
      */
     String CALLBACK_INSTANCES_LIMIT_KEY = "callbacks";
@@ -361,4 +369,9 @@ public interface CommonConstants {
 
     String QOS_STARTUP_PROBE_EXTENSION = "dubbo.application.startup-probe";
 
+    String REGISTRY_DELAY_NOTIFICATION_KEY = "delay-notification";
+
+    String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval";
+    String CACHE_CLEAR_WAITING_THRESHOLD = "dubbo.application.url.cache.clear.waiting";
+
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
index 3feeeb2..bd8f43c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
@@ -23,8 +23,6 @@ public interface RegistryConstants {
 
     String REGISTRY_CLUSTER_KEY = "REGISTRY_CLUSTER";
 
-    String REGISTRY_CLUSTER = "REGISTRY_CLUSTER";
-
     String REGISTRY_CLUSTER_TYPE_KEY = "registry-cluster-type";
 
     String REGISTRY_PROTOCOL = "registry";
@@ -45,6 +43,8 @@ public interface RegistryConstants {
 
     String CONFIGURATORS_CATEGORY = "configurators";
 
+    String ALL_CATEGORIES = "providers,configurators,routers";
+
     String DYNAMIC_CONFIGURATORS_CATEGORY = "dynamicconfigurators";
 
     String APP_DYNAMIC_CONFIGURATORS_CATEGORY = "appdynamicconfigurators";
@@ -59,10 +59,13 @@ public interface RegistryConstants {
 
     String COMPATIBLE_CONFIG_KEY = "compatible_config";
 
-    String REGISTRY_DUPLICATE_KEY = "duplicate";
+    String REGISTRY_PUBLISH_INTERFACE_KEY = "publish-interface";
+
+    String REGISTRY_PUBLISH_INSTANCE_KEY = "publish-instance";
 
-    String ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION = "enable-auto-migration";
+    String DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY = "dubbo.application.publish-interface";
 
+    String DUBBO_PUBLISH_INSTANCE_DEFAULT_KEY = "dubbo.application.publish-instance";
     /**
      * The parameter key of Dubbo Registry type
      *
@@ -114,4 +117,5 @@ public interface RegistryConstants {
     String ZONE_KEY = "zone";
 
     String REGISTRY_SERVICE_REFERENCE_PATH = "org.apache.dubbo.registry.RegistryService";
+    String INIT = "INIT";
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java
index ba1873e..7793500 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Activate.java
@@ -86,6 +86,8 @@ public @interface Activate {
     /**
      * Absolute ordering info, optional
      *
+     * Ascending order, smaller values will be in the front o the list.
+     *
      * @return absolute ordering info
      */
     int order() default 0;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
index 922b04a..26489e0 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
@@ -307,6 +307,21 @@ public class ExtensionLoader<T> {
         return activateExtensions;
     }
 
+    public List<T> getActivateExtensions() {
+        List<T> activateExtensions = new ArrayList<>();
+        getExtensionClasses();
+        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
+            String name = entry.getKey();
+            Object activate = entry.getValue();
+            if (!(activate instanceof Activate)) {
+                continue;
+            }
+            activateExtensions.add(getExtension(name));
+        }
+        activateExtensions.sort(ActivateComparator.COMPARATOR);
+        return activateExtensions;
+    }
+
     private boolean isMatchGroup(String group, String[] groups) {
         if (StringUtils.isEmpty(group)) {
             return true;
@@ -334,13 +349,12 @@ public class ExtensionLoader<T> {
                 keyValue = arr[1];
             }
 
-            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
-                String k = entry.getKey();
-                String v = entry.getValue();
-                if ((k.equals(key) || k.endsWith("." + key))
-                        && ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
-                    return true;
-                }
+            String realValue = url.getParameter(key);
+            if (StringUtils.isEmpty(realValue)) {
+                realValue = url.getAnyMethodParameter(key);
+            }
+            if ((keyValue != null && keyValue.equals(realValue)) || (keyValue == null && ConfigUtils.isNotEmpty(realValue))) {
+                return true;
             }
         }
         return false;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
index 1669089..b8eb41e 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
@@ -33,7 +33,6 @@ import java.util.concurrent.ThreadPoolExecutor;
 
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
 
 /**
@@ -50,8 +49,14 @@ public class DefaultExecutorRepository implements ExecutorRepository {
 
     private ScheduledExecutorService serviceExporterExecutor;
 
+    public ScheduledExecutorService registryNotificationExecutor;
+
     private ScheduledExecutorService reconnectScheduledExecutor;
 
+    private ScheduledExecutorService serviceDiscveryAddressNotificationExecutor;
+
+    private ScheduledExecutorService metadataRetryExecutor;
+
     private ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> data = new ConcurrentHashMap<>();
 
     public DefaultExecutorRepository() {
@@ -62,6 +67,9 @@ public class DefaultExecutorRepository implements ExecutorRepository {
 //
 //        reconnectScheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-reconnect-scheduler"));
         serviceExporterExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-exporter-scheduler"));
+        serviceDiscveryAddressNotificationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-SD-address-refresh"));
+        registryNotificationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-registry-notification"));
+        metadataRetryExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-metadata-retry"));
     }
 
     /**
@@ -72,7 +80,7 @@ public class DefaultExecutorRepository implements ExecutorRepository {
      */
     public synchronized ExecutorService createExecutorIfAbsent(URL url) {
         String componentKey = EXECUTOR_SERVICE_COMPONENT_KEY;
-        if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) {
+        if (CONSUMER_SIDE.equalsIgnoreCase(url.getSide())) {
             componentKey = CONSUMER_SIDE;
         }
         Map<Integer, ExecutorService> executors = data.computeIfAbsent(componentKey, k -> new ConcurrentHashMap<>());
@@ -89,7 +97,7 @@ public class DefaultExecutorRepository implements ExecutorRepository {
 
     public ExecutorService getExecutor(URL url) {
         String componentKey = EXECUTOR_SERVICE_COMPONENT_KEY;
-        if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) {
+        if (CONSUMER_SIDE.equalsIgnoreCase(url.getSide())) {
             componentKey = CONSUMER_SIDE;
         }
         Map<Integer, ExecutorService> executors = data.get(componentKey);
@@ -155,6 +163,20 @@ public class DefaultExecutorRepository implements ExecutorRepository {
     }
 
     @Override
+    public ScheduledExecutorService getRegistryNotificationExecutor() {
+        return registryNotificationExecutor;
+    }
+
+    public ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor() {
+        return serviceDiscveryAddressNotificationExecutor;
+    }
+
+    @Override
+    public ScheduledExecutorService getMetadataRetryExecutor() {
+        return metadataRetryExecutor;
+    }
+
+    @Override
     public ExecutorService getSharedExecutor() {
         return SHARED_EXECUTOR;
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
index af3b110..d00a7bb 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java
@@ -57,6 +57,18 @@ public interface ExecutorRepository {
 
     ScheduledExecutorService getServiceExporterExecutor();
 
+    ScheduledExecutorService getServiceDiscoveryAddressNotificationExecutor();
+
+    ScheduledExecutorService getMetadataRetryExecutor();
+
+    /**
+     * Scheduled executor handle registry notification.
+     *
+     * @return
+     */
+    ScheduledExecutorService getRegistryNotificationExecutor();
+
+
     /**
      * Get the default shared threadpool.
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
new file mode 100644
index 0000000..fbb31b6
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/DubboServiceAddressURL.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.util.Map;
+import java.util.Objects;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
+
+public class DubboServiceAddressURL extends ServiceAddressURL {
+    public static final String[] PROVIDER_FIRST_KEYS = new String[]{RELEASE_KEY, DUBBO_VERSION_KEY, METHODS_KEY, TIMESTAMP_KEY, TAG_KEY};
+
+    public static DubboServiceAddressURL valueOf(String rawURL, URL consumerURL) {
+        return valueOf(rawURL, consumerURL, null);
+    }
+
+    public static DubboServiceAddressURL valueOf(String rawURL, URL consumerURL, ServiceConfigURL overriddenURL) {
+        URL url = valueOf(rawURL, true);
+        return new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, overriddenURL);
+    }
+
+    private ServiceConfigURL overrideURL;
+
+    public DubboServiceAddressURL(URLAddress urlAddress, URLParam urlParam, URL consumerURL, ServiceConfigURL overrideURL) {
+        super(urlAddress, urlParam, consumerURL);
+        this.overrideURL = overrideURL;
+    }
+
+    protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
+        return (T) new DubboServiceAddressURL(urlAddress, urlParam, this.consumerURL, this.overrideURL);
+    }
+
+    @Override
+    public String getSide() {
+        return consumerURL.getParameter(SIDE_KEY);
+    }
+
+    @Override
+    public String getParameter(String key) {
+        String value = null;
+        if (overrideURL != null) {
+            value = overrideURL.getParameter(key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getParameter(key);
+        }
+        return value;
+    }
+
+    @Override
+    public String getMethodParameter(String method, String key) {
+        String value = null;
+        if (overrideURL != null) {
+            value = overrideURL.getMethodParameterStrict(method, key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getMethodParameter(method, key);
+        }
+        return value;
+    }
+
+    @Override
+    public String getAnyMethodParameter(String key) {
+        String value = null;
+        if (overrideURL != null) {
+            value = overrideURL.getAnyMethodParameter(key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getAnyMethodParameter(key);
+        }
+        return value;
+    }
+
+    public ServiceConfigURL getOverrideURL() {
+        return overrideURL;
+    }
+
+    public void setOverrideURL(ServiceConfigURL overrideURL) {
+        this.overrideURL = overrideURL;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        return prime * super.hashCode() + (overrideURL == null ? 0 : overrideURL.hashCode());
+    }
+
+    /**
+     * ignore consumer url compare.
+     * It's only meaningful for comparing two AddressURLs related to the same consumerURL.
+     *
+     * @param obj
+     * @return
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DubboServiceAddressURL)) {
+            return false;
+        }
+        if (overrideURL == null) {
+            return super.equals(obj);
+        } else {
+            DubboServiceAddressURL other = (DubboServiceAddressURL) obj;
+            boolean overrideEquals = Objects.equals(overrideURL.getParameters(), other.getOverrideURL().getParameters());
+            if (!overrideEquals) {
+                return false;
+            }
+
+            Map<String, String> params = this.getParameters();
+            for (Map.Entry<String, String> entry : params.entrySet()) {
+                String key = entry.getKey();
+                if (overrideURL.getParameters().containsKey(key)) {
+                    continue;
+                }
+                if (!entry.getValue().equals(other.getUrlParam().getParameter(key))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/PathURLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/PathURLAddress.java
new file mode 100644
index 0000000..f723991
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/PathURLAddress.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.util.Objects;
+
+public class PathURLAddress extends URLAddress {
+    private String protocol;
+    private String username;
+    private String password;
+    private String path;
+
+    private transient String address;
+    private transient String ip;
+
+    public PathURLAddress(String protocol, String username, String password, String path, String host, int port) {
+        this(protocol, username, password, path, host, port, null);
+    }
+
+    public PathURLAddress(String protocol, String username, String password, String path, String host, int port, String rawAddress) {
+        super(host, port, rawAddress);
+
+        this.protocol = protocol;
+        this.username = username;
+        this.password = password;
+
+        // trim the beginning "/"
+        while (path != null && path.startsWith("/")) {
+            path = path.substring(1);
+        }
+        this.path = path;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public URLAddress setProtocol(String protocol) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public URLAddress setUsername(String username) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public PathURLAddress setPassword(String password) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public PathURLAddress setPath(String path) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    @Override
+    public URLAddress setHost(String host) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    @Override
+    public URLAddress setPort(int port) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    @Override
+    public URLAddress setAddress(String host, int port) {
+        return new PathURLAddress(protocol, username, password, path, host, port, rawAddress);
+    }
+
+    public String getAddress() {
+        if (address == null) {
+            address = getAddress(getHost(), getPort());
+        }
+        return address;
+    }
+
+    /**
+     * Fetch IP address for this URL.
+     * <p>
+     * Pls. note that IP should be used instead of Host when to compare with socket's address or to search in a map
+     * which use address as its key.
+     *
+     * @return ip in string format
+     */
+    public String getIp() {
+        if (ip == null) {
+            ip = NetUtils.getIpByHost(getHost());
+        }
+        return ip;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(protocol, username, password, path, host, port);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof URLAddress)) return false;
+        URLAddress that = (URLAddress) obj;
+        return Objects.equals(this.getProtocol(), that.getProtocol()) &&
+                Objects.equals(this.getUsername(), that.getUsername()) &&
+                Objects.equals(this.getPassword(), that.getPassword()) &&
+                Objects.equals(this.getPath(), that.getPath()) &&
+                Objects.equals(this.getHost(), that.getHost()) &&
+                Objects.equals(this.getPort(), that.getPort());
+    }
+
+    @Override
+    public String toString() {
+        if (rawAddress != null) {
+            return rawAddress;
+        }
+
+        StringBuilder buf = new StringBuilder();
+        if (StringUtils.isNotEmpty(protocol)) {
+            buf.append(protocol);
+            buf.append("://");
+        }
+//
+//        if (StringUtils.isNotEmpty(username)) {
+//            buf.append(username);
+//            if (StringUtils.isNotEmpty(password)) {
+//                buf.append(":");
+//                buf.append(password);
+//            }
+//            buf.append("@");
+//        }
+
+        if (StringUtils.isNotEmpty(host)) {
+            buf.append(host);
+            if (port > 0) {
+                buf.append(":");
+                buf.append(port);
+            }
+        }
+
+        if (StringUtils.isNotEmpty(path)) {
+            buf.append("/");
+            buf.append(path);
+        }
+
+        return buf.toString();
+    }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceAddressURL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceAddressURL.java
new file mode 100644
index 0000000..91c8412
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceAddressURL.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.common.url.component;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
+
+public abstract class ServiceAddressURL extends URL {
+    protected final transient URL consumerURL;
+
+    //cache
+    private transient Map<String, String> concatenatedPrams;
+//    private transient Map<String, String> allParameters;
+
+    public ServiceAddressURL(
+            String protocol,
+            String username,
+            String password,
+            String host,
+            int port,
+            String path,
+            Map<String, String> parameters,
+            URL consumerURL
+    ) {
+        super(protocol, username, password, host, port, path, parameters);
+        this.consumerURL = consumerURL;
+    }
+
+    public ServiceAddressURL(URLAddress urlAddress, URLParam urlParam, URL consumerURL){
+        super(urlAddress, urlParam);
+        this.consumerURL = consumerURL;
+    }
+
+    @Override
+    public String getPath() {
+        return consumerURL.getPath();
+    }
+
+    @Override
+    public String getServiceInterface() {
+        return consumerURL.getServiceInterface();
+    }
+
+    @Override
+    public String getApplication() {
+        return consumerURL.getApplication();
+    }
+
+    @Override
+    public String getRemoteApplication() {
+        return super.getParameter(APPLICATION_KEY);
+    }
+
+    @Override
+    public String getGroup() {
+        String group = super.getParameter(GROUP_KEY);
+        if (StringUtils.isNotEmpty(group)) {
+            return group;
+        }
+        return consumerURL.getGroup();
+    }
+
+    @Override
+    public String getVersion() {
+        String version = super.getParameter(VERSION_KEY);
+        if (StringUtils.isNotEmpty(version)) {
+            return version;
+        }
+        return consumerURL.getVersion();
+    }
+
+    /**
+     * FIXME, Avoid calling this method on the main line.
+     */
+//    @Override
+//    public Map<String, String> getParameters() {
+//        Map<String, String> allParameters = new HashMap<>((int)(super.getParameters().size()/.75 + 1));
+//        allParameters.putAll(super.getParameters());
+//        if (consumerURL != null) {
+//            allParameters.putAll(consumerURL.getParameters());
+//        }
+//        if (overriddenURL != null) {
+//            allParameters.putAll(overriddenURL.getParameters());
+//        }
+//        allParameters.remove(CATEGORY_KEY);
+//        return Collections.unmodifiableMap(allParameters);
+//    }
+
+    @Override
+    public String getParameter(String key) {
+        String value = null;
+        if (consumerURL != null) {
+            value = consumerURL.getParameter(key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getParameter(key);
+        }
+        return value;
+    }
+
+    @Override
+    public String getMethodParameter(String method, String key) {
+        String value = null;
+        if (consumerURL != null) {
+            value = consumerURL.getMethodParameterStrict(method, key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getMethodParameterStrict(method, key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = getParameter(key);
+        }
+        return value;
+    }
+
+    @Override
+    public String getAnyMethodParameter(String key) {
+        String value = null;
+        if (consumerURL != null) {
+            value = consumerURL.getAnyMethodParameter(key);
+        }
+        if (StringUtils.isEmpty(value)) {
+            value = super.getAnyMethodParameter(key);
+        }
+        return value;
+    }
+
+    @Override
+    public String getConcatenatedParameter(String key) {
+        if (concatenatedPrams == null) {
+            concatenatedPrams = new HashMap<>(1);
+        }
+        String value = concatenatedPrams.get(key);
+        if (StringUtils.isNotEmpty(value)) {
+            return value;
+        }
+
+        // Combine filters and listeners on Provider and Consumer
+        String remoteValue = super.getParameter(key);
+        String localValue = consumerURL.getParameter(key);
+        if (remoteValue != null && remoteValue.length() > 0
+                && localValue != null && localValue.length() > 0) {
+            value = remoteValue + "," + localValue;
+            concatenatedPrams.put(key, value);
+            return value;
+        }
+        if (localValue != null && localValue.length() > 0) {
+            value = localValue;
+        } else if (remoteValue != null && remoteValue.length() > 0) {
+            value = remoteValue;
+        }
+        concatenatedPrams.put(key, value);
+        return value;
+    }
+
+    @Override
+    public String getCategory() {
+        return PROVIDERS_CATEGORY;
+    }
+
+    @Override
+    public String getSide() {
+        return CONSUMER_SIDE;
+    }
+
+    public URL getConsumerURL() {
+        return consumerURL;
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ;
+    }
+
+    /**
+     * ignore consumer url compare.
+     * It's only meaningful for comparing two address urls related to the same consumerURL.
+     *
+     * @param obj
+     * @return
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof ServiceAddressURL)) {
+            return false;
+        }
+        return super.equals(obj);
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
new file mode 100644
index 0000000..2bbb062
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/ServiceConfigURL.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.URL;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ServiceConfigURL extends URL {
+    private final Map<String, Object> attributes;
+
+    private volatile transient String full;
+    private volatile transient String string;
+    private volatile transient String identity;
+    private volatile transient String parameter;
+
+    public ServiceConfigURL() {
+        this.attributes = null;
+    }
+
+    public ServiceConfigURL(URLAddress urlAddress, URLParam urlParam, Map<String, Object> attributes) {
+        super(urlAddress, urlParam);
+        this.attributes = (attributes != null ? attributes : new HashMap<>());
+    }
+
+    public ServiceConfigURL(String protocol,
+               String username,
+               String password,
+               String host,
+               int port,
+               String path,
+               Map<String, String> parameters) {
+        this(new PathURLAddress(protocol, username, password, path, host, port), new URLParam(parameters), null);
+    }
+
+    public ServiceConfigURL(String protocol,
+                            String username,
+                            String password,
+                            String host,
+                            int port,
+                            String path,
+                            Map<String, String> parameters,
+                            Map<String, Object> attributes) {
+        this(new PathURLAddress(protocol, username, password, path, host, port), new URLParam(parameters), attributes);
+    }
+
+    protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
+        return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public Object getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+    @Override
+    public URL addAttributes(Map<String, Object> attributes) {
+        Map<String, Object> newAttributes = new HashMap<>();
+        newAttributes.putAll(this.attributes);
+        newAttributes.putAll(attributes);
+
+        return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
+    }
+
+    public ServiceConfigURL putAttribute(String key, Object obj) {
+        Map<String, Object> newAttributes = new HashMap<>(attributes);
+        newAttributes.put(key, obj);
+
+        return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
+    }
+
+    @Override
+    public URL removeAttribute(String key) {
+        Map<String, Object> newAttributes = new HashMap<>(attributes);
+        newAttributes.remove(key);
+
+        return new ServiceConfigURL(getUrlAddress(), getUrlParam(), newAttributes);
+    }
+
+    @Override
+    public boolean hasAttribute(String key) {
+        return getAttribute(key) != null;
+    }
+
+    @Override
+    public String toString() {
+        if (string != null) {
+            return string;
+        }
+        return string = super.toString();
+    }
+
+    @Override
+    public String toFullString() {
+        if (full != null) {
+            return full;
+        }
+        return full = super.toFullString();
+    }
+
+    @Override
+    public String toIdentityString() {
+        if (identity != null) {
+            return identity;
+        }
+        return identity = super.toIdentityString();
+    }
+
+    @Override
+    public String toParameterString() {
+        if (parameter != null) {
+            return parameter;
+        }
+        return parameter = super.toParameterString();
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
new file mode 100644
index 0000000..614862a
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLAddress.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Objects;
+
+public class URLAddress implements Serializable {
+    private static final long serialVersionUID = -1985165475234910535L;
+
+    protected String host;
+    protected int port;
+
+    // cache
+    protected transient String rawAddress;
+    protected transient long timestamp;
+
+    public URLAddress(String host, int port) {
+        this(host, port, null);
+    }
+
+    public URLAddress(String host, int port, String rawAddress) {
+        this.host = host;
+        port = Math.max(port, 0);
+        this.port = port;
+
+        this.rawAddress = rawAddress;
+        this.timestamp = System.currentTimeMillis();
+    }
+
+    public String getProtocol() {
+        return "";
+    }
+
+    public URLAddress setProtocol(String protocol) {
+        return this;
+    }
+
+    public String getUsername() {
+        return "";
+    }
+
+    public URLAddress setUsername(String username) {
+        return this;
+    }
+
+    public String getPassword() {
+        return "";
+    }
+
+    public URLAddress setPassword(String password) {
+        return this;
+    }
+
+    public String getPath() {
+        return "";
+    }
+
+    public URLAddress setPath(String path) {
+        return this;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public URLAddress setHost(String host) {
+        return new URLAddress(host, port, rawAddress);
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public URLAddress setPort(int port) {
+        return new URLAddress(host, port, rawAddress);
+    }
+
+    public String getAddress() {
+        if (rawAddress == null) {
+            rawAddress = getAddress(getHost(), getPort());
+        }
+        return rawAddress;
+    }
+
+    public URLAddress setAddress(String host, int port) {
+        return new URLAddress(host, port, rawAddress);
+    }
+
+    public String getIp() {
+        return NetUtils.getIpByHost(getHost());
+    }
+
+    public String getRawAddress() {
+        return rawAddress;
+    }
+
+    protected String getAddress(String host, int port) {
+        return port <= 0 ? host : host + ':' + port;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(host, port);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof URLAddress)) return false;
+        URLAddress that = (URLAddress) obj;
+        return Objects.equals(this.getProtocol(), that.getProtocol()) &&
+                Objects.equals(this.getUsername(), that.getUsername()) &&
+                Objects.equals(this.getPassword(), that.getPassword()) &&
+                Objects.equals(this.getPath(), that.getPath()) &&
+                Objects.equals(this.getHost(), that.getHost()) &&
+                Objects.equals(this.getPort(), that.getPort());
+    }
+
+    @Override
+    public String toString() {
+        if (rawAddress != null) {
+            return rawAddress;
+        }
+
+        StringBuilder buf = new StringBuilder();
+        if (StringUtils.isNotEmpty(host)) {
+            buf.append(host);
+            if (port > 0) {
+                buf.append(":");
+                buf.append(port);
+            }
+        }
+        return buf.toString();
+    }
+
+    public static URLAddress parse(String rawAddress, String defaultProtocol, boolean encoded) {
+        try {
+            if (encoded) {
+                rawAddress = URLDecoder.decode(rawAddress, "UTF-8");
+            }
+
+            boolean isPathAddress = !Character.isDigit(rawAddress.charAt(0));
+            if (isPathAddress) {
+                return createPathURLAddress(rawAddress, defaultProtocol);
+            }
+            return createURLAddress(rawAddress, defaultProtocol);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    private static URLAddress createURLAddress(String rawAddress, String defaultProtocol) {
+        String host = null;
+        int port = 0;
+
+        int i = rawAddress.lastIndexOf(':');
+        if (i >= 0 && i < rawAddress.length() - 1) {
+            if (rawAddress.lastIndexOf('%') > i) {
+                // ipv6 address with scope id
+                // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
+                // see https://howdoesinternetwork.com/2013/ipv6-zone-id
+                // ignore
+            } else {
+                port = Integer.parseInt(rawAddress.substring(i + 1));
+                host = rawAddress.substring(0, i);
+            }
+        } else {
+            host = rawAddress;
+        }
+
+        return new URLAddress(host, port, rawAddress);
+    }
+
+    private static PathURLAddress createPathURLAddress(String rawAddress, String defaultProtocol) {
+        String protocol = defaultProtocol;
+        String copyOfRawAddress = rawAddress;
+        String path = null, username = null, password = null, host = null;
+        int port = 0;
+        int i = rawAddress.indexOf("://");
+        if (i >= 0) {
+            if (i == 0) {
+                throw new IllegalStateException("url missing protocol: \"" + rawAddress + "\"");
+            }
+            protocol = rawAddress.substring(0, i);
+            rawAddress = rawAddress.substring(i + 3);
+        } else {
+            // case: file:/path/to/file.txt
+            i = rawAddress.indexOf(":/");
+            if (i >= 0) {
+                if (i == 0) {
+                    throw new IllegalStateException("url missing protocol: \"" + rawAddress + "\"");
+                }
+                protocol = rawAddress.substring(0, i);
+                rawAddress = rawAddress.substring(i + 1);
+            }
+        }
+
+        i = rawAddress.indexOf('/');
+        if (i >= 0) {
+            path = rawAddress.substring(i + 1);
+            rawAddress = rawAddress.substring(0, i);
+        }
+        i = rawAddress.lastIndexOf('@');
+        if (i >= 0) {
+            username = rawAddress.substring(0, i);
+            int j = username.indexOf(':');
+            if (j >= 0) {
+                password = username.substring(j + 1);
+                username = username.substring(0, j);
+            }
+            rawAddress = rawAddress.substring(i + 1);
+        }
+        i = rawAddress.lastIndexOf(':');
+        if (i >= 0 && i < rawAddress.length() - 1) {
+            if (rawAddress.lastIndexOf('%') > i) {
+                // ipv6 address with scope id
+                // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
+                // see https://howdoesinternetwork.com/2013/ipv6-zone-id
+                // ignore
+            } else {
+                port = Integer.parseInt(rawAddress.substring(i + 1));
+                host = rawAddress.substring(0, i);
+            }
+        }
+
+        // check cache
+        protocol = URLItemCache.checkProtocol(protocol);
+        path = URLItemCache.checkPath(path);
+
+        return new PathURLAddress(protocol, username, password, path, host, port, copyOfRawAddress);
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
new file mode 100644
index 0000000..2384493
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLItemCache.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.utils.LRUCache;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class URLItemCache {
+    // thread safe with limited size, by default 1000
+    private static final Map<String, String> PARAM_KEY_CACHE = new LRUCache<>(10000);
+    private static final Map<String, String> PARAM_VALUE_CACHE = new LRUCache<>(100000);
+    private static final Map<String, String> PATH_CACHE = new LRUCache<>(10000);
+    private static final Map<String, String> PROTOCOL_CACHE = new ConcurrentHashMap<>();
+
+    public static void putParams(Map<String, String> params, String key, String value) {
+        String cachedKey = PARAM_KEY_CACHE.get(key);
+        if (cachedKey == null) {
+            cachedKey = key;
+            PARAM_KEY_CACHE.put(key, key);
+        }
+        String cachedValue = PARAM_VALUE_CACHE.get(value);
+        if (cachedValue == null) {
+            cachedValue = value;
+            PARAM_VALUE_CACHE.put(value, value);
+        }
+
+        params.put(cachedKey, cachedValue);
+    }
+
+    public static String checkProtocol(String _protocol) {
+        if (_protocol == null) {
+            return _protocol;
+        }
+        String cachedProtocol = PROTOCOL_CACHE.putIfAbsent(_protocol, _protocol);
+        if (cachedProtocol != null) {
+            return cachedProtocol;
+        }
+        return _protocol;
+    }
+
+    public static String checkPath(String _path) {
+        if (_path == null) {
+            return _path;
+        }
+        String cachedPath = PATH_CACHE.putIfAbsent(_path, _path);
+        if (cachedPath != null) {
+            return cachedPath;
+        }
+        return _path;
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
new file mode 100644
index 0000000..b2dca0c
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.url.component;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLStrParser;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
+import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
+
+public class URLParam implements Serializable {
+    private static final long serialVersionUID = -1985165475234910535L;
+
+    private final String rawParam;
+    private final Map<String, String> params;
+
+    //cache
+    private transient Map<String, Map<String, String>> methodParameters;
+    private transient long timestamp;
+
+    public URLParam(Map<String, String> params) {
+        this(params, null);
+    }
+
+    public URLParam(Map<String, String> params, String rawParam) {
+        this.params = Collections.unmodifiableMap((params == null ? new HashMap<>() : new HashMap<>(params)));
+        this.rawParam = rawParam;
+
+        this.timestamp = System.currentTimeMillis();
+    }
+
+    public Map<String, Map<String, String>> getMethodParameters() {
+        if (methodParameters == null) {
+            methodParameters = Collections.unmodifiableMap(initMethodParameters(this.params));
+        }
+        return methodParameters;
+    }
+
+    public static Map<String, Map<String, String>> initMethodParameters(Map<String, String> parameters) {
+        Map<String, Map<String, String>> methodParameters = new HashMap<>();
+        if (parameters == null) {
+            return methodParameters;
+        }
+
+        String methodsString = parameters.get(METHODS_KEY);
+        if (StringUtils.isNotEmpty(methodsString)) {
+            String[] methods = methodsString.split(",");
+            for (Map.Entry<String, String> entry : parameters.entrySet()) {
+                String key = entry.getKey();
+                for (String method : methods) {
+                    String methodPrefix = method + '.';
+                    if (key.startsWith(methodPrefix)) {
+                        String realKey = key.substring(methodPrefix.length());
+                        URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
+                    }
+                }
+            }
+        } else {
+            for (Map.Entry<String, String> entry : parameters.entrySet()) {
+                String key = entry.getKey();
+                int methodSeparator = key.indexOf('.');
+                if (methodSeparator > 0) {
+                    String method = key.substring(0, methodSeparator);
+                    String realKey = key.substring(methodSeparator + 1);
+                    URL.putMethodParameter(method, realKey, entry.getValue(), methodParameters);
+                }
+            }
+        }
+        return methodParameters;
+    }
+
+    public Map<String, String> getParameters() {
+        return params;
+    }
+
+    public URLParam addParameter(String key, String value) {
+        if (StringUtils.isEmpty(key)
+                || StringUtils.isEmpty(value)) {
+            return this;
+        }
+        // if value doesn't change, return immediately
+        if (value.equals(getParameters().get(key))) { // value != null
+            return this;
+        }
+
+        Map<String, String> map = new HashMap<>(getParameters());
+        map.put(key, value);
+        return new URLParam(map, rawParam);
+    }
+
+    public URLParam addParameterIfAbsent(String key, String value) {
+        if (StringUtils.isEmpty(key)
+                || StringUtils.isEmpty(value)) {
+            return this;
+        }
+        if (hasParameter(key)) {
+            return this;
+        }
+
+        Map<String, String> map = new HashMap<>(getParameters());
+        map.put(key, value);
+
+        return new URLParam(map, rawParam);
+    }
+
+    /**
+     * Add parameters to a new url.
+     *
+     * @param parameters parameters in key-value pairs
+     * @return A new URL
+     */
+    public URLParam addParameters(Map<String, String> parameters) {
+        if (CollectionUtils.isEmptyMap(parameters)) {
+            return this;
+        }
+
+        boolean hasAndEqual = true;
+        for (Map.Entry<String, String> entry : parameters.entrySet()) {
+            String value = getParameters().get(entry.getKey());
+            if (value == null) {
+                if (entry.getValue() != null) {
+                    hasAndEqual = false;
+                    break;
+                }
+            } else {
+                if (!value.equals(entry.getValue())) {
+                    hasAndEqual = false;
+                    break;
+                }
+            }
+        }
+        // return immediately if there's no change
+        if (hasAndEqual) {
+            return this;
+        }
+
+        Map<String, String> map = new HashMap<>((int)(getParameters().size() + parameters.size() / 0.75f) + 1);
+        map.putAll(getParameters());
+        map.putAll(parameters);
+        return new URLParam(map, rawParam);
+    }
+
+    public URLParam addParametersIfAbsent(Map<String, String> parameters) {
+        if (CollectionUtils.isEmptyMap(parameters)) {
+            return this;
+        }
+
+        Map<String, String> map = new HashMap<>((int)(getParameters().size() + parameters.size() / 0.75f) + 1);
+        map.putAll(parameters);
+        map.putAll(getParameters());
+        return new URLParam(map, rawParam);
+    }
+
+    public URLParam removeParameters(String... keys) {
+        if (keys == null || keys.length == 0) {
+            return this;
+        }
+
+        Map<String, String> map = new HashMap<>(getParameters());
+        for (String key : keys) {
+            map.remove(key);
+        }
+        if (map.size() == getParameters().size()) {
+            return this;
+        }
+        return new URLParam(map, rawParam);
+    }
+
+    public URLParam clearParameters() {
+        return new URLParam(new HashMap<>());
+    }
+
+    public boolean hasParameter(String key) {
+        String value = getParameter(key);
+        return value != null && value.length() > 0;
+    }
+
+    public String getParameter(String key) {
+        return params.get(key);
+    }
+
+    public String getRawParam() {
+        return rawParam;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(params);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof URLParam)) return false;
+        URLParam that = (URLParam) obj;
+        return Objects.equals(this.getParameters(), that.getParameters());
+    }
+
+    @Override
+    public String toString() {
+        if (StringUtils.isNotEmpty(rawParam)) {
+            return rawParam;
+        }
+        if (params == null) {
+            return "";
+        }
+
+        StringBuilder buf = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : new TreeMap<>(params).entrySet()) {
+            if (StringUtils.isNotEmpty(entry.getKey())) {
+                if (first) {
+                    first = false;
+                } else {
+                    buf.append("&");
+                }
+                buf.append(entry.getKey());
+                buf.append("=");
+                buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
+            }
+        }
+        return buf.toString();
+    }
+
+    public static URLParam parse(String rawParam, boolean encoded, Map<String, String> extraParameters) {
+        Map<String, String> parameters = URLStrParser.parseParams(rawParam, encoded);
+        if (CollectionUtils.isNotEmptyMap(extraParameters)) {
+            parameters.putAll(extraParameters);
+        }
+        return new URLParam(parameters, rawParam);
+    }
+
+    public static URLParam parse(String rawParam) {
+        String[] parts = rawParam.split("&");
+        Map<String, String> parameters = new HashMap<>((int) (parts.length/.75f) + 1);
+        for (String part : parts) {
+            part = part.trim();
+            if (part.length() > 0) {
+                int j = part.indexOf('=');
+                if (j >= 0) {
+                    String key = part.substring(0, j);
+                    String value = part.substring(j + 1);
+                    parameters.put(key, value);
+                    // compatible with lower versions registering "default." keys
+                    if (key.startsWith(DEFAULT_KEY_PREFIX)) {
+                        parameters.putIfAbsent(key.substring(DEFAULT_KEY_PREFIX.length()), value);
+                    }
+                } else {
+                    parameters.put(part, part);
+                }
+            }
+        }
+        return new URLParam(parameters, rawParam);
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index d45ada1..eb645f3 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.common.utils;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLStrParser;
 import org.apache.dubbo.common.constants.RemotingConstants;
 
 import java.util.ArrayList;
@@ -31,6 +32,7 @@ import static java.util.Collections.emptyMap;
 import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.CLASSIFIER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
@@ -102,7 +104,7 @@ public class UrlUtils {
             defaultParameters.remove(PORT_KEY);
             defaultParameters.remove(PATH_KEY);
         }
-        URL u = URL.valueOf(url);
+        URL u = URL.cacheableValueOf(url);
         boolean changed = false;
         String protocol = u.getProtocol();
         String username = u.getUsername();
@@ -400,8 +402,8 @@ public class UrlUtils {
             return false;
         }
 
-        if (!isMatchCategory(providerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY),
-                consumerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY))) {
+        if (!isMatchCategory(providerUrl.getCategory(DEFAULT_CATEGORY),
+                consumerUrl.getCategory(DEFAULT_CATEGORY))) {
             return false;
         }
         if (!providerUrl.getParameter(ENABLED_KEY, true)
@@ -409,12 +411,12 @@ public class UrlUtils {
             return false;
         }
 
-        String consumerGroup = consumerUrl.getParameter(GROUP_KEY);
-        String consumerVersion = consumerUrl.getParameter(VERSION_KEY);
+        String consumerGroup = consumerUrl.getGroup();
+        String consumerVersion = consumerUrl.getVersion();
         String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
 
-        String providerGroup = providerUrl.getParameter(GROUP_KEY);
-        String providerVersion = providerUrl.getParameter(VERSION_KEY);
+        String providerGroup = providerUrl.getGroup();
+        String providerVersion = providerUrl.getVersion();
         String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
         return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
                 && (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
@@ -463,10 +465,10 @@ public class UrlUtils {
     public static boolean isServiceKeyMatch(URL pattern, URL value) {
         return pattern.getParameter(INTERFACE_KEY).equals(
                 value.getParameter(INTERFACE_KEY))
-                && isItemMatch(pattern.getParameter(GROUP_KEY),
-                value.getParameter(GROUP_KEY))
-                && isItemMatch(pattern.getParameter(VERSION_KEY),
-                value.getParameter(VERSION_KEY));
+                && isItemMatch(pattern.getGroup(),
+                value.getGroup())
+                && isItemMatch(pattern.getVersion(),
+                value.getVersion());
     }
 
     public static List<URL> classifyUrls(List<URL> urls, Predicate<URL> predicate) {
@@ -475,22 +477,24 @@ public class UrlUtils {
 
     public static boolean isConfigurator(URL url) {
         return OVERRIDE_PROTOCOL.equals(url.getProtocol()) ||
-                CONFIGURATORS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY));
+                CONFIGURATORS_CATEGORY.equals(url.getCategory(DEFAULT_CATEGORY));
     }
 
     public static boolean isRoute(URL url) {
         return ROUTE_PROTOCOL.equals(url.getProtocol()) ||
-                ROUTERS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY));
+                ROUTERS_CATEGORY.equals(url.getCategory(DEFAULT_CATEGORY));
     }
 
     public static boolean isProvider(URL url) {
         return !OVERRIDE_PROTOCOL.equals(url.getProtocol()) &&
                 !ROUTE_PROTOCOL.equals(url.getProtocol()) &&
-                PROVIDERS_CATEGORY.equals(url.getParameter(CATEGORY_KEY, PROVIDERS_CATEGORY));
+                PROVIDERS_CATEGORY.equals(url.getCategory(PROVIDERS_CATEGORY));
     }
 
     public static boolean isRegistry(URL url) {
-        return REGISTRY_PROTOCOL.equals(url.getProtocol()) || SERVICE_REGISTRY_PROTOCOL.equalsIgnoreCase(url.getProtocol());
+        return REGISTRY_PROTOCOL.equals(url.getProtocol())
+                || SERVICE_REGISTRY_PROTOCOL.equalsIgnoreCase(url.getProtocol())
+                || (url.getProtocol() != null && url.getProtocol().endsWith("-registry-protocol"));
     }
 
     /**
@@ -553,4 +557,105 @@ public class UrlUtils {
         arr[1] = serviceKey;
         return arr;
     }
+
+    /**
+     * NOTICE: This method allocate too much objects, we can use {@link URLStrParser#parseDecodedStr(String)} instead.
+     * <p>
+     * Parse url string
+     *
+     * @param url URL string
+     * @return URL instance
+     * @see URL
+     */
+    public static URL valueOf(String url) {
+        if (url == null || (url = url.trim()).length() == 0) {
+            throw new IllegalArgumentException("url == null");
+        }
+        String protocol = null;
+        String username = null;
+        String password = null;
+        String host = null;
+        int port = 0;
+        String path = null;
+        Map<String, String> parameters = null;
+        int i = url.indexOf('?'); // separator between body and parameters
+        if (i >= 0) {
+            String[] parts = url.substring(i + 1).split("&");
+            parameters = new HashMap<>();
+            for (String part : parts) {
+                part = part.trim();
+                if (part.length() > 0) {
+                    int j = part.indexOf('=');
+                    if (j >= 0) {
+                        String key = part.substring(0, j);
+                        String value = part.substring(j + 1);
+                        parameters.put(key, value);
+                        // compatible with lower versions registering "default." keys
+                        if (key.startsWith(DEFAULT_KEY_PREFIX)) {
+                            parameters.putIfAbsent(key.substring(DEFAULT_KEY_PREFIX.length()), value);
+                        }
+                    } else {
+                        parameters.put(part, part);
+                    }
+                }
+            }
+            url = url.substring(0, i);
+        }
+        i = url.indexOf("://");
+        if (i >= 0) {
+            if (i == 0) {
+                throw new IllegalStateException("url missing protocol: \"" + url + "\"");
+            }
+            protocol = url.substring(0, i);
+            url = url.substring(i + 3);
+        } else {
+            // case: file:/path/to/file.txt
+            i = url.indexOf(":/");
+            if (i >= 0) {
+                if (i == 0) {
+                    throw new IllegalStateException("url missing protocol: \"" + url + "\"");
+                }
+                protocol = url.substring(0, i);
+                url = url.substring(i + 1);
+            }
+        }
+
+        i = url.indexOf('/');
+        if (i >= 0) {
+            path = url.substring(i + 1);
+            url = url.substring(0, i);
+        }
+        i = url.lastIndexOf('@');
+        if (i >= 0) {
+            username = url.substring(0, i);
+            int j = username.indexOf(':');
+            if (j >= 0) {
+                password = username.substring(j + 1);
+                username = username.substring(0, j);
+            }
+            url = url.substring(i + 1);
+        }
+        i = url.lastIndexOf(':');
+        if (i >= 0 && i < url.length() - 1) {
+            if (url.lastIndexOf('%') > i) {
+                // ipv6 address with scope id
+                // e.g. fe80:0:0:0:894:aeec:f37d:23e1%en0
+                // see https://howdoesinternetwork.com/2013/ipv6-zone-id
+                // ignore
+            } else {
+                port = Integer.parseInt(url.substring(i + 1));
+                url = url.substring(0, i);
+            }
+        }
+        if (url.length() > 0) {
+            host = url;
+        }
+
+        return new URL(protocol, username, password, host, port, path, parameters);
+    }
+
+    public static boolean isConsumer(URL url) {
+        return url.getProtocol().equalsIgnoreCase("consumer") || url.getPort() == 0;
+    }
+
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index db2b583..4e7bbf4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@ -461,6 +461,7 @@ public abstract class AbstractConfig implements Serializable {
     }
 
     public void refresh() {
+        refreshed.set(true);
         Environment env = ApplicationModel.getEnvironment();
         try {
             CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
@@ -494,6 +495,11 @@ public abstract class AbstractConfig implements Serializable {
         }
     }
 
+    @Parameter(excluded = true)
+    public boolean isRefreshed() {
+        return refreshed.get();
+    }
+
     @Override
     public String toString() {
         try {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
index 970a6ae..4be28bf 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
@@ -451,6 +451,9 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
         this.layer = layer;
     }
 
+    /**
+     * Always use the global ApplicationConfig
+     */
     public ApplicationConfig getApplication() {
         if (application != null) {
             return application;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
index 52dea09..3ecef21 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
@@ -34,14 +34,17 @@ import java.util.Map;
 import java.util.Set;
 
 import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
 import static org.apache.dubbo.common.constants.CommonConstants.DUMP_DIRECTORY;
 import static org.apache.dubbo.common.constants.CommonConstants.HOST_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED;
 import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY;
 import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
 import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INTERFACE_KEY;
 import static org.apache.dubbo.config.Constants.DEVELOPMENT_ENVIRONMENT;
 import static org.apache.dubbo.config.Constants.PRODUCTION_ENVIRONMENT;
 import static org.apache.dubbo.config.Constants.TEST_ENVIRONMENT;
@@ -159,6 +162,16 @@ public class ApplicationConfig extends AbstractConfig {
 
     private String repository;
 
+    private Boolean enableFileCache;
+
+    private Boolean publishInterface;
+
+    /**
+     * The preferred protocol(name) of this application
+     * convenient for places where it's hard to determine which is the preferred protocol
+     */
+    private String protocol;
+
     /**
      * Metadata Service, used in Service Discovery
      */
@@ -459,6 +472,33 @@ public class ApplicationConfig extends AbstractConfig {
         this.repository = repository;
     }
 
+    @Parameter(key = REGISTRY_LOCAL_FILE_CACHE_ENABLED)
+    public Boolean getEnableFileCache() {
+        return enableFileCache;
+    }
+
+    public void setEnableFileCache(Boolean enableFileCache) {
+        this.enableFileCache = enableFileCache;
+    }
+
+    @Parameter(key = REGISTRY_PUBLISH_INTERFACE_KEY)
+    public Boolean getPublishInterface() {
+        return publishInterface;
+    }
+
+    public void setPublishInterface(Boolean publishInterface) {
+        this.publishInterface = publishInterface;
+    }
+
+    @Parameter(excluded = true, key="application-protocol")
+    public String getProtocol() {
+        return protocol == null ? DUBBO : protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
     @Parameter(key = "metadata-service-port")
     public Integer getMetadataServicePort() {
         return metadataServicePort;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
index 817bbc7..2999682 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
@@ -25,6 +25,8 @@ import java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INSTANCE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
 import static org.apache.dubbo.common.utils.PojoUtils.updatePropertyIfAbsent;
 import static org.apache.dubbo.config.Constants.REGISTRIES_SUFFIX;
@@ -182,6 +184,10 @@ public class RegistryConfig extends AbstractConfig {
      */
     private Integer weight;
 
+    private Boolean publishInterface;
+
+    private Boolean publishInstance;
+
     public RegistryConfig() {
     }
 
@@ -515,6 +521,24 @@ public class RegistryConfig extends AbstractConfig {
         this.weight = weight;
     }
 
+    @Parameter(key = REGISTRY_PUBLISH_INTERFACE_KEY)
+    public Boolean getPublishInterface() {
+        return publishInterface;
+    }
+
+    public void setPublishInterface(Boolean publishInterface) {
+        this.publishInterface = publishInterface;
+    }
+
+    @Parameter(key = REGISTRY_PUBLISH_INSTANCE_KEY)
+    public Boolean getPublishInstance() {
+        return publishInstance;
+    }
+
+    public void setPublishInstance(Boolean publishInstance) {
+        this.publishInstance = publishInstance;
+    }
+
     @Override
     public void refresh() {
         super.refresh();
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
index 218fef0..95c860f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
@@ -45,6 +45,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -71,7 +72,18 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
 
     final Map<String, Map<String, AbstractConfig>> configsCache = newMap();
 
+    private static volatile boolean configWarnLogEnabled = false;
+    private static Map<String, Boolean> warnLogStatus = new ConcurrentHashMap<>();
+
     public ConfigManager() {
+        try {
+            String rawWarn = System.getProperty("dubbo.application.config.warn");
+            if (rawWarn != null) {
+                configWarnLogEnabled = Boolean.parseBoolean(rawWarn);
+            }
+        } catch (Exception e) {
+            logger.warn("Illegal 'dubbo.application.config.warn'config, only boolean value is accepted.", e);
+        }
     }
 
     // ApplicationConfig correlative methods
@@ -430,7 +442,10 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
 //                throw new IllegalStateException("No such " + configType.getName() + " is found");
                 return null;
             } else if (size > 1) {
-                logger.warn("Expected single matching of " + configType + ", but found " + size + " instances, will randomly pick the first one.");
+                if (configWarnLogEnabled && warnLogStatus.get(configType) == null) {
+                    logger.warn("Expected single matching of " + configType + ", but found " + size + " instances, will randomly pick the first one.");
+                    warnLogStatus.put(configType, true);
+                }
             }
 
             return configsMap.values().iterator().next();
@@ -477,7 +492,9 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
     private static void checkDuplicate(AbstractConfig oldOne, AbstractConfig newOne) throws IllegalStateException {
         if (oldOne != null && !oldOne.equals(newOne)) {
             String configName = oldOne.getClass().getSimpleName();
-            logger.warn("Duplicate Config found for " + configName + ", you should use only one unique " + configName + " for one application.");
+            if (configWarnLogEnabled) {
+                logger.warn("Duplicate Config found for " + configName + ", only one unique " + configName + " is allowed for one application.");
+            }
         }
     }
 
@@ -503,10 +520,12 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
         C existedConfig = configsMap.get(key);
 
         if (existedConfig != null && !config.equals(existedConfig)) {
-            if (logger.isWarnEnabled()) {
-                String type = config.getClass().getSimpleName();
-                logger.warn(String.format("Duplicate %s found, there already has one default %s or more than two %ss have the same id, " +
-                        "you can try to give each %s a different id : %s", type, type, type, type, config));
+            if (configWarnLogEnabled) {
+                if (logger.isWarnEnabled()) {
+                    String type = config.getClass().getSimpleName();
+                    logger.warn(String.format("Duplicate %s found, there already has one default %s or more than two %ss have the same id, " +
+                            "you can try to give each %s a different id : %s", type, type, type, type, config));
+                }
             }
         } else {
             configsMap.put(key, config);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
index 6238b80..7400857 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.context.FrameworkExt;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.context.ConfigManager;
 
@@ -96,6 +97,10 @@ public class ApplicationModel {
         return (ServiceRepository) LOADER.getExtension(ServiceRepository.NAME);
     }
 
+    public static ExecutorRepository getExecutorRepository() {
+        return ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+    }
+
     public static ApplicationConfig getApplicationConfig() {
         return getConfigManager().getApplicationOrElseThrow();
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java
index 801132f..85920e2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/support/ProtocolUtils.java
@@ -27,8 +27,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZ
 import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_DEFAULT;
 import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA;
 import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_PROTOBUF;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 
 public class ProtocolUtils {
 
@@ -38,8 +36,8 @@ public class ProtocolUtils {
     }
 
     public static String serviceKey(URL url) {
-        return serviceKey(url.getPort(), url.getPath(), url.getParameter(VERSION_KEY),
-                url.getParameter(GROUP_KEY));
+        return serviceKey(url.getPort(), url.getPath(), url.getVersion(),
+                url.getGroup());
     }
 
     public static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/InterfaceAddressURLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/InterfaceAddressURLTest.java
new file mode 100644
index 0000000..8aad037
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/InterfaceAddressURLTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common;
+
+import org.apache.dubbo.common.url.component.DubboServiceAddressURL;
+import org.apache.dubbo.common.url.component.ServiceAddressURL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+
+public class InterfaceAddressURLTest {
+    private static final String rawURL = "dubbo://10.20.130.230:20880/context/path?version=1.0.0&group=g1&application=provider&timeout=1000&category=provider&side=provider&sayHello.weight=222";
+    private static final URL overrideURL = URL.valueOf("override://10.20.130.230:20880/context/path?version=1.0.0&application=morgan&timeout=2000&category=configurators&sayHello.overrideKey=override");
+    private static final URL consumerURL = URL.valueOf("consumer://10.20.130.230/context/path?version=2.0.0,1.0.0&group=g2&application=morgan&timeout=3000&side=consumer&sayHello.timeout=5000");
+
+    @Test
+    public void testMergeOverriden() {
+        URL url = URL.valueOf(rawURL);
+        ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), null, null);
+        assertEquals("1000", interfaceAddressURL.getParameter(TIMEOUT_KEY));
+
+        ServiceAddressURL withConsumer = DubboServiceAddressURL.valueOf(rawURL, consumerURL);
+        assertEquals("3000", withConsumer.getParameter(TIMEOUT_KEY));
+
+        ServiceAddressURL withOverriden = DubboServiceAddressURL.valueOf(rawURL, consumerURL, (ServiceConfigURL) overrideURL);
+        assertEquals("2000", withOverriden.getParameter(TIMEOUT_KEY));
+    }
+
+    @Test
+    public void testGetParameter() {
+        URL url = URL.valueOf(rawURL);
+        ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, null);
+
+        assertEquals("3000", interfaceAddressURL.getParameter(TIMEOUT_KEY));
+        assertNotEquals("1.0.0", interfaceAddressURL.getVersion());
+
+        assertEquals("morgan", interfaceAddressURL.getApplication());
+        assertEquals("provider", interfaceAddressURL.getRemoteApplication());
+
+        assertEquals("dubbo", interfaceAddressURL.getProtocol());
+        assertEquals("context/path", interfaceAddressURL.getPath());
+
+        assertEquals("consumer", interfaceAddressURL.getSide());
+        assertEquals("1.0.0", interfaceAddressURL.getVersion());
+        assertEquals("g1", interfaceAddressURL.getGroup());
+    }
+
+    @Test
+    public void testGetMethodParameter() {
+        URL url = URL.valueOf(rawURL);
+        ServiceAddressURL interfaceAddressURL = new DubboServiceAddressURL(url.getUrlAddress(), url.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL);
+
+        assertEquals("5000", interfaceAddressURL.getMethodParameter("sayHello", TIMEOUT_KEY));
+        assertEquals("2000", interfaceAddressURL.getMethodParameter("non-exist-methods", TIMEOUT_KEY));
+        assertEquals("222", interfaceAddressURL.getMethodParameter("sayHello", "weight"));
+        assertEquals("222", interfaceAddressURL.getMethodParameter("sayHello", "weight"));
+        assertEquals("override", interfaceAddressURL.getMethodParameter("sayHello", "overrideKey"));
+    }
+
+    @Test
+    public void testURLEquals() {
+        URL url1 = URL.valueOf(rawURL);
+        URL url2 = URL.valueOf(rawURL);
+        assertNotSame(url1, url2);
+        assertEquals(url1, url2);
+
+        // with consumer
+        ServiceAddressURL withConsumer = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null);
+        ServiceAddressURL withConsumer2 = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null);
+        assertEquals(withConsumer, withConsumer2);
+
+        ServiceAddressURL withOverride = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL);
+        url2 = url2.addParameter("timeout", "4444");
+        ServiceAddressURL withOverride2 = new DubboServiceAddressURL(url2.getUrlAddress(), url2.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL);
+        assertNotEquals(url1, url2);
+        assertEquals(withOverride, withOverride2);
+    }
+
+    @Test
+    public void testToString() {
+        URL url1 = URL.valueOf(rawURL);
+        System.out.println(url1.toString());
+        ServiceAddressURL withConsumer = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, null);
+        System.out.println(withConsumer.toString());
+        ServiceAddressURL withOverride2 = new DubboServiceAddressURL(url1.getUrlAddress(), url1.getUrlParam(), consumerURL, (ServiceConfigURL) overrideURL);
+        System.out.println(withOverride2.toString());
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java
index c607bd3..5fa5bec 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLBuilderTest.java
@@ -40,7 +40,7 @@ public class URLBuilderTest {
                 .build();
         assertThat(url2.getParameter("newKey1"), equalTo("newValue1"));
         assertThat(url2.getParameter("newKey2"), equalTo("2"));
-        assertThat(url2.getParameter("version"), equalTo("1"));
+        assertThat(url2.getVersion(), equalTo("1"));
     }
 
     @Test
@@ -84,7 +84,7 @@ public class URLBuilderTest {
                 .removeParameters(Arrays.asList("key2", "application"))
                 .build();
         assertThat(url2.getParameters().size(), equalTo(1));
-        assertThat(url2.getParameter("version"), equalTo("1.0.0"));
+        assertThat(url2.getVersion(), equalTo("1.0.0"));
     }
 
     @Test
@@ -94,7 +94,7 @@ public class URLBuilderTest {
                 .addParameterIfAbsent("absentKey", "absentValue")
                 .addParameterIfAbsent("version", "2.0.0") // should not override
                 .build();
-        assertThat(url2.getParameter("version"), equalTo("1.0.0"));
+        assertThat(url2.getVersion(), equalTo("1.0.0"));
         assertThat(url2.getParameter("absentKey"), equalTo("absentValue"));
     }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
index 5ba606d..b48dbdb 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java
@@ -50,7 +50,7 @@ public class URLTest {
         assertEquals(0, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = URL.valueOf("context/path?version=1.0.0&application=morgan");
@@ -63,7 +63,7 @@ public class URLTest {
         assertEquals(0, url.getPort());
         assertEquals("path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -132,7 +132,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -260,7 +260,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertNull(url.getPath());
         assertEquals(1, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
 
         url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
         assertURLStrDecoder(url);
@@ -272,10 +272,10 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
-        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+        url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue=");
         assertURLStrDecoder(url);
         assertEquals("dubbo", url.getProtocol());
         assertEquals("admin", url.getUsername());
@@ -285,7 +285,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(3, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
         assertEquals("noValue", url.getParameter("noValue"));
     }
@@ -301,7 +301,7 @@ public class URLTest {
 
     @Test
     public void test_noValueKey() throws Exception {
-        URL url = URL.valueOf("http://1.2.3.4:8080/path?k0&k1=v1");
+        URL url = URL.valueOf("http://1.2.3.4:8080/path?k0=&k1=v1");
 
         assertURLStrDecoder(url);
         assertTrue(url.hasParameter("k0"));
@@ -403,7 +403,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = url.setPort(1);
@@ -417,7 +417,7 @@ public class URLTest {
         assertEquals(1, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = url.setPath("path");
@@ -431,7 +431,7 @@ public class URLTest {
         assertEquals(1, url.getPort());
         assertEquals("path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = url.setProtocol("protocol");
@@ -445,7 +445,7 @@ public class URLTest {
         assertEquals(1, url.getPort());
         assertEquals("path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = url.setUsername("username");
@@ -459,7 +459,7 @@ public class URLTest {
         assertEquals(1, url.getPort());
         assertEquals("path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
 
         url = url.setPassword("password");
@@ -473,7 +473,7 @@ public class URLTest {
         assertEquals(1, url.getPort());
         assertEquals("path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -495,7 +495,7 @@ public class URLTest {
         assertEquals("morgan", url.getParameter("application"));
         assertEquals("v1", url.getParameter("k1"));
         assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
+        assertNull(url.getVersion());
 
         url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
         url = url.removeParameters("version", "application", "NotExistedKey");
@@ -510,7 +510,7 @@ public class URLTest {
         assertEquals(2, url.getParameters().size());
         assertEquals("v1", url.getParameter("k1"));
         assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
+        assertNull(url.getVersion());
         assertNull(url.getParameter("application"));
 
         url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
@@ -526,7 +526,7 @@ public class URLTest {
         assertEquals(2, url.getParameters().size());
         assertEquals("v1", url.getParameter("k1"));
         assertEquals("v2", url.getParameter("k2"));
-        assertNull(url.getParameter("version"));
+        assertNull(url.getVersion());
         assertNull(url.getParameter("application"));
     }
 
@@ -769,7 +769,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -787,7 +787,7 @@ public class URLTest {
         assertEquals(20880, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -801,7 +801,7 @@ public class URLTest {
         assertEquals(0, url.getPort());
         assertEquals("context/path", url.getPath());
         assertEquals(2, url.getParameters().size());
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("morgan", url.getParameter("application"));
     }
 
@@ -897,4 +897,20 @@ public class URLTest {
         assertEquals(Integer.valueOf(1), url.getParameter("i", Integer.class));
         assertEquals(Boolean.FALSE, url.getParameter("b", Boolean.class));
     }
+
+    @Test
+    public void testEquals() {
+        URL url1 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+        URL url2 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+        Assertions.assertEquals(url1, url2);
+
+        URL url3 = URL.valueOf("10.20.130.230:20881/context/path?interface=org.apache.dubbo.test.interfaceName&group=group&version=1.0.0");
+        Assertions.assertNotEquals(url1, url3);
+
+        URL url4 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&weight=10&group=group&version=1.0.0");
+        Assertions.assertNotEquals(url1, url4);
+
+        URL url5 = URL.valueOf("10.20.130.230:20880/context/path?interface=org.apache.dubbo.test.interfaceName&weight=10&group=group&version=1.0.0");
+        Assertions.assertEquals(url4, url5);
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java
index ed737ef..ea54e9f 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/threadpool/support/AbortPolicyWithReportTest.java
@@ -17,7 +17,7 @@
 package org.apache.dubbo.common.threadpool.support;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.threadpool.support.AbortPolicyWithReport;
+
 import org.junit.jupiter.api.Test;
 
 import java.util.concurrent.Executors;
@@ -27,7 +27,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 public class AbortPolicyWithReportTest {
     @Test
     public void jStackDumpTest() throws InterruptedException {
-        URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue");
+        URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?dump.directory=/tmp&version=1.0.0&application=morgan&noValue=");
         AbortPolicyWithReport abortPolicyWithReport = new AbortPolicyWithReport("Test", url);
 
         try {
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java
index a29c8e9..dc0eda6 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/UrlUtilsTest.java
@@ -367,7 +367,7 @@ public class UrlUtilsTest {
     @Test
     public void testIsMatchUrlWithDefaultPrefix() {
         URL url = URL.valueOf("dubbo://127.0.0.1:20880/com.xxx.XxxService?default.version=1.0.0&default.group=test");
-        assertEquals("1.0.0", url.getParameter("version"));
+        assertEquals("1.0.0", url.getVersion());
         assertEquals("1.0.0", url.getParameter("default.version"));
 
         URL consumerUrl = URL.valueOf("consumer://127.0.0.1/com.xxx.XxxService?version=1.0.0&group=test");
diff --git a/dubbo-compatible/pom.xml b/dubbo-compatible/pom.xml
index b96623a..9d6f462 100644
--- a/dubbo-compatible/pom.xml
+++ b/dubbo-compatible/pom.xml
@@ -45,27 +45,22 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-http</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-p2p</artifactId>
+            <artifactId>dubbo-remoting-zookeeper</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-zookeeper</artifactId>
+            <artifactId>dubbo-rpc-rest</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-rest</artifactId>
+            <artifactId>dubbo-filter-cache</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-thrift</artifactId>
+            <artifactId>dubbo-filter-validation</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
         <dependency>
diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/thrift/ClassNameGenerator.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/thrift/ClassNameGenerator.java
deleted file mode 100644
index dec29d3..0000000
--- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/protocol/thrift/ClassNameGenerator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.dubbo.rpc.protocol.thrift;
-
-@Deprecated
-public interface ClassNameGenerator extends org.apache.dubbo.rpc.protocol.thrift.ClassNameGenerator {
-}
diff --git a/dubbo-config/dubbo-config-api/pom.xml b/dubbo-config/dubbo-config-api/pom.xml
index a84e7e9..86880eb 100644
--- a/dubbo-config/dubbo-config-api/pom.xml
+++ b/dubbo-config/dubbo-config-api/pom.xml
@@ -55,16 +55,6 @@
             <artifactId>dubbo-rpc-injvm</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-filter-validation</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-filter-cache</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
 
         <!-- FIXME, we shouldn't rely on these modules, even in test scope -->
         <dependency>
@@ -83,20 +73,6 @@
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-rmi</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-hessian</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-remoting-netty4</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
@@ -132,7 +108,7 @@
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-eureka</artifactId>
+            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
             <exclusions>
@@ -145,41 +121,21 @@
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-etcd3</artifactId>
+            <artifactId>dubbo-metadata-report-redis</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-consul</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.google.guava</groupId>
-                    <artifactId>guava</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-etcd</artifactId>
+            <artifactId>dubbo-configcenter-zookeeper</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
+            <artifactId>dubbo-configcenter-nacos</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
             <exclusions>
@@ -192,41 +148,14 @@
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-consul</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-<!--        <dependency>-->
-<!--            <groupId>org.apache.dubbo</groupId>-->
-<!--            <artifactId>dubbo-metadata-report-redis</artifactId>-->
-<!--            <version>${project.parent.version}</version>-->
-<!--            <scope>test</scope>-->
-<!--        </dependency>-->
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-zookeeper</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-nacos</artifactId>
+            <artifactId>dubbo-filter-cache</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.google.guava</groupId>
-                    <artifactId>guava</artifactId>
-                </exclusion>
-            </exclusions>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-consul</artifactId>
+            <artifactId>dubbo-filter-validation</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
         </dependency>
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index f38b80b..ec73b47 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@ -233,7 +233,8 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
 
         if (bootstrap == null) {
             bootstrap = DubboBootstrap.getInstance();
-            bootstrap.init();
+            bootstrap.initialize();
+            bootstrap.reference(this);
         }
 
         checkAndUpdateSubConfigs();
@@ -336,7 +337,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
                             url = url.setPath(interfaceName);
                         }
                         if (UrlUtils.isRegistry(url)) {
-                            urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
+                            urls.add(url.putAttribute(REFER_KEY, map));
                         } else {
                             urls.add(ClusterUtils.mergeUrl(url, map));
                         }
@@ -353,7 +354,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
                             if (monitorUrl != null) {
                                 map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                             }
-                            urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
+                            urls.add(u.putAttribute(REFER_KEY, map));
                         }
                     }
                     if (urls.isEmpty()) {
@@ -388,10 +389,10 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
         }
 
         if (logger.isInfoEnabled()) {
-            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
+            logger.info("Referred dubbo service " + interfaceClass.getName());
         }
 
-        URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
+        URL consumerURL = new URL(CONSUMER_PROTOCOL, map.get(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
         MetadataUtils.publishServiceDefinition(consumerURL);
 
         // create service proxy
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 356a897..44f7730 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -37,6 +37,7 @@ import org.apache.dubbo.config.support.Parameter;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.event.Event;
 import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.ServiceNameMapping;
 import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.rpc.Exporter;
@@ -182,13 +183,14 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
     }
 
     public synchronized void export() {
-        if (!shouldExport()) {
+        if (!shouldExport() || exported) {
             return;
         }
 
         if (bootstrap == null) {
             bootstrap = DubboBootstrap.getInstance();
             bootstrap.initialize();
+            bootstrap.service(this);
         }
 
         checkAndUpdateSubConfigs();
@@ -201,13 +203,15 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
         serviceMetadata.setServiceInterfaceName(getInterface());
         serviceMetadata.setTarget(getRef());
 
+        if (!shouldExport()) {
+            return;
+        }
+
         if (shouldDelay()) {
             DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
         } else {
             doExport();
         }
-
-        exported();
     }
 
     public void exported() {
@@ -304,6 +308,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
             path = interfaceName;
         }
         doExportUrls();
+        exported();
     }
 
     @SuppressWarnings({"unchecked", "rawtypes"})
@@ -480,9 +485,9 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
                         }
                         if (logger.isInfoEnabled()) {
                             if (url.getParameter(REGISTER_KEY, true)) {
-                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
+                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url.getServiceKey() + " to registry " + registryURL.getAddress());
                             } else {
-                                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
+                                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url.getServiceKey());
                             }
                         }
 
@@ -492,7 +497,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
                             registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                         }
 
-                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
+                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.putAttribute(EXPORT_KEY, url));
                         DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
 
                         Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
@@ -502,6 +507,9 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
                     if (logger.isInfoEnabled()) {
                         logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                     }
+                    if (MetadataService.class.getName().equals(url.getServiceInterface())) {
+                        MetadataUtils.saveMetadataURL(url);
+                    }
                     Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                     DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
 
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index daf6029..a4116f4 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -68,7 +68,6 @@ import org.apache.dubbo.metadata.report.MetadataReportFactory;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
 import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
 import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
@@ -82,7 +81,6 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
-import java.util.SortedSet;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
@@ -513,11 +511,6 @@ public class DubboBootstrap extends GenericEventListener {
         return this;
     }
 
-    @Deprecated
-    public void init() {
-        initialize();
-    }
-
     /**
      * Initialize
      */
@@ -869,7 +862,7 @@ public class DubboBootstrap extends GenericEventListener {
      * Initialize {@link MetadataService} from {@link WritableMetadataService}'s extension
      */
     private void initMetadataService() {
-        startMetadataCenter();
+//        startMetadataCenter();
         this.metadataService = getDefaultExtension();
         this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
     }
@@ -930,7 +923,7 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private boolean hasExportedServices() {
-        return !metadataService.getExportedURLs().isEmpty();
+        return CollectionUtils.isNotEmpty(configManager.getServices());
     }
 
     /**
@@ -1151,21 +1144,11 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void registerServiceInstance() {
-        if (CollectionUtils.isEmpty(getServiceDiscoveries())) {
-            return;
-        }
-
         ApplicationConfig application = getApplication();
 
         String serviceName = application.getName();
 
-        URL exportedURL = selectMetadataServiceExportedURL();
-
-        String host = exportedURL.getHost();
-
-        int port = exportedURL.getPort();
-
-        ServiceInstance serviceInstance = createServiceInstance(serviceName, host, port);
+        ServiceInstance serviceInstance = createServiceInstance(serviceName);
 
         doRegisterServiceInstance(serviceInstance);
 
@@ -1173,55 +1156,37 @@ public class DubboBootstrap extends GenericEventListener {
         executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> {
             InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension();
             localMetadataService.blockUntilUpdated();
-            ServiceInstanceMetadataUtils.refreshMetadataAndInstance();
-        }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MICROSECONDS);
+            ServiceInstanceMetadataUtils.refreshMetadataAndInstance(serviceInstance);
+        }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS);
     }
 
     private void doRegisterServiceInstance(ServiceInstance serviceInstance) {
-        //FIXME
-        publishMetadataToRemote(serviceInstance);
-
-        getServiceDiscoveries().forEach(serviceDiscovery ->
-        {
-            calInstanceRevision(serviceDiscovery, serviceInstance);
-            // register metadata
-            serviceDiscovery.register(serviceInstance);
-        });
+        // register instance only when at least one service is exported.
+        if (serviceInstance.getPort() != null && serviceInstance.getPort() != -1) {
+            publishMetadataToRemote(serviceInstance);
+            logger.info("Start registering instance address to registry.");
+            getServiceDiscoveries().forEach(serviceDiscovery ->
+            {
+                calInstanceRevision(serviceDiscovery, serviceInstance);
+                if (logger.isDebugEnabled()) {
+                    logger.info("Start registering instance address to registry" + serviceDiscovery.getUrl() + ", instance " + serviceInstance);
+                }
+                // register metadata
+                serviceDiscovery.register(serviceInstance);
+            });
+        }
     }
 
     private void publishMetadataToRemote(ServiceInstance serviceInstance) {
 //        InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService)WritableMetadataService.getDefaultExtension();
 //        localMetadataService.blockUntilUpdated();
+        if (logger.isInfoEnabled()) {
+            logger.info("Start publishing metadata to remote center, this only makes sense for applications enabled remote metadata center.");
+        }
         RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
         remoteMetadataService.publishMetadata(serviceInstance.getServiceName());
     }
 
-    private URL selectMetadataServiceExportedURL() {
-
-        URL selectedURL = null;
-
-        SortedSet<String> urlValues = metadataService.getExportedURLs();
-
-        for (String urlValue : urlValues) {
-            URL url = URL.valueOf(urlValue);
-            if (MetadataService.class.getName().equals(url.getServiceInterface())) {
-                continue;
-            }
-            if ("rest".equals(url.getProtocol())) { // REST first
-                selectedURL = url;
-                break;
-            } else {
-                selectedURL = url; // If not found, take any one
-            }
-        }
-
-        if (selectedURL == null && CollectionUtils.isNotEmpty(urlValues)) {
-            selectedURL = URL.valueOf(urlValues.iterator().next());
-        }
-
-        return selectedURL;
-    }
-
     private void unregisterServiceInstance() {
         if (serviceInstance != null) {
             getServiceDiscoveries().forEach(serviceDiscovery -> {
@@ -1230,18 +1195,10 @@ public class DubboBootstrap extends GenericEventListener {
         }
     }
 
-    private ServiceInstance createServiceInstance(String serviceName, String host, int port) {
-        this.serviceInstance = new DefaultServiceInstance(serviceName, host, port);
+    private ServiceInstance createServiceInstance(String serviceName) {
+        this.serviceInstance = new DefaultServiceInstance(serviceName);
         setMetadataStorageType(serviceInstance, getMetadataType());
-
-        ExtensionLoader<ServiceInstanceCustomizer> loader =
-                ExtensionLoader.getExtensionLoader(ServiceInstanceCustomizer.class);
-        // FIXME, sort customizer before apply
-        loader.getSupportedExtensionInstances().forEach(customizer -> {
-            // customizes
-            customizer.customize(this.serviceInstance);
-        });
-
+        ServiceInstanceMetadataUtils.customizeInstance(this.serviceInstance);
         return this.serviceInstance;
     }
 
@@ -1338,7 +1295,9 @@ public class DubboBootstrap extends GenericEventListener {
                     return applicationConfig;
                 });
 
-        application.refresh();
+        if (!application.isRefreshed()) {
+            application.refresh();
+        }
         return application;
     }
 
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index f68c901..faae7d7 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -72,7 +72,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
 
             ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
             serviceConfig.setApplication(getApplicationConfig());
-            serviceConfig.setRegistries(getRegistries());
+            serviceConfig.setRegistry(new RegistryConfig("N/A"));
             serviceConfig.setProtocol(generateMetadataProtocol());
             serviceConfig.setInterface(MetadataService.class);
             serviceConfig.setRef(metadataService);
@@ -141,17 +141,6 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
         return ApplicationModel.getConfigManager().getApplication().get();
     }
 
-    private List<RegistryConfig> getRegistries() {
-        return new ArrayList<>(ApplicationModel.getConfigManager().getRegistries());
-    }
-
-    /**
-     * MetadataService will always being exported as a Dubbo protocol service. Some protocols have requirements on service definitions,
-     * trying to export MetadataService on those protocols may cause exception.
-     * <p>
-     * For registries that can carry metadata information, such as Zookeeper, Nacos, users need not to care about which Port MetadataService will work on.
-     * For registries that have limited information, typically DNS, we strongly recommend users specify the MetadataService Port using 'dubbo.application.metadata-service-port=xxx'
-     */
     private ProtocolConfig generateMetadataProtocol() {
         ProtocolConfig defaultProtocol = new ProtocolConfig();
         Integer port = getApplicationConfig().getMetadataServicePort();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
similarity index 51%
rename from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java
rename to dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
index db68898..3831337 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstanceHostPortCustomizer.java
@@ -16,22 +16,22 @@
  */
 package org.apache.dubbo.config.metadata;
 
+import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
-import java.util.Collection;
-import java.util.stream.Stream;
+import java.util.Set;
 
 /**
  * The {@link ServiceInstanceCustomizer} to customize the {@link ServiceInstance#getPort() port} of service instance.
  *
  * @since 2.7.5
  */
-public class ServiceInstancePortCustomizer implements ServiceInstanceCustomizer {
+public class ServiceInstanceHostPortCustomizer implements ServiceInstanceCustomizer {
 
     @Override
     public void customize(ServiceInstance serviceInstance) {
@@ -40,24 +40,31 @@ public class ServiceInstancePortCustomizer implements ServiceInstanceCustomizer
             return;
         }
 
-        Collection<ProtocolConfig> protocols = ApplicationModel.getConfigManager()
-                .getProtocols();
+        WritableMetadataService writableMetadataService = WritableMetadataService.getDefaultExtension();
 
-        if (CollectionUtils.isEmpty(protocols)) {
-            throw new IllegalStateException("We should have at least one protocol configured at this point.");
-        }
-
-        Stream<ProtocolConfig> protocolStream = protocols.stream();
-        ProtocolConfig protocolConfig = protocolStream
-                // use rest as service instance's default protocol.
-                .filter(protocol -> "rest".equals(protocol.getName()))
-                .findFirst()
-                .orElseGet(() -> protocolStream.findFirst().get());
-
-        if (serviceInstance instanceof DefaultServiceInstance) {
-            DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance;
-            if (protocolConfig.getPort() != null) {
-                instance.setPort(protocolConfig.getPort());
+        String host = null;
+        Integer port = null;
+        Set<URL> urls = writableMetadataService.getExportedServiceURLs();
+        if (CollectionUtils.isNotEmpty(urls)) {
+            String preferredProtocol = ApplicationModel.getApplicationConfig().getProtocol();
+            if (preferredProtocol != null) {
+                for (URL exportedURL : urls) {
+                    if (preferredProtocol.equals(exportedURL.getProtocol())) {
+                        host = exportedURL.getHost();
+                        port = exportedURL.getPort();
+                        break;
+                    }
+                }
+            } else {
+                URL url = urls.iterator().next();
+                host = url.getHost();
+                port = url.getPort();
+            }
+            if (serviceInstance instanceof DefaultServiceInstance) {
+                DefaultServiceInstance instance = (DefaultServiceInstance) serviceInstance;
+                instance.setHost(host);
+                instance.setPort(port);
+                instance.setId(host + ":" + port);
             }
         }
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index 13e5fb1..ad42b51 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.config.utils;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -86,9 +87,12 @@ import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SE
 import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_DUPLICATE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.DUBBO_PUBLISH_INSTANCE_DEFAULT_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INSTANCE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PUBLISH_INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
 import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
@@ -210,18 +214,34 @@ public class ConfigValidationUtils {
     private static List<URL> genCompatibleRegistries(List<URL> registryList, boolean provider) {
         List<URL> result = new ArrayList<>(registryList.size());
         registryList.forEach(registryURL -> {
-            result.add(registryURL);
             if (provider) {
+                boolean publishInterface = registryURL.getParameter(REGISTRY_PUBLISH_INTERFACE_KEY, ConfigurationUtils.getDynamicGlobalConfiguration().getBoolean(DUBBO_PUBLISH_INTERFACE_DEFAULT_KEY, true));
                 // for registries enabled service discovery, automatically register interface compatible addresses.
-                if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())
-                        && registryURL.getParameter(REGISTRY_DUPLICATE_KEY, true)
-                        && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {
-                    URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL)
-                            .setProtocol(REGISTRY_PROTOCOL)
-                            .removeParameter(REGISTRY_TYPE_KEY)
-                            .build();
-                    result.add(interfaceCompatibleRegistryURL);
+                if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
+                    result.add(registryURL);
+                    if (publishInterface && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {
+                        URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL)
+                                .setProtocol(REGISTRY_PROTOCOL)
+                                .removeParameter(REGISTRY_TYPE_KEY)
+                                .build();
+                        result.add(interfaceCompatibleRegistryURL);
+                    }
+                } else {
+                    boolean publishInstance = registryURL.getParameter(REGISTRY_PUBLISH_INSTANCE_KEY, ConfigurationUtils.getDynamicGlobalConfiguration().getBoolean(DUBBO_PUBLISH_INSTANCE_DEFAULT_KEY, true));
+                    if (registryNotExists(registryURL, registryList, SERVICE_REGISTRY_PROTOCOL)
+                            && publishInstance) {
+                        URL serviceDiscoveryRegistryURL = URLBuilder.from(registryURL)
+                                .setProtocol(SERVICE_REGISTRY_PROTOCOL)
+                                .removeParameter(REGISTRY_TYPE_KEY)
+                                .build();
+                        result.add(serviceDiscoveryRegistryURL);
+                    }
+                    if (publishInterface) {
+                        result.add(registryURL);
+                    }
                 }
+            } else {
+                result.add(registryURL);
             }
         });
         return result;
@@ -273,7 +293,7 @@ public class ConfigValidationUtils {
             return URLBuilder.from(registryURL)
                     .setProtocol(DUBBO_PROTOCOL)
                     .addParameter(PROTOCOL_KEY, monitor.getProtocol())
-                    .addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map))
+                    .putAttribute(REFER_KEY, StringUtils.toQueryString(map))
                     .build();
         }
         return null;
@@ -527,7 +547,12 @@ public class ConfigValidationUtils {
     }
 
     private static String extractRegistryType(URL url) {
-        return isServiceDiscoveryRegistryType(url) ? SERVICE_REGISTRY_PROTOCOL : REGISTRY_PROTOCOL;
+        return isServiceDiscoveryRegistryType(url) ? SERVICE_REGISTRY_PROTOCOL : getRegistryProtocolType(url);
+    }
+
+    private static String getRegistryProtocolType(URL url) {
+        String registryProtocol = url.getParameter("registry-protocol-type");
+        return StringUtils.isNotEmpty(registryProtocol) ? registryProtocol : REGISTRY_PROTOCOL;
     }
 
     public static void checkExtension(Class<?> type, String property, String value) {
diff --git a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
index 3e9be3b..656f868 100644
--- a/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
+++ b/dubbo-config/dubbo-config-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
@@ -1 +1 @@
-port=org.apache.dubbo.config.metadata.ServiceInstancePortCustomizer
\ No newline at end of file
+port=org.apache.dubbo.config.metadata.ServiceInstanceHostPortCustomizer
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java
index 4d157ff..ed46382 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java
@@ -258,7 +258,7 @@ public class ServiceConfigTest {
     @Test
     public void testApplicationInUrl() {
         service.export();
-        Assertions.assertNotNull(service.toUrl().getParameter(APPLICATION_KEY));
-        Assertions.assertEquals("app", service.toUrl().getParameter(APPLICATION_KEY));
+        Assertions.assertNotNull(service.toUrl().getApplication());
+        Assertions.assertEquals("app", service.toUrl().getApplication());
     }
 }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionByAnnotation.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionByAnnotation.java
deleted file mode 100644
index 23d00e4..0000000
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionByAnnotation.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.consumer;
-
-import org.apache.dubbo.config.api.DemoService;
-
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * DemoAction
- */
-public class DemoActionByAnnotation {
-
-    @Autowired
-    private DemoService demoService;
-
-    public DemoService getDemoService() {
-        return demoService;
-    }
-
-}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionBySetter.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionBySetter.java
deleted file mode 100644
index 0606e26..0000000
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoActionBySetter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.consumer;
-
-import org.apache.dubbo.config.api.DemoService;
-
-/**
- * DemoAction
- */
-public class DemoActionBySetter {
-
-    private DemoService demoService;
-
-    public DemoService getDemoService() {
-        return demoService;
-    }
-
-    public void setDemoService(DemoService demoService) {
-        this.demoService = demoService;
-    }
-
-}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoInterceptor.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoInterceptor.java
deleted file mode 100644
index 46d7e9a..0000000
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/consumer/DemoInterceptor.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.config.consumer;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-
-/**
- * DemoInterceptor
- */
-public class DemoInterceptor implements MethodInterceptor {
-
-    public Object invoke(MethodInvocation invocation) throws Throwable {
-        return "aop:" + invocation.proceed();
-    }
-
-}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index 31bfcf4..c1255cc 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -66,12 +66,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-default</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-monitor-default</artifactId>
             <version>${project.parent.version}</version>
             <scope>test</scope>
@@ -84,12 +78,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-rmi</artifactId>
-            <version>${project.parent.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-injvm</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
index ab229eb..cf20146 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
@@ -419,7 +419,16 @@
                 <xsd:documentation><![CDATA[ Register consumer instance or not, default false. ]]></xsd:documentation>
             </xsd:annotation>
         </xsd:attribute>
-
+        <xsd:attribute name="publish-interface" type="xsd:boolean">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ Publish interface level url to registry, default false. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
+        <xsd:attribute name="protocol" type="xsd:string">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ The preferred protocol to use, set protocol name. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
     </xsd:complexType>
 
     <xsd:complexType name="moduleType">
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
index a79ebb4..57efa02 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
@@ -415,6 +415,16 @@
                 <xsd:documentation><![CDATA[ Register consumer instance or not, default false. ]]></xsd:documentation>
             </xsd:annotation>
         </xsd:attribute>
+        <xsd:attribute name="publish-interface" type="xsd:boolean">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ Publish interface level url to registry, default true. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
+        <xsd:attribute name="protocol" type="xsd:string">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ The preferred protocol to use, set protocol name. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
     </xsd:complexType>
 
     <xsd:complexType name="moduleType">
@@ -623,6 +633,16 @@
                 <xsd:documentation><![CDATA[ weight of registry. ]]></xsd:documentation>
             </xsd:annotation>
         </xsd:attribute>
+        <xsd:attribute name="publish-interface" type="xsd:boolean">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ Publish interface level url to registry, default true. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
+        <xsd:attribute name="publish-instance" type="xsd:boolean">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ Publish instance level url to registry, default true. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
     </xsd:complexType>
 
     <xsd:complexType name="metadataReportType">
diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml b/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml
index 26d78ef..3756a95 100644
--- a/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-apollo/pom.xml
@@ -16,12 +16,13 @@
   -->
 <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>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
     </parent>
+
     <artifactId>dubbo-configcenter-apollo</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
index f191178..64626f7 100644
--- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
@@ -44,11 +44,9 @@ 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
@@ -97,7 +95,7 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration {
         }
 
         String namespace = url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP);
-        String apolloNamespace = StringUtils.isEmpty(namespace) ? url.getParameter(GROUP_KEY, DEFAULT_GROUP) : namespace;
+        String apolloNamespace = StringUtils.isEmpty(namespace) ? url.getGroup(DEFAULT_GROUP) : namespace;
         dubboConfig = ConfigService.getConfig(apolloNamespace);
         dubboConfigFile = ConfigService.getConfigFile(apolloNamespace, ConfigFileFormat.Properties);
 
@@ -155,7 +153,7 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration {
     @Override
     public String getConfig(String key, String group, long timeout) throws IllegalStateException {
         if (StringUtils.isNotEmpty(group)) {
-            if (group.equals(url.getParameter(APPLICATION_KEY))) {
+            if (group.equals(url.getApplication())) {
                 return ConfigService.getAppConfig().getProperty(key, null);
             } else {
                 return ConfigService.getConfig(group).getProperty(key, null);
@@ -181,7 +179,7 @@ public class ApolloDynamicConfiguration implements DynamicConfiguration {
         if (StringUtils.isEmpty(group)) {
             return dubboConfigFile.getContent();
         }
-        if (group.equals(url.getParameter(APPLICATION_KEY))) {
+        if (group.equals(url.getApplication())) {
             return ConfigService.getConfigFile(APOLLO_APPLICATION_KEY, ConfigFileFormat.Properties).getContent();
         }
 
diff --git a/dubbo-configcenter/dubbo-configcenter-consul/pom.xml b/dubbo-configcenter/dubbo-configcenter-consul/pom.xml
deleted file mode 100644
index c6b2393..0000000
--- a/dubbo-configcenter/dubbo-configcenter-consul/pom.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?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>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>dubbo-configcenter-consul</artifactId>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-common</artifactId>
-            <version>${project.parent.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-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java
deleted file mode 100644
index c1e5a44..0000000
--- a/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.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.TreePathDynamicConfiguration;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
-
-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 org.apache.dubbo.common.utils.StringUtils;
-
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
-
-/**
- * config center implementation for consul
- */
-public class ConsulDynamicConfiguration extends TreePathDynamicConfiguration {
-    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 final Consul client;
-
-    private final KeyValueClient kvClient;
-
-    private final int watchTimeout;
-
-    private final ConcurrentMap<String, ConsulListener> watchers = new ConcurrentHashMap<>();
-
-    public ConsulDynamicConfiguration(URL url) {
-        super(url);
-        watchTimeout = url.getParameter(WATCH_TIMEOUT, DEFAULT_WATCH_TIMEOUT);
-        String host = url.getHost();
-        int port = url.getPort() != 0 ? url.getPort() : DEFAULT_PORT;
-        Consul.Builder builder = Consul.builder()
-                .withHostAndPort(HostAndPort.fromParts(host, port));
-        String token = url.getParameter("token", (String) null);
-        if (StringUtils.isNotEmpty(token)) {
-            builder.withAclToken(token);
-        }
-        client = builder.build();
-        this.kvClient = client.keyValueClient();
-    }
-
-    @Override
-    public String getInternalProperty(String key) {
-        logger.info("getting config from: " + key);
-        return kvClient.getValueAsString(key, Charsets.UTF_8).orElse(null);
-    }
-
-    @Override
-    protected boolean doPublishConfig(String pathKey, String content) throws Exception {
-        return kvClient.putValue(pathKey, content);
-    }
-
-    @Override
-    protected String doGetConfig(String pathKey) throws Exception {
-        return getInternalProperty(pathKey);
-    }
-
-    @Override
-    protected boolean doRemoveConfig(String pathKey) throws Exception {
-        kvClient.deleteKey(pathKey);
-        return true;
-    }
-
-    @Override
-    protected Collection<String> doGetConfigKeys(String groupPath) {
-        List<String> keys = kvClient.getKeys(groupPath);
-        List<String> configKeys = new LinkedList<>();
-        if (CollectionUtils.isNotEmpty(keys)) {
-            keys.stream()
-                    .filter(k -> !k.equals(groupPath))
-                    .map(k -> k.substring(k.lastIndexOf(PATH_SEPARATOR) + 1))
-                    .forEach(configKeys::add);
-        }
-        return configKeys;
-    }
-
-    @Override
-    protected void doAddListener(String pathKey, ConfigurationListener listener) {
-        logger.info("register listener " + listener.getClass() + " for config with key: " + pathKey);
-        ConsulListener watcher = watchers.computeIfAbsent(pathKey, k -> new ConsulListener(pathKey));
-        watcher.addListener(listener);
-    }
-
-    @Override
-    protected void doRemoveListener(String pathKey, ConfigurationListener listener) {
-        logger.info("unregister listener " + listener.getClass() + " for config with key: " + pathKey);
-        ConsulListener watcher = watchers.get(pathKey);
-        if (watcher != null) {
-            watcher.removeListener(listener);
-        }
-    }
-
-    @Override
-    protected void doClose() throws Exception {
-        client.destroy();
-    }
-
-    private class ConsulListener implements KVCache.Listener<String, Value> {
-
-        private KVCache kvCache;
-        private final Set<ConfigurationListener> listeners = new LinkedHashSet<>();
-        private final String normalizedKey;
-
-        public ConsulListener(String normalizedKey) {
-            this.normalizedKey = normalizedKey;
-            initKVCache();
-        }
-
-        private void initKVCache() {
-            this.kvCache = KVCache.newCache(kvClient, normalizedKey, watchTimeout);
-            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(normalizedKey, getGroup(), 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-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java b/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java
deleted file mode 100644
index 980a156..0000000
--- a/dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.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-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
deleted file mode 100644
index b7a5091..0000000
--- a/dubbo-configcenter/dubbo-configcenter-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
+++ /dev/null
@@ -1 +0,0 @@
-consul=org.apache.dubbo.configcenter.consul.ConsulDynamicConfigurationFactory
diff --git a/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java
deleted file mode 100644
index c54d103..0000000
--- a/dubbo-configcenter/dubbo-configcenter-consul/src/test/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfigurationTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.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.Arrays;
-import java.util.Optional;
-import java.util.TreeSet;
-
-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://127.0.0.1:" + consul.getHttpPort());
-
-        configuration = new ConsulDynamicConfiguration(configCenterUrl);
-        client = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", 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 testPublishConfig() {
-        configuration.publishConfig("value", "metadata", "1");
-        // test equals
-        assertEquals("1", configuration.getConfig("value", "/metadata"));
-        assertEquals("1", kvClient.getValueAsString("/dubbo/config/metadata/value").get());
-    }
-
-    @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() {
-        configuration.publishConfig("v1", "metadata", "1");
-        configuration.publishConfig("v2", "metadata", "2");
-        configuration.publishConfig("v3", "metadata", "3");
-        // test equals
-        assertEquals(new TreeSet(Arrays.asList("v1", "v2", "v3")), configuration.getConfigKeys("metadata"));
-    }
-}
diff --git a/dubbo-configcenter/dubbo-configcenter-etcd/pom.xml b/dubbo-configcenter/dubbo-configcenter-etcd/pom.xml
deleted file mode 100644
index 3e8d8f3..0000000
--- a/dubbo-configcenter/dubbo-configcenter-etcd/pom.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?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>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <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-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
deleted file mode 100644
index f686e86..0000000
--- a/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.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-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java b/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java
deleted file mode 100644
index 269cee6..0000000
--- a/dubbo-configcenter/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationFactory.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.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-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory b/dubbo-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
deleted file mode 100644
index d84b1ae..0000000
--- a/dubbo-configcenter/dubbo-configcenter-etcd/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
+++ /dev/null
@@ -1 +0,0 @@
-etcd=org.apache.dubbo.configcenter.support.etcd.EtcdDynamicConfigurationFactory
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
deleted file mode 100644
index e6ed4b5..0000000
--- a/dubbo-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.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);
-
-    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-configcenter/dubbo-configcenter-nacos/pom.xml b/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml
index b54dfc6..65364b8 100644
--- a/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-nacos/pom.xml
@@ -23,7 +23,6 @@
         <groupId>org.apache.dubbo</groupId>
         <artifactId>dubbo-configcenter</artifactId>
         <version>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
index a96f843..fbdbf74 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
@@ -74,7 +74,7 @@ public class ZookeeperDynamicConfiguration extends TreePathDynamicConfiguration
      */
     @Override
     public String getInternalProperty(String key) {
-        return zkClient.getContent(key);
+        return zkClient.getContent(buildPathKey("", key));
     }
 
     @Override
diff --git a/dubbo-configcenter/pom.xml b/dubbo-configcenter/pom.xml
index 869611c..f69f389 100644
--- a/dubbo-configcenter/pom.xml
+++ b/dubbo-configcenter/pom.xml
@@ -33,8 +33,6 @@
     <modules>
         <module>dubbo-configcenter-zookeeper</module>
         <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-container/dubbo-container-log4j/pom.xml b/dubbo-container/dubbo-container-log4j/pom.xml
deleted file mode 100644
index 949615f..0000000
--- a/dubbo-container/dubbo-container-log4j/pom.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<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>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
-    </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-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java b/dubbo-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java
deleted file mode 100644
index 946e4c5..0000000
--- a/dubbo-container/dubbo-container-log4j/src/main/java/org/apache/dubbo/container/log4j/Log4jContainer.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.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-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container b/dubbo-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
deleted file mode 100644
index 0b4c162..0000000
--- a/dubbo-container/dubbo-container-log4j/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
+++ /dev/null
@@ -1 +0,0 @@
-log4j=org.apache.dubbo.container.log4j.Log4jContainer
\ No newline at end of file
diff --git a/dubbo-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java b/dubbo-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java
deleted file mode 100644
index 535a3d5..0000000
--- a/dubbo-container/dubbo-container-log4j/src/test/java/org/apache/dubbo/container/log4j/Log4jContainerTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.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-container/dubbo-container-logback/pom.xml b/dubbo-container/dubbo-container-logback/pom.xml
deleted file mode 100644
index 0af3164..0000000
--- a/dubbo-container/dubbo-container-logback/pom.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<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>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
-    </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-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.java b/dubbo-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.java
deleted file mode 100644
index 430e2e2..0000000
--- a/dubbo-container/dubbo-container-logback/src/main/java/org/apache/dubbo/container/logback/LogbackContainer.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.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-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container b/dubbo-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
deleted file mode 100644
index 04c4eaa..0000000
--- a/dubbo-container/dubbo-container-logback/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.container.Container
+++ /dev/null
@@ -1 +0,0 @@
-logback=org.apache.dubbo.container.logback.LogbackContainer
\ No newline at end of file
diff --git a/dubbo-container/pom.xml b/dubbo-container/pom.xml
index d088993..ec2cab0 100644
--- a/dubbo-container/pom.xml
+++ b/dubbo-container/pom.xml
@@ -32,7 +32,5 @@
     <modules>
         <module>dubbo-container-api</module>
         <module>dubbo-container-spring</module>
-        <module>dubbo-container-log4j</module>
-        <module>dubbo-container-logback</module>
     </modules>
 </project>
diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
index 6dd31d2..c48478a 100644
--- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
@@ -65,10 +65,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-dubbo</artifactId>
         </dependency>
         <dependency>
diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
index 27186ea..b78ec15 100644
--- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/pom.xml
@@ -66,10 +66,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-dubbo</artifactId>
         </dependency>
         <dependency>
diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml b/dubbo-demo/dubbo-demo-generic-call/pom.xml
similarity index 73%
copy from dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
copy to dubbo-demo/dubbo-demo-generic-call/pom.xml
index 6dd31d2..0c53604 100644
--- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-generic-call/pom.xml
@@ -21,13 +21,13 @@
 <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-demo-annotation</artifactId>
+        <artifactId>dubbo-demo</artifactId>
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>dubbo-demo-annotation-consumer</artifactId>
+    <artifactId>dubbo-demo-generic-call</artifactId>
 
     <properties>
         <skip_maven_deploy>true</skip_maven_deploy>
@@ -36,8 +36,7 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-demo-interface</artifactId>
-            <version>${project.parent.version}</version>
+            <artifactId>dubbo-config-api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
@@ -45,14 +44,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-nacos</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.alibaba.nacos</groupId>
-            <artifactId>nacos-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-zookeeper</artifactId>
         </dependency>
         <dependency>
@@ -61,11 +52,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-nacos</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
+            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
@@ -73,10 +60,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-config-spring</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-remoting-netty4</artifactId>
         </dependency>
         <dependency>
diff --git a/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java b/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java
new file mode 100644
index 0000000..65b2ff5
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-generic-call/src/main/java/org/apache/dubbo/demo/consumer/GenericApplication.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.demo.consumer;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.MetadataReportConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.utils.ReferenceConfigCache;
+import org.apache.dubbo.rpc.service.GenericService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GenericApplication {
+    public static void main(String[] args) {
+        if (isClassic(args)) {
+//            runWithRefer();
+        } else {
+            runWithBootstrap();
+        }
+    }
+
+    private static boolean isClassic(String[] args) {
+        return args.length > 0 && "classic".equalsIgnoreCase(args[0]);
+    }
+
+    private static void runWithBootstrap() {
+        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
+        reference.setInterface("org.apache.dubbo.demo.DemoService");
+        reference.setGeneric("true");
+
+        ApplicationConfig applicationConfig = new ApplicationConfig("demo-consumer");
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("mapping-type", "metadata");
+        applicationConfig.setParameters(parameters);
+
+        MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
+        metadataReportConfig.setAddress("zookeeper://127.0.0.1:2181");
+
+        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+        bootstrap.application(applicationConfig)
+                .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+                .reference(reference)
+                .start();
+
+        // generic invoke
+        GenericService genericService = (GenericService) ReferenceConfigCache.getCache().get(reference);
+        while (true) {
+            try {
+                Object genericInvokeResult = genericService.$invoke("sayHello", new String[]{String.class.getName()},
+                        new Object[]{"dubbo generic invoke"});
+                System.out.println(genericInvokeResult);
+                Thread.sleep(1000);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+//    private static void runWithRefer() {
+//        ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
+//        reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
+//        reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
+//        reference.setInterface(DemoService.class);
+//        DemoService service = reference.get();
+//        String message = service.sayHello("dubbo");
+//        System.out.println(message);
+//    }
+}
diff --git a/dubbo-registry/dubbo-registry-sofa/src/test/resources/log4j.properties b/dubbo-demo/dubbo-demo-generic-call/src/main/resources/log4j.properties
similarity index 100%
copy from dubbo-registry/dubbo-registry-sofa/src/test/resources/log4j.properties
copy to dubbo-demo/dubbo-demo-generic-call/src/main/resources/log4j.properties
diff --git a/dubbo-demo/dubbo-demo-interface/pom.xml b/dubbo-demo/dubbo-demo-interface/pom.xml
index 97aa7bf..b2b1cd7 100644
--- a/dubbo-demo/dubbo-demo-interface/pom.xml
+++ b/dubbo-demo/dubbo-demo-interface/pom.xml
@@ -29,4 +29,11 @@
     <properties>
         <skip_maven_deploy>true</skip_maven_deploy>
     </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-rest</artifactId>
+        </dependency>
+    </dependencies>
 </project>
diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianService.java b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java
similarity index 62%
rename from dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianService.java
rename to dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java
index 7de8333..308b061 100644
--- a/dubbo-rpc/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianService.java
+++ b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/RestDemoService.java
@@ -1,37 +1,45 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.rpc.protocol.hessian;
-
-
-/**
- * HessianService
- */
-public interface HessianService {
-
-    String sayHello(String name);
-
-    String sayHello(String name, int times);
-
-    void timeOut(int millis);
-
-    String customException();
-
-    String context(String name);
-
-    String getRemoteApplicationName();
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.demo;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+
+@Path("/demoService")
+public interface RestDemoService {
+    @GET
+    @Path("/hello")
+    Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
+
+    @GET
+    @Path("/error")
+    String error();
+
+    @POST
+    @Path("/say")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String sayHello(String name);
+
+    @GET
+    @Path("/getRemoteApplicationName")
+    String getRemoteApplicationName();
+}
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
index b3a4acc..fffa2bd 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/pom.xml
@@ -65,10 +65,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-config-spring</artifactId>
         </dependency>
         <dependency>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
index fba18bc..afe8cb3 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
@@ -36,22 +36,27 @@ public class Application {
 
         new Thread(() -> {
             while (true) {
-                String greetings = greetingService.hello();
-                System.out.println(greetings + " from separated thread.");
                 try {
+                    String greetings = greetingService.hello();
+                    System.out.println(greetings + " from separated thread.");
+
                     Thread.sleep(100);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
+                } catch (Exception e) {
+//                    e.printStackTrace();
                 }
             }
         }).start();
 
         while (true) {
-            CompletableFuture<String> hello = demoService.sayHelloAsync("world");
-            System.out.println("result: " + hello.get());
+            try {
+                CompletableFuture<String> hello = demoService.sayHelloAsync("world");
+                System.out.println("result: " + hello.get());
 
-            String greetings = greetingService.hello();
-            System.out.println("result: " + greetings);
+                String greetings = greetingService.hello();
+                System.out.println("result: " + greetings);
+            } catch (Exception e) {
+//                e.printStackTrace();
+            }
 
             Thread.sleep(500);
         }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
index 9225959..f0c6df1 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
@@ -23,17 +23,16 @@
 
     <dubbo:application name="demo-consumer">
         <dubbo:parameter key="mapping-type" value="metadata"/>
-        <dubbo:parameter key="enable-auto-migration" value="true"/>
     </dubbo:application>
 
     <!--    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>-->
 
     <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 
-    <dubbo:reference provided-by="demo-provider" id="demoService" check="false"
+    <dubbo:reference id="demoService" check="false"
                      interface="org.apache.dubbo.demo.DemoService"/>
 
-    <dubbo:reference provided-by="demo-provider" version="1.0.0" group="greeting" id="greetingService" check="false"
+    <dubbo:reference version="1.0.0" group="greeting" id="greetingService" check="false"
                      interface="org.apache.dubbo.demo.GreetingService"/>
 
 </beans>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/pom.xml
similarity index 98%
copy from dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
copy to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/pom.xml
index c4590c2..bceae6a 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/pom.xml
@@ -23,7 +23,7 @@
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <artifactId>dubbo-demo-xml-provider</artifactId>
+    <artifactId>dubbo-demo-xml-provider-backend</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The demo provider module of dubbo project</description>
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/examples/DubboDemoProvider.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainApplication.java
similarity index 79%
rename from dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/examples/DubboDemoProvider.java
rename to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainApplication.java
index d87be9d..f57e635 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/examples/DubboDemoProvider.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainApplication.java
@@ -14,18 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.protocol.thrift.examples;
+package org.apache.dubbo.demo.provider.chain;
 
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
-public class DubboDemoProvider {
-
+public class ChainApplication {
     public static void main(String[] args) throws Exception {
-        ClassPathXmlApplicationContext context =
-                new ClassPathXmlApplicationContext("dubbo-demo-provider.xml");
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider-chain.xml");
         context.start();
-        System.out.println("context started");
         System.in.read();
     }
-
 }
diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainServiceImpl.java
similarity index 59%
rename from dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
rename to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainServiceImpl.java
index 0447117..bd15576 100644
--- a/dubbo-rpc/dubbo-rpc-rmi/src/test/java/org/apache/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/java/org/apache/dubbo/demo/provider/chain/ChainServiceImpl.java
@@ -1,32 +1,33 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.rpc.protocol.rmi;
-
-import org.apache.dubbo.rpc.RpcContext;
-
-import java.rmi.RemoteException;
-
-public class RemoteServiceImpl implements RemoteService {
-    public String getThreadName() throws RemoteException {
-        System.out.println("RpcContext.getContext().getRemoteHost()=" + RpcContext.getContext().getRemoteHost());
-        return Thread.currentThread().getName();
-    }
-
-    public String sayHello(String name) throws RemoteException {
-        return "hello " + name + "@" + RemoteServiceImpl.class.getName();
-    }
-}
\ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.demo.provider.chain;
+
+import org.apache.dubbo.demo.ChainService;
+import org.apache.dubbo.rpc.RpcContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChainServiceImpl implements ChainService {
+    private static final Logger logger = LoggerFactory.getLogger(ChainServiceImpl.class);
+
+    @Override
+    public String chain(String input) {
+        logger.info("Received " + input + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
+        return "Received " + input + ", response from provider: " + RpcContext.getContext().getLocalAddress();
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/dubbo.properties b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/dubbo.properties
new file mode 100644
index 0000000..ad602ba
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/dubbo.properties
@@ -0,0 +1 @@
+dubbo.application.qos.port=22222
diff --git a/dubbo-registry/dubbo-registry-sofa/src/test/resources/log4j.properties b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/log4j.properties
similarity index 100%
rename from dubbo-registry/dubbo-registry-sofa/src/test/resources/log4j.properties
rename to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/log4j.properties
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/resources/dubbo-demo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/spring/dubbo-provider-chain.xml
similarity index 68%
rename from dubbo-rpc/dubbo-rpc-thrift/src/test/resources/dubbo-demo-provider.xml
rename to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/spring/dubbo-provider-chain.xml
index cf375c5..d677188 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/test/resources/dubbo-demo-provider.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider-backend/src/main/resources/spring/dubbo-provider-chain.xml
@@ -19,16 +19,17 @@
        xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
-    http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
-    <dubbo:application name="dubbo-demo-provider"/>
+    <dubbo:application metadata-type="remote" name="demo-provider-chain"/>
+    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
 
-    <dubbo:registry address="multicast://224.5.6.7:1234"/>
+    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 
-    <dubbo:protocol id="thrift" name="thrift"/>
+    <dubbo:protocol name="dubbo" port="-1"/>
 
-    <bean id="demoService" class="org.apache.dubbo.rpc.protocol.thrift.ThriftDemoImpl"/>
+    <bean id="chainService" class="org.apache.dubbo.demo.provider.chain.ChainServiceImpl"/>
 
-    <dubbo:service protocol="thrift" interface="org.apache.dubbo.rpc.gen.thrift.Demo$Iface" ref="demoService"/>
+    <dubbo:service group="g2" interface="org.apache.dubbo.demo.ChainService" ref="chainService"/>
 
-</beans>
\ No newline at end of file
+</beans>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
index c4590c2..c8cf138 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/pom.xml
@@ -64,15 +64,15 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
+            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
+            <artifactId>dubbo-rpc-dubbo</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-dubbo</artifactId>
+            <artifactId>dubbo-rpc-rest</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
diff --git a/dubbo-rpc/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java
similarity index 67%
rename from dubbo-rpc/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java
rename to dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java
index 1830aa2..8936e23 100644
--- a/dubbo-rpc/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/RestDemoServiceImpl.java
@@ -14,11 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.rpc.protocol.http;
+package org.apache.dubbo.demo.provider;
 
+
+import org.apache.dubbo.demo.RestDemoService;
 import org.apache.dubbo.rpc.RpcContext;
 
-public class HttpServiceImpl implements HttpService {
+import java.util.Map;
+
+public class RestDemoServiceImpl implements RestDemoService {
+    private static Map<String, Object> context;
     private boolean called;
 
     public String sayHello(String name) {
@@ -26,29 +31,24 @@ public class HttpServiceImpl implements HttpService {
         return "Hello, " + name;
     }
 
+
     public boolean isCalled() {
         return called;
     }
 
-    public void timeOut(int millis) {
-        try {
-            Thread.sleep(millis);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
+    @Override
+    public Integer hello(Integer a, Integer b) {
+        context = RpcContext.getContext().getObjectAttachments();
+        return a + b;
     }
 
-    public String customException() {
-        throw new MyException("custom exception");
+    @Override
+    public String error() {
+        throw new RuntimeException();
     }
 
-    static class MyException extends RuntimeException{
-
-        private static final long serialVersionUID = -3051041116483629056L;
-
-        public MyException(String message) {
-            super(message);
-        }
+    public static Map<String, Object> getAttachments() {
+        return context;
     }
 
     @Override
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
index 856ff73..386be3c 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
@@ -30,12 +30,16 @@
     <dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181?registry-type=service"/>
 
     <dubbo:protocol name="dubbo" port="-1"/>
+    <dubbo:protocol name="rest" port="-1"/>
 
     <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
     <bean id="greetingService" class="org.apache.dubbo.demo.provider.GreetingServiceImpl"/>
+    <bean id="restDemoService" class="org.apache.dubbo.demo.provider.RestDemoServiceImpl"/>
 
-    <dubbo:service interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService" registry="registry1"/>
-    <dubbo:service version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.demo.GreetingService"
-                   ref="greetingService"/>
+    <dubbo:service delay="5000" interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService" registry="registry1" protocol="dubbo"/>
+    <dubbo:service delay="5000" version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.demo.GreetingService"
+                   ref="greetingService" protocol="dubbo"/>
+    <dubbo:service delay="5000" version="1.0.0" timeout="5000" interface="org.apache.dubbo.demo.RestDemoService"
+                   ref="restDemoService" protocol="rest"/>
 
 </beans>
diff --git a/dubbo-demo/pom.xml b/dubbo-demo/pom.xml
index e636a23..573615a 100644
--- a/dubbo-demo/pom.xml
+++ b/dubbo-demo/pom.xml
@@ -35,6 +35,7 @@
         <module>dubbo-demo-xml</module>
         <module>dubbo-demo-annotation</module>
         <module>dubbo-demo-api</module>
+        <module>dubbo-demo-generic-call</module>
     </modules>
 
     <dependencyManagement>
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 32a70ae..8d84b38 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -766,5 +766,4 @@
             </build>
         </profile>
     </profiles>
-
 </project>
diff --git a/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
similarity index 74%
rename from dubbo-all/pom.xml
rename to dubbo-distribution/dubbo-all/pom.xml
index 262f811..e8e4406 100644
--- a/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -21,11 +21,11 @@
         <groupId>org.apache.dubbo</groupId>
         <artifactId>dubbo-parent</artifactId>
         <version>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
+        <relativePath>../../pom.xml</relativePath>
     </parent>
     <artifactId>dubbo</artifactId>
     <packaging>jar</packaging>
-    <name>dubbo-all</name>
+    <name>dubbo</name>
     <description>The all in one project of dubbo</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
@@ -61,20 +61,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-filter-cache</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-filter-validation</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-remoting-api</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -96,35 +82,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-etcd3</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-mina</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-grizzly</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-p2p</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-remoting-http</artifactId>
+            <artifactId>dubbo-remoting-zookeeper</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
             <optional>true</optional>
@@ -152,62 +110,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-http</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-rmi</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-hessian</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-webservice</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-thrift</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-native-thrift</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-memcached</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-redis</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-rest</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -215,13 +117,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-rpc-xml</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-grpc</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -236,13 +131,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-default</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-multicast</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -257,44 +145,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-redis</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-consul</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-etcd3</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-            <exclusions>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-core</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-netty</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-eureka</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-nacos</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -302,13 +152,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-registry-sofa</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-multiple</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -351,20 +194,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-container-log4j</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-container-logback</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-qos</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -379,20 +208,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-fastjson</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-fst</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-hessian2</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -400,13 +215,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-native-hession</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-jdk</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -414,41 +222,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-kryo</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-avro</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-protostuff</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-gson</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-protobuf</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-configcenter-zookeeper</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -470,30 +243,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-consul</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-configcenter-etcd</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-            <exclusions>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-core</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-netty</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-compatible</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -529,31 +278,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-consul</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-etcd</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-            <optional>true</optional>
-            <exclusions>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-core</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>io.grpc</groupId>
-                    <artifactId>grpc-netty</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-metadata-report-nacos</artifactId>
+            <artifactId>dubbo-auth</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
             <optional>true</optional>
@@ -634,39 +359,18 @@
                                     <include>org.apache.dubbo:dubbo-remoting-api</include>
                                     <include>org.apache.dubbo:dubbo-remoting-netty</include>
                                     <include>org.apache.dubbo:dubbo-remoting-netty4</include>
-                                    <include>org.apache.dubbo:dubbo-remoting-etcd3</include>
-                                    <include>org.apache.dubbo:dubbo-remoting-mina</include>
-                                    <include>org.apache.dubbo:dubbo-remoting-grizzly</include>
-                                    <include>org.apache.dubbo:dubbo-remoting-p2p</include>
-                                    <include>org.apache.dubbo:dubbo-remoting-http</include>
                                     <include>org.apache.dubbo:dubbo-remoting-zookeeper</include>
                                     <include>org.apache.dubbo:dubbo-rpc-api</include>
                                     <include>org.apache.dubbo:dubbo-rpc-dubbo</include>
                                     <include>org.apache.dubbo:dubbo-rpc-injvm</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-http</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-rmi</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-hessian</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-webservice</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-thrift</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-native-thrift</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-memcached</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-redis</include>
                                     <include>org.apache.dubbo:dubbo-rpc-rest</include>
-                                    <include>org.apache.dubbo:dubbo-rpc-xml</include>
                                     <include>org.apache.dubbo:dubbo-rpc-grpc</include>
-                                    <include>org.apache.dubbo:dubbo-filter-validation</include>
-                                    <include>org.apache.dubbo:dubbo-filter-cache</include>
                                     <include>org.apache.dubbo:dubbo-cluster</include>
                                     <include>org.apache.dubbo:dubbo-registry-api</include>
                                     <include>org.apache.dubbo:dubbo-registry-default</include>
                                     <include>org.apache.dubbo:dubbo-registry-multicast</include>
                                     <include>org.apache.dubbo:dubbo-registry-zookeeper</include>
-                                    <include>org.apache.dubbo:dubbo-registry-redis</include>
-                                    <include>org.apache.dubbo:dubbo-registry-consul</include>
-                                    <include>org.apache.dubbo:dubbo-registry-etcd3</include>
-                                    <include>org.apache.dubbo:dubbo-registry-eureka</include>
                                     <include>org.apache.dubbo:dubbo-registry-nacos</include>
-                                    <include>org.apache.dubbo:dubbo-registry-sofa</include>
                                     <include>org.apache.dubbo:dubbo-registry-multiple</include>
                                     <include>org.apache.dubbo:dubbo-registry-kubernetes</include>
                                     <include>org.apache.dubbo:dubbo-registry-dns</include>
@@ -674,33 +378,18 @@
                                     <include>org.apache.dubbo:dubbo-monitor-default</include>
                                     <include>org.apache.dubbo:dubbo-container-api</include>
                                     <include>org.apache.dubbo:dubbo-container-spring</include>
-                                    <include>org.apache.dubbo:dubbo-container-log4j</include>
-                                    <include>org.apache.dubbo:dubbo-container-logback</include>
-                                    <include>org.apache.dubbo:dubbo-qos</include>
                                     <include>org.apache.dubbo:dubbo-serialization-api</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-fastjson</include>
                                     <include>org.apache.dubbo:dubbo-serialization-hessian2</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-fst</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-kryo</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-avro</include>
                                     <include>org.apache.dubbo:dubbo-serialization-jdk</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-protostuff</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-gson</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-protobuf</include>
                                     <include>org.apache.dubbo:dubbo-configcenter-api</include>
-                                    <include>org.apache.dubbo:dubbo-configcenter-definition</include>
                                     <include>org.apache.dubbo:dubbo-configcenter-apollo</include>
                                     <include>org.apache.dubbo:dubbo-configcenter-zookeeper</include>
-                                    <include>org.apache.dubbo:dubbo-configcenter-consul</include>
-                                    <include>org.apache.dubbo:dubbo-configcenter-etcd</include>
                                     <include>org.apache.dubbo:dubbo-configcenter-nacos</include>
                                     <include>org.apache.dubbo:dubbo-metadata-api</include>
                                     <include>org.apache.dubbo:dubbo-metadata-report-redis</include>
                                     <include>org.apache.dubbo:dubbo-metadata-report-zookeeper</include>
-                                    <include>org.apache.dubbo:dubbo-metadata-report-consul</include>
-                                    <include>org.apache.dubbo:dubbo-metadata-report-etcd</include>
-                                    <include>org.apache.dubbo:dubbo-metadata-report-nacos</include>
-                                    <include>org.apache.dubbo:dubbo-serialization-native-hession</include>
+                                    <include>org.apache.dubbo:dubbo-qos</include>
+                                    <include>org.apache.dubbo:dubbo-auth</include>
                                 </includes>
                             </artifactSet>
                             <transformers>
@@ -996,6 +685,18 @@
                                         META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy
                                     </resource>
                                 </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.auth.spi.AccessKeyStorage
+                                    </resource>
+                                </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.auth.spi.Authenticator
+                                    </resource>
+                                </transformer>
 
                                 <!-- @since 2.7.8 -->
                                 <transformer
@@ -1010,7 +711,7 @@
                                 <filter>
                                     <artifact>org.apache.dubbo:dubbo</artifact>
                                     <excludes>
-                                        <!-- These two line is optional, it can remove some warn log -->
+                                        <!-- These following two line is optional, it can remove some warn log -->
                                         <exclude>com/**</exclude>
                                         <exclude>org/**</exclude>
                                         <!-- This one is required -->
diff --git a/dubbo-distribution/pom.xml b/dubbo-distribution/dubbo-apache-release/pom.xml
similarity index 91%
copy from dubbo-distribution/pom.xml
copy to dubbo-distribution/dubbo-apache-release/pom.xml
index 5b18042..fd0e677 100644
--- a/dubbo-distribution/pom.xml
+++ b/dubbo-distribution/dubbo-apache-release/pom.xml
@@ -14,18 +14,19 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.dubbo</groupId>
-        <artifactId>dubbo-parent</artifactId>
+        <artifactId>dubbo-distribution</artifactId>
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <artifactId>dubbo-distribution</artifactId>
+    <artifactId>dubbo-apache-release</artifactId>
     <packaging>pom</packaging>
-    <name>dubbo-distribution</name>
-    <description>The binary distribution module for dubbo temporarily</description>
+    <name>dubbo-apache-release</name>
+    <description>The apache source release</description>
     <properties>
         <skip_maven_deploy>true</skip_maven_deploy>
     </properties>
diff --git a/dubbo-distribution/src/assembly/bin-release.xml b/dubbo-distribution/dubbo-apache-release/src/assembly/bin-release.xml
similarity index 95%
rename from dubbo-distribution/src/assembly/bin-release.xml
rename to dubbo-distribution/dubbo-apache-release/src/assembly/bin-release.xml
index 7640993..570d190 100644
--- a/dubbo-distribution/src/assembly/bin-release.xml
+++ b/dubbo-distribution/dubbo-apache-release/src/assembly/bin-release.xml
@@ -24,7 +24,7 @@
     <baseDirectory>${project.build.finalName}-bin</baseDirectory>
     <fileSets>
         <fileSet>
-            <directory>../</directory>
+            <directory>../../</directory>
             <includes>
                 <include>DISCLAIMER</include>
                 <include>NOTICE</include>
@@ -32,7 +32,7 @@
             </includes>
         </fileSet>
         <fileSet>
-            <directory>../dubbo-demo</directory>
+            <directory>../../dubbo-demo</directory>
             <includes>
                 <include>README.md</include>
             </includes>
diff --git a/dubbo-distribution/src/assembly/source-release.xml b/dubbo-distribution/dubbo-apache-release/src/assembly/source-release.xml
similarity index 98%
rename from dubbo-distribution/src/assembly/source-release.xml
rename to dubbo-distribution/dubbo-apache-release/src/assembly/source-release.xml
index 2f26516..68bab41 100644
--- a/dubbo-distribution/src/assembly/source-release.xml
+++ b/dubbo-distribution/dubbo-apache-release/src/assembly/source-release.xml
@@ -25,7 +25,7 @@
 
     <fileSets>
         <fileSet>
-            <directory>../</directory>
+            <directory>../../</directory>
             <useDefaultExcludes>true</useDefaultExcludes>
             <includes>
                 <include>**/*</include>
diff --git a/dubbo-bom/pom.xml b/dubbo-distribution/dubbo-bom/pom.xml
similarity index 52%
rename from dubbo-bom/pom.xml
rename to dubbo-distribution/dubbo-bom/pom.xml
index a5b8b87..d3c1b33 100644
--- a/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -23,68 +23,13 @@
         <groupId>org.apache.dubbo</groupId>
         <artifactId>dubbo-parent</artifactId>
         <version>${revision}</version>
-        <relativePath>../pom.xml</relativePath>
+        <relativePath>../../pom.xml</relativePath>
     </parent>
 
     <artifactId>dubbo-bom</artifactId>
     <packaging>pom</packaging>
 
     <name>dubbo-bom</name>
-    <description>Dubbo dependencies BOM</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>
... 74896 lines suppressed ...