From 23f5b402a10d8f11b73dd3d9676b088cb7977f35 Mon Sep 17 00:00:00 2001
From: "vito.he" <hxmhlt@163.com>
Date: Tue, 10 Sep 2019 18:42:31 +0800
Subject: [PATCH] Add:support single registry config in config center

---
 common/config/environment.go         |  23 ++--
 common/config/environment_test.go    |   2 +-
 common/constant/key.go               |  13 ++-
 config/base_config.go                | 106 ++++++++++++++----
 config/base_config_test.go           | 158 ++++++++++++++++++++++++++-
 config/config_center_config.go       |  21 ++--
 config/config_loader.go              |   7 ++
 config/config_loader_test.go         |  93 +++++++++++++++-
 config/consumer_config.go            |   1 +
 config/provider_config.go            |   1 +
 config/reference_config_test.go      |  61 +++++++++--
 config/registry_config.go            |  49 +++++----
 config/service_config_test.go        |  54 ++++++++-
 config_center/mock_dynamic_config.go |   8 +-
 14 files changed, 515 insertions(+), 82 deletions(-)

diff --git a/common/config/environment.go b/common/config/environment.go
index a9447aa12..931f04609 100644
--- a/common/config/environment.go
+++ b/common/config/environment.go
@@ -36,6 +36,7 @@ type Environment struct {
 	configCenterFirst    bool
 	externalConfigs      sync.Map
 	externalConfigMap    sync.Map
+	appExternalConfigMap sync.Map
 	dynamicConfiguration config_center.DynamicConfiguration
 }
 
@@ -50,6 +51,9 @@ func GetEnvInstance() *Environment {
 	})
 	return instance
 }
+func NewEnvInstance() {
+	instance = &Environment{configCenterFirst: true}
+}
 
 //func (env *Environment) SetConfigCenterFirst() {
 //	env.configCenterFirst = true
@@ -65,11 +69,17 @@ func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) {
 	}
 }
 
+func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string) {
+	for k, v := range externalMap {
+		env.appExternalConfigMap.Store(k, v)
+	}
+}
+
 func (env *Environment) Configuration() *list.List {
 	list := list.New()
-	memConf := newInmemoryConfiguration()
-	memConf.setProperties(&(env.externalConfigMap))
-	list.PushBack(memConf)
+	// The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
+	list.PushFront(newInmemoryConfiguration(&(env.externalConfigMap)))
+	list.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap)))
 	return list
 }
 
@@ -85,11 +95,8 @@ type InmemoryConfiguration struct {
 	store *sync.Map
 }
 
-func newInmemoryConfiguration() *InmemoryConfiguration {
-	return &InmemoryConfiguration{}
-}
-func (conf *InmemoryConfiguration) setProperties(p *sync.Map) {
-	conf.store = p
+func newInmemoryConfiguration(p *sync.Map) *InmemoryConfiguration {
+	return &InmemoryConfiguration{store: p}
 }
 
 func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) {
diff --git a/common/config/environment_test.go b/common/config/environment_test.go
index 0e1d679bc..2d84dc4ae 100644
--- a/common/config/environment_test.go
+++ b/common/config/environment_test.go
@@ -39,7 +39,7 @@ func TestEnvironment_UpdateExternalConfigMap(t *testing.T) {
 func TestEnvironment_ConfigurationAndGetProperty(t *testing.T) {
 	GetEnvInstance().UpdateExternalConfigMap(map[string]string{"1": "2"})
 	list := GetEnvInstance().Configuration()
-	ok, v := list.Front().Value.(*InmemoryConfiguration).GetProperty("1")
+	ok, v := list.Back().Value.(*InmemoryConfiguration).GetProperty("1")
 	assert.True(t, ok)
 	assert.Equal(t, "2", v)
 }
diff --git a/common/constant/key.go b/common/constant/key.go
index feb6fc9aa..1b25d11ed 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -91,12 +91,13 @@ const (
 	COMPATIBLE_CONFIG_KEY = "compatible_config"
 )
 const (
-	RegistryConfigPrefix  = "dubbo.registries."
-	ReferenceConfigPrefix = "dubbo.reference."
-	ServiceConfigPrefix   = "dubbo.service."
-	ProtocolConfigPrefix  = "dubbo.protocols."
-	ProviderConfigPrefix  = "dubbo.provider."
-	ConsumerConfigPrefix  = "dubbo.consumer."
+	RegistryConfigPrefix       = "dubbo.registries."
+	SingleRegistryConfigPrefix = "dubbo.registry."
+	ReferenceConfigPrefix      = "dubbo.reference."
+	ServiceConfigPrefix        = "dubbo.service."
+	ProtocolConfigPrefix       = "dubbo.protocols."
+	ProviderConfigPrefix       = "dubbo.provider."
+	ConsumerConfigPrefix       = "dubbo.consumer."
 )
 
 const (
diff --git a/config/base_config.go b/config/base_config.go
index 6c85b5e46..e51d8b955 100644
--- a/config/base_config.go
+++ b/config/base_config.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"reflect"
 	"strconv"
+	"strings"
 )
 
 import (
@@ -72,15 +73,43 @@ func (c *BaseConfig) prepareEnvironment() error {
 		logger.Errorf("Get config content in dynamic configuration error , error message is %v", err)
 		return perrors.WithStack(err)
 	}
+	var appGroup string
+	var appContent string
+	if providerConfig != nil && providerConfig.ApplicationConfig != nil &&
+		reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ProviderConfig" {
+		appGroup = providerConfig.ApplicationConfig.Name
+	} else if consumerConfig != nil && consumerConfig.ApplicationConfig != nil &&
+		reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ConsumerConfig" {
+		appGroup = consumerConfig.ApplicationConfig.Name
+	}
+
+	if len(appGroup) != 0 {
+		configFile := c.ConfigCenterConfig.AppConfigFile
+		if len(configFile) == 0 {
+			configFile = c.ConfigCenterConfig.ConfigFile
+		}
+		appContent, err = dynamicConfig.GetConfig(configFile, config_center.WithGroup(appGroup))
+	}
+	//global config file
 	mapContent, err := dynamicConfig.Parser().Parse(content)
 	if err != nil {
 		return perrors.WithStack(err)
 	}
 	config.GetEnvInstance().UpdateExternalConfigMap(mapContent)
+
+	//appGroup config file
+	if len(appContent) != 0 {
+		appMapConent, err := dynamicConfig.Parser().Parse(appContent)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		config.GetEnvInstance().UpdateAppExternalConfigMap(appMapConent)
+	}
+
 	return nil
 }
 
-func getKeyPrefix(val reflect.Value, id reflect.Value) string {
+func getKeyPrefix(val reflect.Value, id reflect.Value) []string {
 	var (
 		prefix string
 		idStr  string
@@ -94,21 +123,42 @@ func getKeyPrefix(val reflect.Value, id reflect.Value) string {
 	} else {
 		prefix = val.MethodByName("Prefix").Call(nil)[0].String()
 	}
+	var retPrefixs []string
 
-	if idStr != "" {
-		return prefix + idStr + "."
-	} else {
-		return prefix
+	for _, pfx := range strings.Split(prefix, "|") {
+		if idStr != "" {
+			retPrefixs = append(retPrefixs, pfx+idStr+".")
+		} else {
+			retPrefixs = append(retPrefixs, pfx)
+		}
 	}
-}
+	return retPrefixs
 
+}
+func getPtrElement(v reflect.Value) reflect.Value {
+	if v.Kind() == reflect.Ptr {
+		v = v.Elem()
+		if v.Kind() == reflect.Ptr {
+			return getPtrElement(v)
+		}
+	}
+	return v
+}
 func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryConfiguration) {
 	for i := 0; i < val.NumField(); i++ {
 		if key := val.Type().Field(i).Tag.Get("property"); key != "-" && key != "" {
 			f := val.Field(i)
 			if f.IsValid() {
 				setBaseValue := func(f reflect.Value) {
-					ok, value := config.GetProperty(getKeyPrefix(val, id) + key)
+					var ok bool
+					var value string
+					prefixs := getKeyPrefix(val, id)
+					for _, pfx := range prefixs {
+						ok, value = config.GetProperty(pfx + key)
+						if ok {
+							break
+						}
+					}
 					if ok {
 						switch f.Kind() {
 						case reflect.Int64:
@@ -154,12 +204,12 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 
 				}
 
-				setBaseValue(f)
 				if f.Kind() == reflect.Ptr {
-					if f.Elem().Kind() == reflect.Struct {
-						setFieldValue(f.Elem(), reflect.Value{}, config)
+					f = getPtrElement(f)
+					if f.Kind() == reflect.Struct {
+						setFieldValue(f, reflect.Value{}, config)
 					} else {
-						setBaseValue(f.Elem())
+						setBaseValue(f)
 					}
 				}
 
@@ -170,10 +220,11 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 					for i := 0; i < f.Len(); i++ {
 						e := f.Index(i)
 						if e.Kind() == reflect.Ptr {
-							if e.Elem().Kind() == reflect.Struct {
-								setFieldValue(e.Elem(), reflect.Value{}, config)
+							e = getPtrElement(e)
+							if e.Kind() == reflect.Struct {
+								setFieldValue(e, reflect.Value{}, config)
 							} else {
-								setBaseValue(e.Elem())
+								setBaseValue(e)
 							}
 						}
 
@@ -186,10 +237,16 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 						//initiate config
 						s := reflect.New(f.Type().Elem().Elem())
 						prefix := s.MethodByName("Prefix").Call(nil)[0].String()
-						m := config.GetSubProperty(prefix)
-						for k := range m {
-							f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
+						for _, pfx := range strings.Split(prefix, "|") {
+							m := config.GetSubProperty(pfx)
+							if m != nil {
+								for k := range m {
+									f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
+								}
+							}
+
 						}
+
 					}
 
 					//iter := f.MapRange()
@@ -198,10 +255,11 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 						v := f.MapIndex(k)
 						switch v.Kind() {
 						case reflect.Ptr:
-							if v.Elem().Kind() == reflect.Struct {
-								setFieldValue(v.Elem(), k, config)
+							v = getPtrElement(v)
+							if v.Kind() == reflect.Struct {
+								setFieldValue(v, k, config)
 							} else {
-								setBaseValue(v.Elem())
+								setBaseValue(v)
 							}
 						case reflect.Int64, reflect.String, reflect.Bool, reflect.Float64:
 							setBaseValue(v)
@@ -210,6 +268,7 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 						}
 					}
 				}
+				setBaseValue(f)
 
 			}
 		}
@@ -217,8 +276,13 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 }
 func (c *BaseConfig) fresh() {
 	configList := config.GetEnvInstance().Configuration()
-	config := configList.Front().Value.(*config.InmemoryConfiguration)
+	for element := configList.Front(); element != nil; element = element.Next() {
+		config := element.Value.(*config.InmemoryConfiguration)
+		c.freshInternalConfig(config)
+	}
+}
 
+func (c *BaseConfig) freshInternalConfig(config *config.InmemoryConfiguration) {
 	//reflect to init struct
 	tp := reflect.ValueOf(c.fatherConfig).Elem().Type()
 	initializeStruct(tp, reflect.ValueOf(c.fatherConfig).Elem())
diff --git a/config/base_config_test.go b/config/base_config_test.go
index d07d983f6..239bece47 100644
--- a/config/base_config_test.go
+++ b/config/base_config_test.go
@@ -125,6 +125,162 @@ func Test_refresh(t *testing.T) {
 	assert.Equal(t, "dubbo", father.ApplicationConfig.Name)
 }
 
+func Test_appExternal_refresh(t *testing.T) {
+	c := &BaseConfig{}
+	mockMap := map[string]string{}
+	mockMap["dubbo.registries.shanghai_reg1.protocol"] = "mock100"
+	mockMap["dubbo.reference.com.MockService.MockService.retries"] = "10"
+	mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10"
+	mockMap["dubbo.consumer.check"] = "false"
+	mockMap["dubbo.application.name"] = "dubbo"
+
+	config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap)
+	mockMap["dubbo.consumer.check"] = "true"
+	config.GetEnvInstance().UpdateExternalConfigMap(mockMap)
+	father := &ConsumerConfig{
+		Check: &[]bool{true}[0],
+		ApplicationConfig: &ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registries: map[string]*RegistryConfig{
+			//"shanghai_reg1": {
+			//	id:         "shanghai_reg1",
+			//	Protocol:   "mock",
+			//	TimeoutStr: "2s",
+			//	Group:      "shanghai_idc",
+			//	Address:    "127.0.0.1:2181",
+			//	Username:   "user1",
+			//	Password:   "pwd1",
+			//},
+			"shanghai_reg2": {
+				Protocol:   "mock",
+				TimeoutStr: "2s",
+				Group:      "shanghai_idc",
+				Address:    "127.0.0.2:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			"hangzhou_reg1": {
+				Protocol:   "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.3:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			"hangzhou_reg2": {
+				Protocol:   "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.4:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+		},
+		References: map[string]*ReferenceConfig{
+			"MockService": {
+				InterfaceName: "com.MockService",
+				Protocol:      "mock",
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []*MethodConfig{
+					{
+						InterfaceId:   "MockService",
+						InterfaceName: "com.MockService",
+						Name:          "GetUser",
+						Retries:       2,
+						Loadbalance:   "random",
+					},
+					{
+						InterfaceId:   "MockService",
+						InterfaceName: "com.MockService",
+						Name:          "GetUser1",
+						Retries:       2,
+						Loadbalance:   "random",
+					},
+				},
+			},
+		},
+	}
+
+	c.SetFatherConfig(father)
+	c.fresh()
+	assert.Equal(t, "mock100", father.Registries["shanghai_reg1"].Protocol)
+	assert.Equal(t, int64(10), father.References["MockService"].Retries)
+
+	assert.Equal(t, int64(10), father.References["MockService"].Methods[0].Retries)
+	assert.Equal(t, &[]bool{true}[0], father.Check)
+	assert.Equal(t, "dubbo", father.ApplicationConfig.Name)
+}
+
+func Test_refresh_singleRegistry(t *testing.T) {
+	c := &BaseConfig{}
+	mockMap := map[string]string{}
+	mockMap["dubbo.registry.address"] = "mock100://127.0.0.1:2181"
+	mockMap["dubbo.reference.com.MockService.MockService.retries"] = "10"
+	mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10"
+	mockMap["dubbo.consumer.check"] = "false"
+	mockMap["dubbo.application.name"] = "dubbo"
+
+	config.GetEnvInstance().UpdateExternalConfigMap(mockMap)
+
+	father := &ConsumerConfig{
+		Check: &[]bool{true}[0],
+		ApplicationConfig: &ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registries: map[string]*RegistryConfig{},
+		Registry:   &RegistryConfig{},
+		References: map[string]*ReferenceConfig{
+			"MockService": {
+				InterfaceName: "com.MockService",
+				Protocol:      "mock",
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []*MethodConfig{
+					{
+						InterfaceId:   "MockService",
+						InterfaceName: "com.MockService",
+						Name:          "GetUser",
+						Retries:       2,
+						Loadbalance:   "random",
+					},
+					{
+						InterfaceId:   "MockService",
+						InterfaceName: "com.MockService",
+						Name:          "GetUser1",
+						Retries:       2,
+						Loadbalance:   "random",
+					},
+				},
+			},
+		},
+	}
+
+	c.SetFatherConfig(father)
+	c.fresh()
+	assert.Equal(t, "mock100://127.0.0.1:2181", father.Registry.Address)
+	assert.Equal(t, int64(10), father.References["MockService"].Retries)
+
+	assert.Equal(t, int64(10), father.References["MockService"].Methods[0].Retries)
+	assert.Equal(t, &[]bool{false}[0], father.Check)
+	assert.Equal(t, "dubbo", father.ApplicationConfig.Name)
+}
+
 func Test_refreshProvider(t *testing.T) {
 	c := &BaseConfig{}
 	mockMap := map[string]string{}
@@ -233,7 +389,7 @@ func Test_startConfigCenter(t *testing.T) {
 	}}
 	err := c.startConfigCenter(context.Background())
 	assert.NoError(t, err)
-	b, v := config.GetEnvInstance().Configuration().Front().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
+	b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
 	assert.True(t, b)
 	assert.Equal(t, "ikurento.com", v)
 }
diff --git a/config/config_center_config.go b/config/config_center_config.go
index 47efce126..e7bbd8e24 100644
--- a/config/config_center_config.go
+++ b/config/config_center_config.go
@@ -23,14 +23,15 @@ import (
 )
 
 type ConfigCenterConfig struct {
-	context    context.Context
-	Protocol   string `required:"true"  yaml:"protocol"  json:"protocol,omitempty"`
-	Address    string `yaml:"address" json:"address,omitempty"`
-	Cluster    string `yaml:"cluster" json:"cluster,omitempty"`
-	Group      string `default:"dubbo" yaml:"group" json:"group,omitempty"`
-	Username   string `yaml:"username" json:"username,omitempty"`
-	Password   string `yaml:"password" json:"password,omitempty"`
-	ConfigFile string `default:"dubbo.properties" yaml:"config_file"  json:"config_file,omitempty"`
-	TimeoutStr string `yaml:"timeout"  json:"timeout,omitempty"`
-	timeout    time.Duration
+	context       context.Context
+	Protocol      string `required:"true"  yaml:"protocol"  json:"protocol,omitempty"`
+	Address       string `yaml:"address" json:"address,omitempty"`
+	Cluster       string `yaml:"cluster" json:"cluster,omitempty"`
+	Group         string `default:"dubbo" yaml:"group" json:"group,omitempty"`
+	Username      string `yaml:"username" json:"username,omitempty"`
+	Password      string `yaml:"password" json:"password,omitempty"`
+	ConfigFile    string `default:"dubbo.properties" yaml:"config_file"  json:"config_file,omitempty"`
+	AppConfigFile string `yaml:"app_config_file"  json:"app_config_file,omitempty"`
+	TimeoutStr    string `yaml:"timeout"  json:"timeout,omitempty"`
+	timeout       time.Duration
 }
diff --git a/config/config_loader.go b/config/config_loader.go
index c5127c8c4..8561828ae 100644
--- a/config/config_loader.go
+++ b/config/config_loader.go
@@ -54,6 +54,11 @@ func init() {
 		providerConfig = nil
 	}
 }
+func checkRegistries(registries map[string]*RegistryConfig, singleRegistry *RegistryConfig) {
+	if len(registries) == 0 && singleRegistry != nil {
+		registries[constant.DEFAULT_KEY] = singleRegistry
+	}
+}
 
 // Dubbo Init
 func Load() {
@@ -64,6 +69,7 @@ func Load() {
 		if err := configCenterRefreshConsumer(); err != nil {
 			logger.Errorf("[consumer config center refresh] %#v", err)
 		}
+		checkRegistries(consumerConfig.Registries, consumerConfig.Registry)
 		for key, ref := range consumerConfig.References {
 			if ref.Generic {
 				genericService := NewGenericService(key)
@@ -116,6 +122,7 @@ func Load() {
 		if err := configCenterRefreshProvider(); err != nil {
 			logger.Errorf("[provider config center refresh] %#v", err)
 		}
+		checkRegistries(providerConfig.Registries, providerConfig.Registry)
 		for key, svs := range providerConfig.Services {
 			rpcService := GetProviderService(key)
 			if rpcService == nil {
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
index 107fea0b1..3562bf5a1 100644
--- a/config/config_loader_test.go
+++ b/config/config_loader_test.go
@@ -29,6 +29,8 @@ import (
 import (
 	"github.com/apache/dubbo-go/cluster/cluster_impl"
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/config"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/proxy/proxy_factory"
 	"github.com/apache/dubbo-go/config_center"
@@ -58,8 +60,36 @@ func TestConfigLoader(t *testing.T) {
 }
 
 func TestLoad(t *testing.T) {
-	doInit()
-	doinit()
+	doInitConsumer()
+	doInitProvider()
+
+	ms := &MockService{}
+	SetConsumerService(ms)
+	SetProviderService(ms)
+
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory)
+
+	Load()
+
+	assert.Equal(t, ms, GetRPCService(ms.Reference()))
+	ms2 := &struct {
+		MockService
+	}{}
+	RPCService(ms2)
+	assert.NotEqual(t, ms2, GetRPCService(ms2.Reference()))
+
+	conServices = map[string]common.RPCService{}
+	proServices = map[string]common.RPCService{}
+	common.ServiceMap.UnRegister("mock", "MockService")
+	consumerConfig = nil
+	providerConfig = nil
+}
+
+func TestLoadWithSingleReg(t *testing.T) {
+	doInitConsumerWithSingleRegistry()
+	doInitProviderWithSingleRegistry()
 
 	ms := &MockService{}
 	SetConsumerService(ms)
@@ -86,8 +116,8 @@ func TestLoad(t *testing.T) {
 }
 
 func TestWithNoRegLoad(t *testing.T) {
-	doInit()
-	doinit()
+	doInitConsumer()
+	doInitProvider()
 	providerConfig.Services["MockService"].Registry = ""
 	consumerConfig.References["MockService"].Registry = ""
 	ms := &MockService{}
@@ -145,3 +175,58 @@ func TestConfigLoaderWithConfigCenter(t *testing.T) {
 	assert.Equal(t, "127.0.0.1:2181", consumerConfig.Registries["hangzhouzk"].Address)
 
 }
+
+func TestConfigLoaderWithConfigCenterSingleRegistry(t *testing.T) {
+	consumerConfig = nil
+	providerConfig = nil
+	config.NewEnvInstance()
+	extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory {
+		return &config_center.MockDynamicConfigurationFactory{Content: `
+	dubbo.consumer.request_timeout=5s
+	dubbo.consumer.connect_timeout=5s
+	dubbo.application.organization=ikurento.com
+	dubbo.application.name=BDTService
+	dubbo.application.module=dubbogo user-info server
+	dubbo.application.version=0.0.1
+	dubbo.application.owner=ZX
+	dubbo.application.environment=dev
+	dubbo.registry.address=mock://127.0.0.1:2182
+	dubbo.service.com.ikurento.user.UserProvider.protocol=dubbo
+	dubbo.service.com.ikurento.user.UserProvider.interface=com.ikurento.user.UserProvider
+	dubbo.service.com.ikurento.user.UserProvider.loadbalance=random
+	dubbo.service.com.ikurento.user.UserProvider.warmup=100
+	dubbo.service.com.ikurento.user.UserProvider.cluster=failover
+	dubbo.protocols.jsonrpc1.name=jsonrpc
+	dubbo.protocols.jsonrpc1.ip=127.0.0.1
+	dubbo.protocols.jsonrpc1.port=20001
+`}
+	})
+
+	conPath, err := filepath.Abs("./testdata/consumer_config_with_configcenter.yml")
+	assert.NoError(t, err)
+	proPath, err := filepath.Abs("./testdata/provider_config.yml")
+	assert.NoError(t, err)
+
+	assert.Nil(t, consumerConfig)
+	assert.Equal(t, ConsumerConfig{}, GetConsumerConfig())
+	assert.Nil(t, providerConfig)
+	assert.Equal(t, ProviderConfig{}, GetProviderConfig())
+
+	err = ConsumerInit(conPath)
+	configCenterRefreshConsumer()
+	checkRegistries(consumerConfig.Registries, consumerConfig.Registry)
+	assert.NoError(t, err)
+	err = ProviderInit(proPath)
+	configCenterRefreshProvider()
+	checkRegistries(providerConfig.Registries, providerConfig.Registry)
+	assert.NoError(t, err)
+
+	assert.NotNil(t, consumerConfig)
+	assert.NotEqual(t, ConsumerConfig{}, GetConsumerConfig())
+	assert.NotNil(t, providerConfig)
+	assert.NotEqual(t, ProviderConfig{}, GetProviderConfig())
+
+	assert.Equal(t, "BDTService", consumerConfig.ApplicationConfig.Name)
+	assert.Equal(t, "mock://127.0.0.1:2182", consumerConfig.Registries[constant.DEFAULT_KEY].Address)
+
+}
diff --git a/config/consumer_config.go b/config/consumer_config.go
index ebeb7fe75..48d1a2760 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -51,6 +51,7 @@ type ConsumerConfig struct {
 	ProxyFactory    string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
 	Check           *bool  `yaml:"check"  json:"check,omitempty" property:"check"`
 
+	Registry     *RegistryConfig             `yaml:"registry" json:"registry,omitempty" property:"registry"`
 	Registries   map[string]*RegistryConfig  `yaml:"registries" json:"registries,omitempty" property:"registries"`
 	References   map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"`
 	ProtocolConf interface{}                 `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
diff --git a/config/provider_config.go b/config/provider_config.go
index db8abaf73..726d05ae6 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -42,6 +42,7 @@ type ProviderConfig struct {
 	ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
 
 	ApplicationConfig *ApplicationConfig         `yaml:"application_config" json:"application_config,omitempty" property:"application_config"`
+	Registry          *RegistryConfig            `yaml:"registry" json:"registry,omitempty" property:"registry"`
 	Registries        map[string]*RegistryConfig `yaml:"registries" json:"registries,omitempty" property:"registries"`
 	Services          map[string]*ServiceConfig  `yaml:"services" json:"services,omitempty" property:"services"`
 	Protocols         map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"`
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index d36fa04d7..4dbdd31d8 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -36,7 +36,7 @@ import (
 
 var regProtocol protocol.Protocol
 
-func doInit() {
+func doInitConsumer() {
 	consumerConfig = &ConsumerConfig{
 		ApplicationConfig: &ApplicationConfig{
 			Organization: "dubbo_org",
@@ -110,8 +110,53 @@ func doInit() {
 	}
 }
 
+func doInitConsumerWithSingleRegistry() {
+	consumerConfig = &ConsumerConfig{
+		ApplicationConfig: &ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registry: &RegistryConfig{
+			Address:  "mock://27.0.0.1:2181",
+			Username: "user1",
+			Password: "pwd1",
+		},
+		Registries: map[string]*RegistryConfig{},
+		References: map[string]*ReferenceConfig{
+			"MockService": {
+				Params: map[string]string{
+					"serviceid": "soa.mock",
+					"forks":     "5",
+				},
+				InterfaceName: "com.MockService",
+				Protocol:      "mock",
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []*MethodConfig{
+					{
+						Name:        "GetUser",
+						Retries:     2,
+						Loadbalance: "random",
+					},
+					{
+						Name:        "GetUser1",
+						Retries:     2,
+						Loadbalance: "random",
+					},
+				},
+			},
+		},
+	}
+}
+
 func Test_ReferMultireg(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 
@@ -124,7 +169,7 @@ func Test_ReferMultireg(t *testing.T) {
 }
 
 func Test_Refer(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 
@@ -137,7 +182,7 @@ func Test_Refer(t *testing.T) {
 	consumerConfig = nil
 }
 func Test_ReferP2P(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
 	m := consumerConfig.References["MockService"]
 	m.Url = "dubbo://127.0.0.1:20000"
@@ -151,7 +196,7 @@ func Test_ReferP2P(t *testing.T) {
 }
 
 func Test_ReferMultiP2P(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
 	m := consumerConfig.References["MockService"]
 	m.Url = "dubbo://127.0.0.1:20000;dubbo://127.0.0.2:20000"
@@ -165,7 +210,7 @@ func Test_ReferMultiP2P(t *testing.T) {
 }
 
 func Test_ReferMultiP2PWithReg(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
 	extension.SetProtocol("registry", GetProtocol)
 	m := consumerConfig.References["MockService"]
@@ -180,7 +225,7 @@ func Test_ReferMultiP2PWithReg(t *testing.T) {
 }
 
 func Test_Implement(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 	for _, reference := range consumerConfig.References {
@@ -193,7 +238,7 @@ func Test_Implement(t *testing.T) {
 }
 
 func Test_Forking(t *testing.T) {
-	doInit()
+	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
 	extension.SetProtocol("registry", GetProtocol)
 	m := consumerConfig.References["MockService"]
diff --git a/config/registry_config.go b/config/registry_config.go
index 0abdab810..480377754 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -43,7 +43,7 @@ type RegistryConfig struct {
 }
 
 func (*RegistryConfig) Prefix() string {
-	return constant.RegistryConfigPrefix
+	return constant.RegistryConfigPrefix + "|" + constant.SingleRegistryConfigPrefix
 }
 
 func loadRegistries(targetRegistries string, registries map[string]*RegistryConfig, roleType common.RoleType) []*common.URL {
@@ -73,27 +73,22 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 				url common.URL
 				err error
 			)
-			if addresses := strings.Split(registryConf.Address, ","); len(addresses) > 1 {
-				url, err = common.NewURL(
-					context.Background(),
-					constant.REGISTRY_PROTOCOL+"://"+addresses[0],
-					common.WithParams(registryConf.getUrlMap(roleType)),
-					common.WithUsername(registryConf.Username),
-					common.WithPassword(registryConf.Password),
-					common.WithLocation(registryConf.Address),
-				)
-			} else {
-				url, err = common.NewURL(
-					context.Background(),
-					constant.REGISTRY_PROTOCOL+"://"+registryConf.Address,
-					common.WithParams(registryConf.getUrlMap(roleType)),
-					common.WithUsername(registryConf.Username),
-					common.WithPassword(registryConf.Password),
-				)
-			}
+
+			addresses := strings.Split(registryConf.Address, ",")
+			address := addresses[0]
+			address = traslateRegistryConf(address, registryConf)
+			url, err = common.NewURL(
+				context.Background(),
+				constant.REGISTRY_PROTOCOL+"://"+address,
+				common.WithParams(registryConf.getUrlMap(roleType)),
+				common.WithUsername(registryConf.Username),
+				common.WithPassword(registryConf.Password),
+				common.WithLocation(registryConf.Address),
+			)
 
 			if err != nil {
-				logger.Errorf("The registry id:%s url is invalid ,and will skip the registry, error: %#v", k, err)
+				logger.Errorf("The registry id:%s url is invalid , error: %#v", k, err)
+				panic(err)
 			} else {
 				urls = append(urls, &url)
 			}
@@ -115,3 +110,17 @@ func (regconfig *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values
 	}
 	return urlMap
 }
+
+func traslateRegistryConf(address string, registryConf *RegistryConfig) string {
+	if strings.Contains(address, "://") {
+		translatedUrl, err := url.Parse(address)
+		if err != nil {
+			logger.Errorf("The registry  url is invalid , error: %#v", err)
+			panic(err)
+		}
+		address = translatedUrl.Host
+		registryConf.Protocol = translatedUrl.Scheme
+		registryConf.Address = strings.Replace(registryConf.Address, translatedUrl.Scheme+"://", "", -1)
+	}
+	return address
+}
diff --git a/config/service_config_test.go b/config/service_config_test.go
index f1de3ac08..d229ce4d2 100644
--- a/config/service_config_test.go
+++ b/config/service_config_test.go
@@ -25,7 +25,7 @@ import (
 	"github.com/apache/dubbo-go/common/extension"
 )
 
-func doinit() {
+func doInitProvider() {
 	providerConfig = &ProviderConfig{
 		ApplicationConfig: &ApplicationConfig{
 			Organization: "dubbo_org",
@@ -104,8 +104,58 @@ func doinit() {
 	}
 }
 
+func doInitProviderWithSingleRegistry() {
+	providerConfig = &ProviderConfig{
+		ApplicationConfig: &ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registry: &RegistryConfig{
+			Address:  "mock://127.0.0.1:2181",
+			Username: "user1",
+			Password: "pwd1",
+		},
+		Registries: map[string]*RegistryConfig{},
+		Services: map[string]*ServiceConfig{
+			"MockService": {
+				InterfaceName: "com.MockService",
+				Protocol:      "mock",
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []*MethodConfig{
+					{
+						Name:        "GetUser",
+						Retries:     2,
+						Loadbalance: "random",
+						Weight:      200,
+					},
+					{
+						Name:        "GetUser1",
+						Retries:     2,
+						Loadbalance: "random",
+						Weight:      200,
+					},
+				},
+			},
+		},
+		Protocols: map[string]*ProtocolConfig{
+			"mock": {
+				Name: "mock",
+				Ip:   "127.0.0.1",
+				Port: "20000",
+			},
+		},
+	}
+}
+
 func Test_Export(t *testing.T) {
-	doinit()
+	doInitProvider()
 	extension.SetProtocol("registry", GetProtocol)
 
 	for i := range providerConfig.Services {
diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go
index e8f10d45f..47b509231 100644
--- a/config_center/mock_dynamic_config.go
+++ b/config_center/mock_dynamic_config.go
@@ -32,7 +32,9 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
-type MockDynamicConfigurationFactory struct{}
+type MockDynamicConfigurationFactory struct {
+	Content string
+}
 
 var (
 	once                 sync.Once
@@ -44,6 +46,7 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.UR
 	once.Do(func() {
 		dynamicConfiguration = &MockDynamicConfiguration{listener: map[string]ConfigurationListener{}}
 		dynamicConfiguration.SetParser(&parser.DefaultConfigurationParser{})
+
 		dynamicConfiguration.content = `
 	dubbo.consumer.request_timeout=5s
 	dubbo.consumer.connect_timeout=5s
@@ -69,6 +72,9 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.UR
 	dubbo.protocols.jsonrpc1.port=20001
 `
 	})
+	if len(f.Content) != 0 {
+		dynamicConfiguration.content = f.Content
+	}
 	return dynamicConfiguration, err
 
 }
-- 
GitLab