diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 0000000000..8d84e695a5 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,5 @@ +notifications: + commits: commits@dubbo.apache.org + issues: notifications@dubbo.apache.org + pullrequests: notifications@dubbo.apache.org + jira_options: link label link label diff --git a/.travis.yml b/.travis.yml index 1b46f5d872..566c88ece0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,32 @@ -language: go +dist: trusty +sudo: required +# define the dependence env +language: go os: - linux - go: - "1.13" - +services: + - docker env: - GO111MODULE=on - install: true +# define ci-stage script: + # license-check + - echo 'start license check' - go fmt ./... && [[ -z `git status -s` ]] - sh before_validate_license.sh - chmod u+x /tmp/tools/license/license-header-checker - /tmp/tools/license/license-header-checker -v -a -r -i vendor /tmp/tools/license/license.txt . go && [[ -z `git status -s` ]] + # unit-test + - echo 'start unit-test' - chmod u+x before_ut.sh && ./before_ut.sh - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic + # integrate-test + - chmod +x integrate_test.sh && ./integrate_test.sh after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 75f5406857..9bade617c8 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Apache License, Version 2.0 ## Release note ## +[v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0) + [v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0) [v1.2.0 - Nov 15, 2019](https://github.com/apache/dubbo-go/releases/tag/v1.2.0) diff --git a/README_CN.md b/README_CN.md index bcb3883593..180759f366 100644 --- a/README_CN.md +++ b/README_CN.md @@ -15,6 +15,8 @@ Apache License, Version 2.0 ## 发布日志 ## +[v1.4.0 - 2020年3月17日](https://github.com/apache/dubbo-go/releases/tag/v1.4.0) + [v1.3.0 - 2020年3月1日](https://github.com/apache/dubbo-go/releases/tag/v1.3.0) [v1.2.0 - 2019年11月15日](https://github.com/apache/dubbo-go/releases/tag/v1.2.0) diff --git a/cluster/cluster_impl/available_cluster_invoker_test.go b/cluster/cluster_impl/available_cluster_invoker_test.go index c2cebd3843..61d1c93452 100644 --- a/cluster/cluster_impl/available_cluster_invoker_test.go +++ b/cluster/cluster_impl/available_cluster_invoker_test.go @@ -42,7 +42,7 @@ var ( availableUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) -func registerAvailable(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +func registerAvailable(invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) availableCluster := NewAvailableCluster() @@ -60,7 +60,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerAvailable(t, invoker) + clusterInvoker := registerAvailable(invoker) mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().IsAvailable().Return(true) @@ -76,7 +76,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerAvailable(t, invoker) + clusterInvoker := registerAvailable(invoker) invoker.EXPECT().IsAvailable().Return(false) diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go index 1279999412..cabd6c5f17 100644 --- a/cluster/cluster_impl/base_cluster_invoker.go +++ b/cluster/cluster_impl/base_cluster_invoker.go @@ -87,7 +87,6 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error { } func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { - var selectedInvoker protocol.Invoker url := invokers[0].GetUrl() sticky := url.GetParamBool(constant.STICKY_KEY, false) diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go index 1be21067a6..ee7d48f349 100644 --- a/cluster/cluster_impl/failover_cluster_test.go +++ b/cluster/cluster_impl/failover_cluster_test.go @@ -107,8 +107,8 @@ func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocatio invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) - invokers = append(invokers, NewMockInvoker(url, successCount)) + newUrl, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) + invokers = append(invokers, NewMockInvoker(newUrl, successCount)) } staticDir := directory.NewStaticDirectory(invokers) diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go index 75d9ef2656..0f941fdccf 100644 --- a/cluster/directory/base_directory.go +++ b/cluster/directory/base_directory.go @@ -92,7 +92,7 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { factory := extension.GetRouterFactory(url.Protocol) r, err := factory.NewRouter(url) if err != nil { - logger.Errorf("Create router fail. router key: %s, error: %v", routerKey, url.Service(), err) + logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err) return } routers = append(routers, r) diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go index d5993959f1..6dc55b3940 100644 --- a/cluster/directory/base_directory_test.go +++ b/cluster/directory/base_directory_test.go @@ -19,7 +19,6 @@ package directory import ( "encoding/base64" - "fmt" "testing" ) @@ -35,7 +34,7 @@ import ( ) func TestNewBaseDirectory(t *testing.T) { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")) + url, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") directory := NewBaseDirectory(&url) assert.NotNil(t, directory) @@ -46,7 +45,7 @@ func TestNewBaseDirectory(t *testing.T) { } func TestBuildRouterChain(t *testing.T) { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")) + url, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") directory := NewBaseDirectory(&url) assert.NotNil(t, directory) diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go index 9f600fedc4..87f5135649 100644 --- a/cluster/directory/static_directory.go +++ b/cluster/directory/static_directory.go @@ -61,7 +61,7 @@ func (dir *staticDirectory) IsAvailable() bool { // List List invokers func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invoker { l := len(dir.invokers) - invokers := make([]protocol.Invoker, l, l) + invokers := make([]protocol.Invoker, l) copy(invokers, dir.invokers) routerChain := dir.RouterChain() diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go index 957c110663..492434431f 100644 --- a/cluster/loadbalance/consistent_hash.go +++ b/cluster/loadbalance/consistent_hash.go @@ -27,7 +27,9 @@ import ( "strconv" "strings" ) - +import ( + gxsort "github.com/dubbogo/gost/sort" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/common/constant" @@ -40,7 +42,7 @@ const ( ConsistentHash = "consistenthash" // HashNodes ... HashNodes = "hash.nodes" - // HashArguments ... + // HashArguments key of hash arguments in url HashArguments = "hash.arguments" ) @@ -53,16 +55,16 @@ func init() { extension.SetLoadbalance(ConsistentHash, NewConsistentHashLoadBalance) } -// ConsistentHashLoadBalance ... +// ConsistentHashLoadBalance implementation of load balancing: using consistent hashing type ConsistentHashLoadBalance struct { } -// NewConsistentHashLoadBalance ... +// NewConsistentHashLoadBalance creates NewConsistentHashLoadBalance func NewConsistentHashLoadBalance() cluster.LoadBalance { return &ConsistentHashLoadBalance{} } -// Select ... +// Select gets invoker based on load balancing strategy func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker { methodName := invocation.MethodName() key := invokers[0].GetUrl().ServiceKey() + "." + methodName @@ -85,27 +87,12 @@ func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocat return selector.Select(invocation) } -// Uint32Slice ... -type Uint32Slice []uint32 - -func (s Uint32Slice) Len() int { - return len(s) -} - -func (s Uint32Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -func (s Uint32Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// ConsistentHashSelector ... +// ConsistentHashSelector implementation of Selector:get invoker based on load balancing strategy type ConsistentHashSelector struct { hashCode uint32 replicaNum int virtualInvokers map[uint32]protocol.Invoker - keys Uint32Slice + keys gxsort.Uint32Slice argumentIndex []int } @@ -141,7 +128,7 @@ func newConsistentHashSelector(invokers []protocol.Invoker, methodName string, return selector } -// Select ... +// Select gets invoker based on load balancing strategy func (c *ConsistentHashSelector) Select(invocation protocol.Invocation) protocol.Invoker { key := c.toKey(invocation.Arguments()) digest := md5.Sum([]byte(key)) diff --git a/common/config/environment.go b/common/config/environment.go index 071af31152..446c46aa1e 100644 --- a/common/config/environment.go +++ b/common/config/environment.go @@ -46,7 +46,7 @@ var ( once sync.Once ) -// GetEnvInstance ... +// GetEnvInstance gets env instance by singleton func GetEnvInstance() *Environment { once.Do(func() { instance = &Environment{configCenterFirst: true} @@ -54,7 +54,7 @@ func GetEnvInstance() *Environment { return instance } -// NewEnvInstance ... +// NewEnvInstance creates Environment instance func NewEnvInstance() { instance = &Environment{configCenterFirst: true} } @@ -67,21 +67,22 @@ func NewEnvInstance() { // return env.configCenterFirst //} -// UpdateExternalConfigMap ... +// UpdateExternalConfigMap updates env externalConfigMap field func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.externalConfigMap.Store(k, v) } } -// UpdateAppExternalConfigMap ... +// UpdateAppExternalConfigMap updates env appExternalConfigMap field func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.appExternalConfigMap.Store(k, v) } } -// Configuration ... +// Configuration puts externalConfigMap and appExternalConfigMap into list +// List represents a doubly linked list. func (env *Environment) Configuration() *list.List { cfgList := list.New() // The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration @@ -90,17 +91,17 @@ func (env *Environment) Configuration() *list.List { return cfgList } -// SetDynamicConfiguration ... +// SetDynamicConfiguration sets value for dynamicConfiguration func (env *Environment) SetDynamicConfiguration(dc config_center.DynamicConfiguration) { env.dynamicConfiguration = dc } -// GetDynamicConfiguration ... +// GetDynamicConfiguration gets dynamicConfiguration func (env *Environment) GetDynamicConfiguration() config_center.DynamicConfiguration { return env.dynamicConfiguration } -// InmemoryConfiguration ... +// InmemoryConfiguration stores config in memory type InmemoryConfiguration struct { store *sync.Map } @@ -109,7 +110,7 @@ func newInmemoryConfiguration(p *sync.Map) *InmemoryConfiguration { return &InmemoryConfiguration{store: p} } -// GetProperty ... +// GetProperty gets value from InmemoryConfiguration instance by @key func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { if conf.store == nil { return false, "" @@ -123,7 +124,7 @@ func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { return false, "" } -// GetSubProperty ... +// GetSubProperty gets sub property from InmemoryConfiguration instance by @subkey func (conf *InmemoryConfiguration) GetSubProperty(subKey string) map[string]struct{} { if conf.store == nil { return nil diff --git a/common/constant/key.go b/common/constant/key.go index 8c84a2a888..9af387193e 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -131,6 +131,7 @@ const ( ProviderConfigPrefix = "dubbo.provider." ConsumerConfigPrefix = "dubbo.consumer." ShutdownConfigPrefix = "dubbo.shutdown." + MetadataReportPrefix = "dubbo.metadata-report." RouterConfigPrefix = "dubbo.router." ) @@ -179,6 +180,9 @@ const ( // ForceUseTag is the tag in attachment ForceUseTag = "dubbo.force.tag" Tagkey = "dubbo.tag" + + // Attachment key in context in invoker + AttachmentKey = "attachment" ) const ( @@ -209,9 +213,23 @@ const ( // consumer CONSUMER = "consumer" // key of access key id - ACCESS_KEY_ID_KEY = "accessKeyId" + ACCESS_KEY_ID_KEY = ".accessKeyId" // key of secret access key - SECRET_ACCESS_KEY_KEY = "secretAccessKey" + SECRET_ACCESS_KEY_KEY = ".secretAccessKey" +) + +// metadata report + +const ( + METACONFIG_REMOTE = "remote" + METACONFIG_LOCAL = "local" + KEY_SEPARATOR = ":" + DEFAULT_PATH_TAG = "metadata" + KEY_REVISON_PREFIX = "revision" + PATH_SEPARATOR = "/" + + // metadata service + METADATA_SERVICE_NAME = "org.apache.dubbo.metadata.MetadataService" ) // HealthCheck Router @@ -235,3 +253,9 @@ const ( // The default time window of circuit-tripped in millisecond if not specfied MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS = 30000 ) + +// service discovery + +const ( + NACOS_GROUP = "nacos.group" +) diff --git a/common/extension/auth.go b/common/extension/auth.go index d7900045d3..7caae00e84 100644 --- a/common/extension/auth.go +++ b/common/extension/auth.go @@ -26,12 +26,12 @@ var ( accesskeyStorages = make(map[string]func() filter.AccessKeyStorage) ) -// SetAuthenticator put the fcn into map with name +// SetAuthenticator puts the @fcn into map with name func SetAuthenticator(name string, fcn func() filter.Authenticator) { authenticators[name] = fcn } -// GetAuthenticator find the Authenticator with name +// GetAuthenticator finds the Authenticator with @name // if not found, it will panic func GetAuthenticator(name string) filter.Authenticator { if authenticators[name] == nil { @@ -40,12 +40,12 @@ func GetAuthenticator(name string) filter.Authenticator { return authenticators[name]() } -// SetAccesskeyStorages will set the fcn into map with this name +// SetAccesskeyStorages will set the @fcn into map with this name func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) { accesskeyStorages[name] = fcn } -// GetAccesskeyStorages find the storage with the name. +// GetAccesskeyStorages finds the storage with the @name. // If not found, it will panic. func GetAccesskeyStorages(name string) filter.AccessKeyStorage { if accesskeyStorages[name] == nil { diff --git a/common/extension/cluster.go b/common/extension/cluster.go index b2d81f6b1e..8be27a1ca3 100644 --- a/common/extension/cluster.go +++ b/common/extension/cluster.go @@ -25,12 +25,13 @@ var ( clusters = make(map[string]func() cluster.Cluster) ) -// SetCluster ... +// SetCluster sets the cluster fault-tolerant mode with @name +// For example: available/failfast/broadcast/failfast/failsafe/... func SetCluster(name string, fcn func() cluster.Cluster) { clusters[name] = fcn } -// GetCluster ... +// GetCluster finds the cluster fault-tolerant mode with @name func GetCluster(name string) cluster.Cluster { if clusters[name] == nil { panic("cluster for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center.go b/common/extension/config_center.go index 03d27db46c..3cbced8d3b 100644 --- a/common/extension/config_center.go +++ b/common/extension/config_center.go @@ -26,12 +26,12 @@ var ( configCenters = make(map[string]func(config *common.URL) (config_center.DynamicConfiguration, error)) ) -// SetConfigCenter ... +// SetConfigCenter sets the DynamicConfiguration with @name func SetConfigCenter(name string, v func(config *common.URL) (config_center.DynamicConfiguration, error)) { configCenters[name] = v } -// GetConfigCenter ... +// GetConfigCenter finds the DynamicConfiguration with @name func GetConfigCenter(name string, config *common.URL) (config_center.DynamicConfiguration, error) { if configCenters[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center_factory.go b/common/extension/config_center_factory.go index 85913fdce1..dff8975229 100644 --- a/common/extension/config_center_factory.go +++ b/common/extension/config_center_factory.go @@ -25,12 +25,12 @@ var ( configCenterFactories = make(map[string]func() config_center.DynamicConfigurationFactory) ) -// SetConfigCenterFactory ... +// SetConfigCenterFactory sets the DynamicConfigurationFactory with @name func SetConfigCenterFactory(name string, v func() config_center.DynamicConfigurationFactory) { configCenterFactories[name] = v } -// GetConfigCenterFactory ... +// GetConfigCenterFactory finds the DynamicConfigurationFactory with @name func GetConfigCenterFactory(name string) config_center.DynamicConfigurationFactory { if configCenterFactories[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_reader.go b/common/extension/config_reader.go index aced5b0281..5e13d8629f 100644 --- a/common/extension/config_reader.go +++ b/common/extension/config_reader.go @@ -26,12 +26,12 @@ var ( defaults = make(map[string]string) ) -// SetConfigReaders set a creator of config reader. +// SetConfigReaders sets a creator of config reader with @name func SetConfigReaders(name string, v func() interfaces.ConfigReader) { configReaders[name] = v } -// GetConfigReaders get a config reader by name. +// GetConfigReaders gets a config reader with @name func GetConfigReaders(name string) interfaces.ConfigReader { if configReaders[name] == nil { panic("config reader for " + name + " is not existing, make sure you have imported the package.") @@ -39,12 +39,12 @@ func GetConfigReaders(name string) interfaces.ConfigReader { return configReaders[name]() } -// SetDefaultConfigReader set {name} to default config reader for {module} +// SetDefaultConfigReader sets @name for @module in default config reader func SetDefaultConfigReader(module, name string) { defaults[module] = name } -// GetDefaultConfigReader +// GetDefaultConfigReader gets default config reader func GetDefaultConfigReader() map[string]string { return defaults } diff --git a/common/extension/configurator.go b/common/extension/configurator.go index de98f8a260..dc2bea73af 100644 --- a/common/extension/configurator.go +++ b/common/extension/configurator.go @@ -23,7 +23,7 @@ import ( ) const ( - // DefaultKey ... + // DefaultKey for default Configurator DefaultKey = "default" ) @@ -33,12 +33,12 @@ var ( configurator = make(map[string]getConfiguratorFunc) ) -// SetConfigurator ... +// SetConfigurator sets the getConfiguratorFunc with @name func SetConfigurator(name string, v getConfiguratorFunc) { configurator[name] = v } -// GetConfigurator ... +// GetConfigurator finds the Configurator with @name func GetConfigurator(name string, url *common.URL) config_center.Configurator { if configurator[name] == nil { panic("configurator for " + name + " is not existing, make sure you have import the package.") @@ -47,12 +47,12 @@ func GetConfigurator(name string, url *common.URL) config_center.Configurator { } -// SetDefaultConfigurator ... +// SetDefaultConfigurator sets the default Configurator func SetDefaultConfigurator(v getConfiguratorFunc) { configurator[DefaultKey] = v } -// GetDefaultConfigurator ... +// GetDefaultConfigurator gets default configurator func GetDefaultConfigurator(url *common.URL) config_center.Configurator { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") @@ -61,7 +61,7 @@ func GetDefaultConfigurator(url *common.URL) config_center.Configurator { } -// GetDefaultConfiguratorFunc ... +// GetDefaultConfiguratorFunc gets default configurator function func GetDefaultConfiguratorFunc() getConfiguratorFunc { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") diff --git a/common/extension/filter.go b/common/extension/filter.go index deea2d908b..96059c4363 100644 --- a/common/extension/filter.go +++ b/common/extension/filter.go @@ -26,12 +26,13 @@ var ( rejectedExecutionHandler = make(map[string]func() filter.RejectedExecutionHandler) ) -// SetFilter ... +// SetFilter sets the filter extension with @name +// For example: hystrix/metrics/token/tracing/limit/... func SetFilter(name string, v func() filter.Filter) { filters[name] = v } -// GetFilter ... +// GetFilter finds the filter extension with @name func GetFilter(name string) filter.Filter { if filters[name] == nil { panic("filter for " + name + " is not existing, make sure you have imported the package.") @@ -39,12 +40,12 @@ func GetFilter(name string) filter.Filter { return filters[name]() } -// SetRejectedExecutionHandler ... +// SetRejectedExecutionHandler sets the RejectedExecutionHandler with @name func SetRejectedExecutionHandler(name string, creator func() filter.RejectedExecutionHandler) { rejectedExecutionHandler[name] = creator } -// GetRejectedExecutionHandler ... +// GetRejectedExecutionHandler finds the RejectedExecutionHandler with @name func GetRejectedExecutionHandler(name string) filter.RejectedExecutionHandler { creator, ok := rejectedExecutionHandler[name] if !ok { diff --git a/common/extension/graceful_shutdown.go b/common/extension/graceful_shutdown.go index 3abd75c0aa..cb55419aab 100644 --- a/common/extension/graceful_shutdown.go +++ b/common/extension/graceful_shutdown.go @@ -49,7 +49,7 @@ func AddCustomShutdownCallback(callback func()) { customShutdownCallbacks.PushBack(callback) } -// GetAllCustomShutdownCallbacks ... +// GetAllCustomShutdownCallbacks gets all custom shutdown callbacks func GetAllCustomShutdownCallbacks() *list.List { return customShutdownCallbacks } diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go index 365c5d0910..548d4dc761 100644 --- a/common/extension/health_checker.go +++ b/common/extension/health_checker.go @@ -26,12 +26,12 @@ var ( healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker) ) -// SethealthChecker set the HealthChecker with name +// SethealthChecker sets the HealthChecker with @name func SethealthChecker(name string, fcn func(url *common.URL) router.HealthChecker) { healthCheckers[name] = fcn } -// GetHealthChecker get the HealthChecker with name +// GetHealthChecker gets the HealthChecker with @name func GetHealthChecker(name string, url *common.URL) router.HealthChecker { if healthCheckers[name] == nil { panic("healthCheckers for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/loadbalance.go b/common/extension/loadbalance.go index 0d557a4640..aa19141014 100644 --- a/common/extension/loadbalance.go +++ b/common/extension/loadbalance.go @@ -25,12 +25,13 @@ var ( loadbalances = make(map[string]func() cluster.LoadBalance) ) -// SetLoadbalance ... +// SetLoadbalance sets the loadbalance extension with @name +// For example: random/round_robin/consistent_hash/least_active/... func SetLoadbalance(name string, fcn func() cluster.LoadBalance) { loadbalances[name] = fcn } -// GetLoadbalance ... +// GetLoadbalance finds the loadbalance extension with @name func GetLoadbalance(name string) cluster.LoadBalance { if loadbalances[name] == nil { panic("loadbalance for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/metadata_report_factory.go b/common/extension/metadata_report_factory.go new file mode 100644 index 0000000000..c55f8617fa --- /dev/null +++ b/common/extension/metadata_report_factory.go @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package extension + +import ( + "github.com/apache/dubbo-go/metadata" +) + +var ( + metaDataReportFactories = make(map[string]func() metadata.MetadataReportFactory, 8) +) + +// SetMetadataReportFactory sets the MetadataReportFactory with @name +func SetMetadataReportFactory(name string, v func() metadata.MetadataReportFactory) { + metaDataReportFactories[name] = v +} + +// GetMetadataReportFactory finds the MetadataReportFactory with @name +func GetMetadataReportFactory(name string) metadata.MetadataReportFactory { + if metaDataReportFactories[name] == nil { + panic("metadata report for " + name + " is not existing, make sure you have import the package.") + } + return metaDataReportFactories[name]() +} diff --git a/common/extension/metrics.go b/common/extension/metrics.go index 42fca7a2db..60cf6bac23 100644 --- a/common/extension/metrics.go +++ b/common/extension/metrics.go @@ -27,12 +27,12 @@ var ( metricReporterMap = make(map[string]func() metrics.Reporter, 4) ) -// SetMetricReporter set a reporter with the name +// SetMetricReporter sets a reporter with the @name func SetMetricReporter(name string, reporterFunc func() metrics.Reporter) { metricReporterMap[name] = reporterFunc } -// GetMetricReporter find the reporter with name. +// GetMetricReporter finds the reporter with @name. // if not found, it will panic. // we should know that this method usually is called when system starts, so we should panic func GetMetricReporter(name string) metrics.Reporter { diff --git a/common/extension/protocol.go b/common/extension/protocol.go index 009687a17a..c89dd08fae 100644 --- a/common/extension/protocol.go +++ b/common/extension/protocol.go @@ -25,12 +25,12 @@ var ( protocols = make(map[string]func() protocol.Protocol) ) -// SetProtocol ... +// SetProtocol sets the protocol extension with @name func SetProtocol(name string, v func() protocol.Protocol) { protocols[name] = v } -// GetProtocol ... +// GetProtocol finds the protocol extension with @name func GetProtocol(name string) protocol.Protocol { if protocols[name] == nil { panic("protocol for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/proxy_factory.go b/common/extension/proxy_factory.go index 19826bb056..1e326d884b 100644 --- a/common/extension/proxy_factory.go +++ b/common/extension/proxy_factory.go @@ -25,12 +25,12 @@ var ( proxyFactories = make(map[string]func(...proxy.Option) proxy.ProxyFactory) ) -// SetProxyFactory ... +// SetProxyFactory sets the ProxyFactory extension with @name func SetProxyFactory(name string, f func(...proxy.Option) proxy.ProxyFactory) { proxyFactories[name] = f } -// GetProxyFactory ... +// GetProxyFactory finds the ProxyFactory extension with @name func GetProxyFactory(name string) proxy.ProxyFactory { if name == "" { name = "default" diff --git a/common/extension/registry.go b/common/extension/registry.go index 6ba746dc47..291a7a7fc2 100644 --- a/common/extension/registry.go +++ b/common/extension/registry.go @@ -26,12 +26,12 @@ var ( registrys = make(map[string]func(config *common.URL) (registry.Registry, error)) ) -// SetRegistry ... +// SetRegistry sets the registry extension with @name func SetRegistry(name string, v func(config *common.URL) (registry.Registry, error)) { registrys[name] = v } -// GetRegistry ... +// GetRegistry finds the registry extension with @name func GetRegistry(name string, config *common.URL) (registry.Registry, error) { if registrys[name] == nil { panic("registry for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/registry_directory.go b/common/extension/registry_directory.go new file mode 100644 index 0000000000..330fc46400 --- /dev/null +++ b/common/extension/registry_directory.go @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package extension + +import ( + "github.com/apache/dubbo-go/cluster" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/registry" +) + +type registryDirectory func(url *common.URL, registry registry.Registry) (cluster.Directory, error) + +var defaultRegistry registryDirectory + +// SetDefaultRegistryDirectory sets the default registryDirectory +func SetDefaultRegistryDirectory(v registryDirectory) { + defaultRegistry = v +} + +// GetDefaultRegistryDirectory finds the registryDirectory with url and registry +func GetDefaultRegistryDirectory(config *common.URL, registry registry.Registry) (cluster.Directory, error) { + if defaultRegistry == nil { + panic("registry directory is not existing, make sure you have import the package.") + } + return defaultRegistry(config, registry) +} diff --git a/common/extension/rest_client.go b/common/extension/rest_client.go index 514d1fdfd2..9caf8c67df 100644 --- a/common/extension/rest_client.go +++ b/common/extension/rest_client.go @@ -25,10 +25,12 @@ var ( restClients = make(map[string]func(restOptions *client.RestOptions) client.RestClient, 8) ) +// SetRestClient sets the RestClient with @name func SetRestClient(name string, fun func(restOptions *client.RestOptions) client.RestClient) { restClients[name] = fun } +// GetNewRestClient finds the RestClient with @name func GetNewRestClient(name string, restOptions *client.RestOptions) client.RestClient { if restClients[name] == nil { panic("restClient for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/rest_server.go b/common/extension/rest_server.go index fa8d435a5c..37a231a57c 100644 --- a/common/extension/rest_server.go +++ b/common/extension/rest_server.go @@ -25,10 +25,12 @@ var ( restServers = make(map[string]func() server.RestServer, 8) ) +// SetRestServer sets the RestServer with @name func SetRestServer(name string, fun func() server.RestServer) { restServers[name] = fun } +// GetNewRestServer finds the RestServer with @name func GetNewRestServer(name string) server.RestServer { if restServers[name] == nil { panic("restServer for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go index 1339228618..21a49d2681 100644 --- a/common/extension/router_factory.go +++ b/common/extension/router_factory.go @@ -31,12 +31,12 @@ var ( fileRouterFactories = make(map[string]router.FileRouterFactory) ) -// SetRouterFactory Set create router factory function by name +// SetRouterFactory sets create router factory function with @name func SetRouterFactory(name string, fun func() router.RouterFactory) { routers[name] = fun } -// GetRouterFactory Get create router factory function by name +// GetRouterFactory gets create router factory function by @name func GetRouterFactory(name string) router.RouterFactory { if routers[name] == nil { panic("router_factory for " + name + " is not existing, make sure you have import the package.") @@ -44,12 +44,12 @@ func GetRouterFactory(name string) router.RouterFactory { return routers[name]() } -// GetRouterFactories Get all create router factory function +// GetRouterFactories gets all create router factory function func GetRouterFactories() map[string]func() router.RouterFactory { return routers } -// GetFileRouterFactories Get all create file router factory instance +// GetFileRouterFactories gets all create file router factory instance func GetFileRouterFactories() map[string]router.FileRouterFactory { l := len(routers) if l == 0 { diff --git a/common/extension/service_discovery.go b/common/extension/service_discovery.go new file mode 100644 index 0000000000..d70b032306 --- /dev/null +++ b/common/extension/service_discovery.go @@ -0,0 +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 extension + +import ( + perrors "github.com/pkg/errors" +) +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/registry" +) + +var ( + discoveryCreatorMap = make(map[string]func(url *common.URL) (registry.ServiceDiscovery, error), 4) +) + +// SetServiceDiscovery will store the @creator and @name +func SetServiceDiscovery(name string, creator func(url *common.URL) (registry.ServiceDiscovery, error)) { + discoveryCreatorMap[name] = creator +} + +// GetServiceDiscovery will return the registry.ServiceDiscovery +// if not found, or initialize instance failed, it will return error. +func GetServiceDiscovery(name string, url *common.URL) (registry.ServiceDiscovery, error) { + creator, ok := discoveryCreatorMap[name] + if !ok { + return nil, perrors.New("Could not find the service discovery with name: " + name) + } + return creator(url) +} diff --git a/common/extension/tps_limit.go b/common/extension/tps_limit.go index c72c2b030f..d25821deee 100644 --- a/common/extension/tps_limit.go +++ b/common/extension/tps_limit.go @@ -26,12 +26,12 @@ var ( tpsLimiter = make(map[string]func() filter.TpsLimiter) ) -// SetTpsLimiter ... +// SetTpsLimiter sets the TpsLimiter with @name func SetTpsLimiter(name string, creator func() filter.TpsLimiter) { tpsLimiter[name] = creator } -// GetTpsLimiter ... +// GetTpsLimiter finds the TpsLimiter with @name func GetTpsLimiter(name string) filter.TpsLimiter { creator, ok := tpsLimiter[name] if !ok { @@ -41,12 +41,12 @@ func GetTpsLimiter(name string) filter.TpsLimiter { return creator() } -// SetTpsLimitStrategy ... +// SetTpsLimitStrategy sets the TpsLimitStrategyCreator with @name func SetTpsLimitStrategy(name string, creator filter.TpsLimitStrategyCreator) { tpsLimitStrategy[name] = creator } -// GetTpsLimitStrategyCreator ... +// GetTpsLimitStrategyCreator finds the TpsLimitStrategyCreator with @name func GetTpsLimitStrategyCreator(name string) filter.TpsLimitStrategyCreator { creator, ok := tpsLimitStrategy[name] if !ok { diff --git a/common/logger/logger.go b/common/logger/logger.go index 016afe6980..9bc6a46100 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -40,13 +40,13 @@ var ( logger Logger ) -// DubboLogger ... +// nolint type DubboLogger struct { Logger dynamicLevel zap.AtomicLevel } -// Logger ... +// Logger is the interface for Logger types type Logger interface { Info(args ...interface{}) Warn(args ...interface{}) @@ -67,7 +67,7 @@ func init() { } } -// InitLog ... +// InitLog use for init logger by call InitLogger func InitLog(logConfFile string) error { if logConfFile == "" { InitLogger(nil) @@ -96,7 +96,7 @@ func InitLog(logConfFile string) error { return nil } -// InitLogger ... +// InitLogger use for init logger by @conf func InitLogger(conf *zap.Config) { var zapLoggerConfig zap.Config if conf == nil { @@ -125,18 +125,18 @@ func InitLogger(conf *zap.Config) { getty.SetLogger(logger) } -// SetLogger ... +// SetLogger sets logger for dubbo and getty func SetLogger(log Logger) { logger = log getty.SetLogger(logger) } -// GetLogger ... +// GetLogger gets the logger func GetLogger() Logger { return logger } -// SetLoggerLevel ... +// SetLoggerLevel use for set logger level func SetLoggerLevel(level string) bool { if l, ok := logger.(OpsLogger); ok { l.SetLoggerLevel(level) @@ -145,13 +145,13 @@ func SetLoggerLevel(level string) bool { return false } -// OpsLogger ... +// OpsLogger use for the SetLoggerLevel type OpsLogger interface { Logger SetLoggerLevel(level string) } -// SetLoggerLevel ... +// SetLoggerLevel use for set logger level func (dl *DubboLogger) SetLoggerLevel(level string) { l := new(zapcore.Level) l.Set(level) diff --git a/common/logger/logging.go b/common/logger/logging.go index 36d48ee61e..7a31ece203 100644 --- a/common/logger/logging.go +++ b/common/logger/logging.go @@ -17,42 +17,42 @@ package logger -// Info ... +// Info is info level func Info(args ...interface{}) { logger.Info(args...) } -// Warn ... +// Warn is warning level func Warn(args ...interface{}) { logger.Warn(args...) } -// Error ... +// Error is error level func Error(args ...interface{}) { logger.Error(args...) } -// Debug ... +// Debug is debug level func Debug(args ...interface{}) { logger.Debug(args...) } -// Infof ... +// Infof is format info level func Infof(fmt string, args ...interface{}) { logger.Infof(fmt, args...) } -// Warnf ... +// Warnf is format warning level func Warnf(fmt string, args ...interface{}) { logger.Warnf(fmt, args...) } -// Errorf ... +// Errorf is format error level func Errorf(fmt string, args ...interface{}) { logger.Errorf(fmt, args...) } -// Debugf ... +// Debugf is format debug level func Debugf(fmt string, args ...interface{}) { logger.Debugf(fmt, args...) } diff --git a/common/node.go b/common/node.go index 979eee31ef..4febd78536 100644 --- a/common/node.go +++ b/common/node.go @@ -17,7 +17,7 @@ package common -// Node ... +// Node use for process dubbo node type Node interface { GetUrl() URL IsAvailable() bool diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go index 68ba3ff788..abcf87cd9d 100644 --- a/common/proxy/proxy.go +++ b/common/proxy/proxy.go @@ -25,12 +25,13 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) -// Proxy struct +// nolint type Proxy struct { rpc common.RPCService invoke protocol.Invoker @@ -44,7 +45,7 @@ var ( typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type() ) -// NewProxy ... +// NewProxy create service proxy. func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy { return &Proxy{ invoke: invoke, @@ -59,7 +60,6 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str // type XxxProvider struct { // Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error // } - func (p *Proxy) Implement(v common.RPCService) { // check parameters, incoming interface must be a elem's pointer. @@ -141,7 +141,7 @@ func (p *Proxy) Implement(v common.RPCService) { } // add user setAttachment - atm := invCtx.Value("attachment") + atm := invCtx.Value(constant.AttachmentKey) if m, ok := atm.(map[string]string); ok { for k, value := range m { inv.SetAttachments(k, value) @@ -149,6 +149,9 @@ func (p *Proxy) Implement(v common.RPCService) { } result := p.invoke.Invoke(invCtx, inv) + if len(result.Attachments()) > 0 { + invCtx = context.WithValue(invCtx, constant.AttachmentKey, result.Attachments()) + } err = result.Error() logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err) @@ -202,12 +205,12 @@ func (p *Proxy) Implement(v common.RPCService) { } -// Get ... +// Get gets rpc service instance. func (p *Proxy) Get() common.RPCService { return p.rpc } -// GetCallback ... +// GetCallback gets callback. func (p *Proxy) GetCallback() interface{} { return p.callBack } diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go index 7b249a3e97..117428cb25 100644 --- a/common/proxy/proxy_factory.go +++ b/common/proxy/proxy_factory.go @@ -22,12 +22,12 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ProxyFactory ... +// ProxyFactory interface. type ProxyFactory interface { GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy GetInvoker(url common.URL) protocol.Invoker } -// Option ... +// Option will define a function of handling ProxyFactory type Option func(ProxyFactory) diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go index 114cfee236..1bb1e29c5c 100644 --- a/common/proxy/proxy_factory/default.go +++ b/common/proxy/proxy_factory/default.go @@ -40,7 +40,7 @@ func init() { extension.SetProxyFactory("default", NewDefaultProxyFactory) } -// DefaultProxyFactory ... +// DefaultProxyFactory is the default proxy factory type DefaultProxyFactory struct { //delegate ProxyFactory } @@ -53,17 +53,17 @@ type DefaultProxyFactory struct { // } //} -// NewDefaultProxyFactory ... +// NewDefaultProxyFactory returns a proxy factory instance func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory { return &DefaultProxyFactory{} } -// GetProxy ... +// GetProxy gets a proxy func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy { return factory.GetAsyncProxy(invoker, nil, url) } -// GetAsyncProxy ... +// GetAsyncProxy gets a async proxy func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy { //create proxy attachments := map[string]string{} @@ -71,19 +71,19 @@ func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, call return proxy.NewProxy(invoker, callBack, attachments) } -// GetInvoker ... +// GetInvoker gets a invoker func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker { return &ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), } } -// ProxyInvoker ... +// ProxyInvoker is a invoker struct type ProxyInvoker struct { protocol.BaseInvoker } -// Invoke ... +// Invoke is used to call service method by invocation func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { result := &protocol.RPCResult{} result.SetAttachments(invocation.Attachments()) @@ -113,6 +113,7 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati in := []reflect.Value{svc.Rcvr()} if method.CtxType() != nil { + ctx = context.WithValue(ctx, constant.AttachmentKey, invocation.Attachments()) in = append(in, method.SuiteContext(ctx)) } diff --git a/common/rpc_service.go b/common/rpc_service.go index b235c32abc..211e4eae9c 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -35,23 +35,23 @@ import ( ) // RPCService -//rpc service interface +// rpc service interface type RPCService interface { // Reference: // rpc service id or reference id Reference() string } -//AsyncCallbackService callback interface for async +// AsyncCallbackService callback interface for async type AsyncCallbackService interface { // Callback: callback CallBack(response CallbackResponse) } -//CallbackResponse for different protocol +// CallbackResponse for different protocol type CallbackResponse interface{} -//AsyncCallback async callback method +// AsyncCallback async callback method type AsyncCallback func(response CallbackResponse) // for lowercase func @@ -59,7 +59,6 @@ type AsyncCallback func(response CallbackResponse) // return map[string][string]{} // } const ( - // METHOD_MAPPER ... METHOD_MAPPER = "MethodMapper" ) @@ -68,10 +67,11 @@ var ( // because Typeof takes an empty interface value. This is annoying. typeOfError = reflect.TypeOf((*error)(nil)).Elem() - // ServiceMap ... + // ServiceMap store description of service. // todo: lowerecas? ServiceMap = &serviceMap{ - serviceMap: make(map[string]map[string]*Service), + serviceMap: make(map[string]map[string]*Service), + interfaceMap: make(map[string][]*Service), } ) @@ -79,7 +79,7 @@ var ( // info of method ////////////////////////// -// MethodType ... +// MethodType is description of service method. type MethodType struct { method reflect.Method ctxType reflect.Type // request context @@ -87,27 +87,27 @@ type MethodType struct { replyType reflect.Type // return value, otherwise it is nil } -// Method ... +// Method gets @m.method. func (m *MethodType) Method() reflect.Method { return m.method } -// CtxType ... +// CtxType gets @m.ctxType. func (m *MethodType) CtxType() reflect.Type { return m.ctxType } -// ArgsType ... +// ArgsType gets @m.argsType. func (m *MethodType) ArgsType() []reflect.Type { return m.argsType } -// ReplyType ... +// ReplyType gets @m.replyType. func (m *MethodType) ReplyType() reflect.Type { return m.replyType } -// SuiteContext ... +// SuiteContext tranfers @ctx to reflect.Value type or get it from @m.ctxType. func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { if contextv := reflect.ValueOf(ctx); contextv.IsValid() { return contextv @@ -119,7 +119,7 @@ func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { // info of service interface ////////////////////////// -// Service ... +// Service is description of service type Service struct { name string rcvr reflect.Value @@ -127,17 +127,17 @@ type Service struct { methods map[string]*MethodType } -// Method ... +// Method gets @s.methods. func (s *Service) Method() map[string]*MethodType { return s.methods } -// RcvrType ... +// RcvrType gets @s.rcvrType. func (s *Service) RcvrType() reflect.Type { return s.rcvrType } -// Rcvr ... +// Rcvr gets @s.rcvr. func (s *Service) Rcvr() reflect.Value { return s.rcvr } @@ -147,10 +147,12 @@ func (s *Service) Rcvr() reflect.Value { ////////////////////////// type serviceMap struct { - mutex sync.RWMutex // protects the serviceMap - serviceMap map[string]map[string]*Service // protocol -> service name -> service + mutex sync.RWMutex // protects the serviceMap + serviceMap map[string]map[string]*Service // protocol -> service name -> service + interfaceMap map[string][]*Service // interface -> service } +// GetService gets a service defination by protocol and name func (sm *serviceMap) GetService(protocol, name string) *Service { sm.mutex.RLock() defer sm.mutex.RUnlock() @@ -163,10 +165,24 @@ func (sm *serviceMap) GetService(protocol, name string) *Service { return nil } -func (sm *serviceMap) Register(protocol string, rcvr RPCService) (string, error) { +// GetInterface gets an interface defination by interface name +func (sm *serviceMap) GetInterface(interfaceName string) []*Service { + sm.mutex.RLock() + defer sm.mutex.RUnlock() + if s, ok := sm.interfaceMap[interfaceName]; ok { + return s + } + return nil +} + +// Register registers a service by @interfaceName and @protocol +func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) (string, error) { if sm.serviceMap[protocol] == nil { sm.serviceMap[protocol] = make(map[string]*Service) } + if sm.interfaceMap[interfaceName] == nil { + sm.interfaceMap[interfaceName] = make([]*Service, 0, 16) + } s := new(Service) s.rcvrType = reflect.TypeOf(rcvr) @@ -201,32 +217,65 @@ func (sm *serviceMap) Register(protocol string, rcvr RPCService) (string, error) } sm.mutex.Lock() sm.serviceMap[protocol][s.name] = s + sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], s) sm.mutex.Unlock() return strings.TrimSuffix(methods, ","), nil } -func (sm *serviceMap) UnRegister(protocol, serviceId string) error { +// UnRegister cancels a service by @interfaceName, @protocol and @serviceId +func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) error { if protocol == "" || serviceId == "" { return perrors.New("protocol or serviceName is nil") } - sm.mutex.RLock() - svcs, ok := sm.serviceMap[protocol] - if !ok { - sm.mutex.RUnlock() - return perrors.New("no services for " + protocol) + + var ( + err error + index = -1 + svcs map[string]*Service + svrs []*Service + ok bool + ) + + f := func() error { + sm.mutex.RLock() + defer sm.mutex.RUnlock() + svcs, ok = sm.serviceMap[protocol] + if !ok { + return perrors.New("no services for " + protocol) + } + s, ok := svcs[serviceId] + if !ok { + return perrors.New("no service for " + serviceId) + } + svrs, ok = sm.interfaceMap[interfaceName] + if !ok { + return perrors.New("no service for " + interfaceName) + } + for i, svr := range svrs { + if svr == s { + index = i + } + } + return nil } - _, ok = svcs[serviceId] - if !ok { - sm.mutex.RUnlock() - return perrors.New("no service for " + serviceId) + + if err = f(); err != nil { + return err } - sm.mutex.RUnlock() sm.mutex.Lock() defer sm.mutex.Unlock() + sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs)) + for i, _ := range svrs { + if i != index { + sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i]) + } + } delete(svcs, serviceId) - delete(sm.serviceMap, protocol) + if len(sm.serviceMap) == 0 { + delete(sm.serviceMap, protocol) + } return nil } diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go index 8c9b9d15cd..2311205d0e 100644 --- a/common/rpc_service_test.go +++ b/common/rpc_service_test.go @@ -77,46 +77,48 @@ func TestServiceMap_Register(t *testing.T) { // lowercase s0 := &testService{} // methods, err := ServiceMap.Register("testporotocol", s0) - _, err := ServiceMap.Register("testporotocol", s0) + _, err := ServiceMap.Register("testService", "testporotocol", s0) assert.EqualError(t, err, "type testService is not exported") // succ s := &TestService{} - methods, err := ServiceMap.Register("testporotocol", s) + methods, err := ServiceMap.Register("testService", "testporotocol", s) assert.NoError(t, err) assert.Equal(t, "MethodOne,MethodThree,methodTwo", methods) // repeat - _, err = ServiceMap.Register("testporotocol", s) + _, err = ServiceMap.Register("testService", "testporotocol", s) assert.EqualError(t, err, "service already defined: com.test.Path") // no method s1 := &TestService1{} - _, err = ServiceMap.Register("testporotocol", s1) + _, err = ServiceMap.Register("testService", "testporotocol", s1) assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type") ServiceMap = &serviceMap{ - serviceMap: make(map[string]map[string]*Service), + serviceMap: make(map[string]map[string]*Service), + interfaceMap: make(map[string][]*Service), } } func TestServiceMap_UnRegister(t *testing.T) { s := &TestService{} - _, err := ServiceMap.Register("testprotocol", s) + _, err := ServiceMap.Register("TestService", "testprotocol", s) assert.NoError(t, err) assert.NotNil(t, ServiceMap.GetService("testprotocol", "com.test.Path")) + assert.Equal(t, 1, len(ServiceMap.GetInterface("TestService"))) - err = ServiceMap.UnRegister("", "com.test.Path") + err = ServiceMap.UnRegister("", "", "com.test.Path") assert.EqualError(t, err, "protocol or serviceName is nil") - err = ServiceMap.UnRegister("protocol", "com.test.Path") + err = ServiceMap.UnRegister("", "protocol", "com.test.Path") assert.EqualError(t, err, "no services for protocol") - err = ServiceMap.UnRegister("testprotocol", "com.test.Path1") + err = ServiceMap.UnRegister("", "testprotocol", "com.test.Path1") assert.EqualError(t, err, "no service for com.test.Path1") // succ - err = ServiceMap.UnRegister("testprotocol", "com.test.Path") + err = ServiceMap.UnRegister("TestService", "testprotocol", "com.test.Path") assert.NoError(t, err) } diff --git a/common/url.go b/common/url.go index ebb648db27..1cfa47ae28 100644 --- a/common/url.go +++ b/common/url.go @@ -18,7 +18,6 @@ package common import ( - "bytes" "encoding/base64" "fmt" "math" @@ -26,7 +25,6 @@ import ( "net/url" "strconv" "strings" - "sync" ) import ( @@ -40,136 +38,140 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -///////////////////////////////// +// /////////////////////////////// // dubbo role type -///////////////////////////////// +// /////////////////////////////// // role constant const ( - // CONSUMER ... + // CONSUMER is consumer role CONSUMER = iota - // CONFIGURATOR ... + // CONFIGURATOR is configurator role CONFIGURATOR - // ROUTER ... + // ROUTER is router role ROUTER - // PROVIDER ... + // PROVIDER is provider role PROVIDER ) var ( - // DubboNodes ... + // DubboNodes Dubbo service node DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"} // DubboRole Dubbo service role DubboRole = [...]string{"consumer", "", "routers", "provider"} ) -// RoleType ... +// nolint type RoleType int func (t RoleType) String() string { return DubboNodes[t] } -// Role ... +// Role returns role by @RoleType func (t RoleType) Role() string { return DubboRole[t] } type baseUrl struct { - Protocol string - Location string // ip+port - Ip string - Port string - //url.Values is not safe map, add to avoid concurrent map read and map write error - paramsLock sync.RWMutex + Protocol string + Location string // ip+port + Ip string + Port string params url.Values PrimitiveURL string } -// URL ... +// URL is not thread-safe. +// we fail to define this struct to be immutable object. +// but, those method which will update the URL, including SetParam, SetParams +// are only allowed to be invoked in creating URL instance +// Please keep in mind that this struct is immutable after it has been created and initialized. type URL struct { baseUrl Path string // like /com.ikurento.dubbo.UserProvider3 Username string Password string Methods []string - //special for registry + // special for registry SubURL *URL } +// Option accepts url +// Option will define a function of handling URL type option func(*URL) -// WithUsername ... +// WithUsername sets username for url func WithUsername(username string) option { return func(url *URL) { url.Username = username } } -// WithPassword ... +// WithPassword sets password for url func WithPassword(pwd string) option { return func(url *URL) { url.Password = pwd } } -// WithMethods ... +// WithMethods sets methods for url func WithMethods(methods []string) option { return func(url *URL) { url.Methods = methods } } -// WithParams ... +// WithParams sets params for url func WithParams(params url.Values) option { return func(url *URL) { url.params = params } } -// WithParamsValue ... +// WithParamsValue sets params field for url func WithParamsValue(key, val string) option { return func(url *URL) { url.SetParam(key, val) } } -// WithProtocol ... +// WithProtocol sets protocol for url func WithProtocol(proto string) option { return func(url *URL) { url.Protocol = proto } } -// WithIp ... +// WithIp sets ip for url func WithIp(ip string) option { return func(url *URL) { url.Ip = ip } } -// WithPort ... +// WithPort sets port for url func WithPort(port string) option { return func(url *URL) { url.Port = port } } -// WithPath ... +// WithPath sets path for url func WithPath(path string) option { return func(url *URL) { url.Path = "/" + strings.TrimPrefix(path, "/") } } -// WithLocation ... +// WithLocation sets location for url func WithLocation(location string) option { return func(url *URL) { url.Location = location } } -// WithToken ... +// WithToken sets token for url func WithToken(token string) option { return func(url *URL) { if len(token) > 0 { @@ -182,7 +184,7 @@ func WithToken(token string) option { } } -// NewURLWithOptions ... +// NewURLWithOptions will create a new url with options func NewURLWithOptions(opts ...option) *URL { url := &URL{} for _, opt := range opts { @@ -195,7 +197,6 @@ func NewURLWithOptions(opts ...option) *URL { // NewURL will create a new url // the urlString should not be empty func NewURL(urlString string, opts ...option) (URL, error) { - var ( err error rawUrlString string @@ -213,7 +214,7 @@ func NewURL(urlString string, opts ...option) (URL, error) { return s, perrors.Errorf("url.QueryUnescape(%s), error{%v}", urlString, err) } - //rawUrlString = "//" + rawUrlString + // rawUrlString = "//" + rawUrlString if strings.Index(rawUrlString, "//") < 0 { t := URL{baseUrl: baseUrl{}} for _, opt := range opts { @@ -249,7 +250,7 @@ func NewURL(urlString string, opts ...option) (URL, error) { return s, nil } -// URLEqual ... +// URLEqual judge @url and @c is equal or not. func (c URL) URLEqual(url URL) bool { c.Ip = "" c.Port = "" @@ -265,17 +266,19 @@ func (c URL) URLEqual(url URL) bool { } else if urlGroup == constant.ANY_VALUE { urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1) } + + // 1. protocol, username, password, ip, port, service name, group, version should be equal if cKey != urlKey { return false } + + // 2. if url contains enabled key, should be true, or * if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE { return false } - //TODO :may need add interface key any value condition - if !isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) { - return false - } - return true + + // TODO :may need add interface key any value condition + return isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) } func isMatchCategory(category1 string, category2 string) bool { @@ -291,38 +294,35 @@ func isMatchCategory(category1 string, category2 string) bool { } func (c URL) String() string { - var buildString string + var buf strings.Builder if len(c.Username) == 0 && len(c.Password) == 0 { - buildString = fmt.Sprintf( + buf.WriteString(fmt.Sprintf( "%s://%s:%s%s?", - c.Protocol, c.Ip, c.Port, c.Path) + c.Protocol, c.Ip, c.Port, c.Path)) } else { - buildString = fmt.Sprintf( + buf.WriteString(fmt.Sprintf( "%s://%s:%s@%s:%s%s?", - c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path) + c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)) } - c.paramsLock.RLock() - buildString += c.params.Encode() - c.paramsLock.RUnlock() - return buildString + buf.WriteString(c.params.Encode()) + return buf.String() } -// Key ... +// Key gets key func (c URL) Key() string { buildString := fmt.Sprintf( "%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, "")) return buildString - //return c.ServiceKey() } -// ServiceKey ... +// ServiceKey gets a unique key of a service. func (c URL) ServiceKey() string { intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if intf == "" { return "" } - buf := &bytes.Buffer{} + var buf strings.Builder group := c.GetParam(constant.GROUP_KEY, "") if group != "" { buf.WriteString(group) @@ -347,7 +347,7 @@ func (c *URL) ColonSeparatedKey() string { if intf == "" { return "" } - buf := &bytes.Buffer{} + var buf strings.Builder buf.WriteString(intf) buf.WriteString(":") version := c.GetParam(constant.VERSION_KEY, "") @@ -362,44 +362,44 @@ func (c *URL) ColonSeparatedKey() string { return buf.String() } -// EncodedServiceKey ... +// EncodedServiceKey encode the service key func (c *URL) EncodedServiceKey() string { serviceKey := c.ServiceKey() return strings.Replace(serviceKey, "/", "*", 1) } -// Service ... +// Service gets service func (c URL) Service() string { service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if service != "" { return service } else if c.SubURL != nil { service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) - if service != "" { //if url.path is "" then return suburl's path, special for registry url + if service != "" { // if url.path is "" then return suburl's path, special for registry url return service } } return "" } -// AddParam ... +// AddParam will add the key-value pair +// Not thread-safe +// think twice before using it. func (c *URL) AddParam(key string, value string) { - c.paramsLock.Lock() c.params.Add(key, value) - c.paramsLock.Unlock() } -// SetParam ... +// SetParam will put the key-value pair into url +// it's not thread safe. +// think twice before you want to use this method +// usually it should only be invoked when you want to initialized an url func (c *URL) SetParam(key string, value string) { - c.paramsLock.Lock() c.params.Set(key, value) - c.paramsLock.Unlock() } -// RangeParams ... +// RangeParams will iterate the params +// it's not thread-safe func (c *URL) RangeParams(f func(key, value string) bool) { - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() for k, v := range c.params { if !f(k, v[0]) { break @@ -407,32 +407,28 @@ func (c *URL) RangeParams(f func(key, value string) bool) { } } -// GetParam ... +// GetParam gets value by key func (c URL) GetParam(s string, d string) string { - var r string - c.paramsLock.RLock() - if r = c.params.Get(s); len(r) == 0 { + r := c.params.Get(s) + if len(r) == 0 { r = d } - c.paramsLock.RUnlock() return r } -// GetParams ... +// GetParams gets values func (c URL) GetParams() url.Values { return c.params } -// GetParamAndDecoded ... +// GetParamAndDecoded gets values and decode func (c URL) GetParamAndDecoded(key string) (string, error) { - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, "")) value := string(ruleDec) return value, err } -// GetRawParam ... +// GetRawParam gets raw param func (c URL) GetRawParam(key string) string { switch key { case "protocol": @@ -452,76 +448,61 @@ func (c URL) GetRawParam(key string) string { } } -// GetParamBool ... -func (c URL) GetParamBool(s string, d bool) bool { - - var r bool - var err error - if r, err = strconv.ParseBool(c.GetParam(s, "")); err != nil { +// GetParamBool judge whether @key exists or not +func (c URL) GetParamBool(key string, d bool) bool { + r, err := strconv.ParseBool(c.GetParam(key, "")) + if err != nil { return d } return r } -// GetParamInt ... -func (c URL) GetParamInt(s string, d int64) int64 { - var r int - var err error - - if r, err = strconv.Atoi(c.GetParam(s, "")); r == 0 || err != nil { +// GetParamInt gets int value by @key +func (c URL) GetParamInt(key string, d int64) int64 { + r, err := strconv.Atoi(c.GetParam(key, "")) + if r == 0 || err != nil { return d } return int64(r) } -// GetMethodParamInt ... +// GetMethodParamInt gets int method param func (c URL) GetMethodParamInt(method string, key string, d int64) int64 { - var r int - var err error - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() - if r, err = strconv.Atoi(c.GetParam("methods."+method+"."+key, "")); r == 0 || err != nil { + r, err := strconv.Atoi(c.GetParam("methods."+method+"."+key, "")) + if r == 0 || err != nil { return d } return int64(r) } -// GetMethodParamInt64 ... +// GetMethodParamInt64 gets int64 method param func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { r := c.GetMethodParamInt(method, key, math.MinInt64) if r == math.MinInt64 { return c.GetParamInt(key, d) } - return r } -// GetMethodParam ... +// GetMethodParam gets method param func (c URL) GetMethodParam(method string, key string, d string) string { - var r string - if r = c.GetParam("methods."+method+"."+key, ""); r == "" { + r := c.GetParam("methods."+method+"."+key, "") + if r == "" { r = d } return r } -// GetMethodParamBool ... +// GetMethodParamBool judge whether @method param exists or not func (c URL) GetMethodParamBool(method string, key string, d bool) bool { r := c.GetParamBool("methods."+method+"."+key, d) return r } -// RemoveParams ... -func (c *URL) RemoveParams(set *gxset.HashSet) { - c.paramsLock.Lock() - defer c.paramsLock.Unlock() - for k := range set.Items { - s := k.(string) - delete(c.params, s) - } -} - -// SetParams ... +// SetParams will put all key-value pair into url. +// 1. if there already has same key, the value will be override +// 2. it's not thread safe +// 3. think twice when you want to invoke this method func (c *URL) SetParams(m url.Values) { for k := range m { c.SetParam(k, m.Get(k)) @@ -530,7 +511,6 @@ func (c *URL) SetParams(m url.Values) { // ToMap transfer URL to Map func (c URL) ToMap() map[string]string { - paramsMap := make(map[string]string) c.RangeParams(func(key, value string) bool { @@ -571,29 +551,35 @@ func (c URL) ToMap() map[string]string { // configuration > reference config >service config // in this function we should merge the reference local url config into the service url from registry. -//TODO configuration merge, in the future , the configuration center's config should merge too. - -// MergeUrl ... +// TODO configuration merge, in the future , the configuration center's config should merge too. + +// MergeUrl will merge those two url +// the result is based on serviceUrl, and the key which si only contained in referenceUrl +// will be added into result. +// for example, if serviceUrl contains params (a1->v1, b1->v2) and referenceUrl contains params(a2->v3, b1 -> v4) +// the params of result will be (a1->v1, b1->v2, a2->v3). +// You should notice that the value of b1 is v2, not v4. +// due to URL is not thread-safe, so this method is not thread-safe func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { mergedUrl := serviceUrl.Clone() - //iterator the referenceUrl if serviceUrl not have the key ,merge in + // iterator the referenceUrl if serviceUrl not have the key ,merge in referenceUrl.RangeParams(func(key, value string) bool { if v := mergedUrl.GetParam(key, ""); len(v) == 0 { mergedUrl.SetParam(key, value) } return true }) - //loadBalance,cluster,retries strategy config + // loadBalance,cluster,retries strategy config methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY}) - //remote timestamp + // remote timestamp if v := serviceUrl.GetParam(constant.TIMESTAMP_KEY, ""); len(v) > 0 { mergedUrl.SetParam(constant.REMOTE_TIMESTAMP_KEY, v) mergedUrl.SetParam(constant.TIMESTAMP_KEY, referenceUrl.GetParam(constant.TIMESTAMP_KEY, "")) } - //finally execute methodConfigMergeFcn + // finally execute methodConfigMergeFcn for _, method := range referenceUrl.Methods { for _, fcn := range methodConfigMergeFcn { fcn("methods." + method) @@ -603,7 +589,7 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { return mergedUrl } -// Clone ... +// Clone will copy the url func (c *URL) Clone() *URL { newUrl := &URL{} copier.Copy(newUrl, c) @@ -615,8 +601,43 @@ func (c *URL) Clone() *URL { return newUrl } +func (c *URL) CloneExceptParams(excludeParams *gxset.HashSet) *URL { + newUrl := &URL{} + copier.Copy(newUrl, c) + newUrl.params = url.Values{} + c.RangeParams(func(key, value string) bool { + if !excludeParams.Contains(key) { + newUrl.SetParam(key, value) + } + return true + }) + return newUrl +} + +// Copy url based on the reserved parameter's keys. +func (c *URL) CloneWithParams(reserveParams []string) *URL { + params := url.Values{} + for _, reserveParam := range reserveParams { + v := c.GetParam(reserveParam, "") + if len(v) != 0 { + params.Set(reserveParam, v) + } + } + + return NewURLWithOptions( + WithProtocol(c.Protocol), + WithUsername(c.Username), + WithPassword(c.Password), + WithIp(c.Ip), + WithPort(c.Port), + WithPath(c.Path), + WithMethods(c.Methods), + WithParams(params), + ) +} + func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) { - var methodConfigMergeFcn = []func(method string){} + methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys)) for _, paramKey := range paramKeys { if v := referenceUrl.GetParam(paramKey, ""); len(v) > 0 { mergedUrl.SetParam(paramKey, v) diff --git a/common/yaml/yaml.go b/common/yaml/yaml.go index 93ebb16614..5edda1b3c7 100644 --- a/common/yaml/yaml.go +++ b/common/yaml/yaml.go @@ -40,7 +40,7 @@ func LoadYMLConfig(confProFile string) ([]byte, error) { return ioutil.ReadFile(confProFile) } -// unmarshalYMLConfig Load yml config byte from file , then unmarshal to object +// unmarshalYMLConfig Load yml config byte from file, then unmarshal to object func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) { confFileStream, err := LoadYMLConfig(confProFile) if err != nil { diff --git a/config/application_config.go b/config/application_config.go index 23ab7d34ac..33b47c81dd 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -33,6 +33,7 @@ type ApplicationConfig struct { Version string `yaml:"version" json:"version,omitempty" property:"version"` Owner string `yaml:"owner" json:"owner,omitempty" property:"owner"` Environment string `yaml:"environment" json:"environment,omitempty" property:"environment"` + MetadataType string `default:"local" yaml:"metadataType" json:"metadataType,omitempty" property:"metadataType"` //field for metadata report } // Prefix ... diff --git a/config/base_config_test.go b/config/base_config_test.go index d16b242092..bc422d0189 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -21,9 +21,11 @@ import ( "reflect" "testing" ) + import ( "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/extension" @@ -481,7 +483,6 @@ func Test_refreshProvider(t *testing.T) { } func Test_startConfigCenter(t *testing.T) { - extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory { return &config_center.MockDynamicConfigurationFactory{} }) @@ -499,21 +500,21 @@ func Test_startConfigCenter(t *testing.T) { } func Test_initializeStruct(t *testing.T) { - consumerConfig := &ConsumerConfig{} + testConsumerConfig := &ConsumerConfig{} tp := reflect.TypeOf(ConsumerConfig{}) v := reflect.New(tp) initializeStruct(tp, v.Elem()) - fmt.Println(reflect.ValueOf(consumerConfig).Elem().Type().String()) + fmt.Println(reflect.ValueOf(testConsumerConfig).Elem().Type().String()) fmt.Println(v.Elem().Type().String()) - reflect.ValueOf(consumerConfig).Elem().Set(v.Elem()) + reflect.ValueOf(testConsumerConfig).Elem().Set(v.Elem()) assert.Condition(t, func() (success bool) { - return consumerConfig.ApplicationConfig != nil + return testConsumerConfig.ApplicationConfig != nil }) assert.Condition(t, func() (success bool) { - return consumerConfig.Registries != nil + return testConsumerConfig.Registries != nil }) assert.Condition(t, func() (success bool) { - return consumerConfig.References != nil + return testConsumerConfig.References != nil }) } diff --git a/config/config_loader.go b/config/config_loader.go index c0687d8fc1..c84817eaea 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -81,128 +81,142 @@ func checkApplicationName(config *ApplicationConfig) { } } -// Load Dubbo Init -func Load() { - - // init router - if confRouterFile != "" { - if errPro := RouterInit(confRouterFile); errPro != nil { - log.Printf("[routerConfig init] %#v", errPro) - } - } - - // reference config +func loadConsumerConfig() { if consumerConfig == nil { logger.Warnf("consumerConfig is nil!") - } else { - // init other consumer config - conConfigType := consumerConfig.ConfigType - for key, value := range extension.GetDefaultConfigReader() { - if conConfigType == nil { - if v, ok := conConfigType[key]; ok { - value = v - } - } - if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil { - logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value) + return + } + // init other consumer config + conConfigType := consumerConfig.ConfigType + for key, value := range extension.GetDefaultConfigReader() { + if conConfigType == nil { + if v, ok := conConfigType[key]; ok { + value = v } } + if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil { + logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value) + } + } - metricConfig = consumerConfig.MetricConfig - applicationConfig = consumerConfig.ApplicationConfig + metricConfig = consumerConfig.MetricConfig + applicationConfig = consumerConfig.ApplicationConfig - checkApplicationName(consumerConfig.ApplicationConfig) - if err := configCenterRefreshConsumer(); err != nil { - logger.Errorf("[consumer config center refresh] %#v", err) + checkApplicationName(consumerConfig.ApplicationConfig) + 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) + SetConsumerService(genericService) } - checkRegistries(consumerConfig.Registries, consumerConfig.Registry) - for key, ref := range consumerConfig.References { - if ref.Generic { - genericService := NewGenericService(key) - SetConsumerService(genericService) - } - rpcService := GetConsumerService(key) - if rpcService == nil { - logger.Warnf("%s does not exist!", key) - continue - } - ref.id = key - ref.Refer(rpcService) - ref.Implement(rpcService) + rpcService := GetConsumerService(key) + if rpcService == nil { + logger.Warnf("%s does not exist!", key) + continue } + ref.id = key + ref.Refer(rpcService) + ref.Implement(rpcService) + } - //wait for invoker is available, if wait over default 3s, then panic - var count int - checkok := true - for { - for _, refconfig := range consumerConfig.References { - if (refconfig.Check != nil && *refconfig.Check) || - (refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) || - (refconfig.Check == nil && consumerConfig.Check == nil) { //default to true - - if refconfig.invoker != nil && - !refconfig.invoker.IsAvailable() { - checkok = false - count++ - if count > maxWait { - errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) - logger.Error(errMsg) - panic(errMsg) - } - time.Sleep(time.Second * 1) - break - } - if refconfig.invoker == nil { - logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName) + //wait for invoker is available, if wait over default 3s, then panic + var count int + checkok := true + for { + for _, refconfig := range consumerConfig.References { + if (refconfig.Check != nil && *refconfig.Check) || + (refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) || + (refconfig.Check == nil && consumerConfig.Check == nil) { //default to true + + if refconfig.invoker != nil && + !refconfig.invoker.IsAvailable() { + checkok = false + count++ + if count > maxWait { + errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) + logger.Error(errMsg) + panic(errMsg) } + time.Sleep(time.Second * 1) + break + } + if refconfig.invoker == nil { + logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName) } } - if checkok { - break - } - checkok = true } + if checkok { + break + } + checkok = true } +} - // service config +func loadProviderConfig() { if providerConfig == nil { logger.Warnf("providerConfig is nil!") - } else { - // init other provider config - proConfigType := providerConfig.ConfigType - for key, value := range extension.GetDefaultConfigReader() { - if proConfigType != nil { - if v, ok := proConfigType[key]; ok { - value = v - } - } - if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil { - logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value) + return + } + + // init other provider config + proConfigType := providerConfig.ConfigType + for key, value := range extension.GetDefaultConfigReader() { + if proConfigType != nil { + if v, ok := proConfigType[key]; ok { + value = v } } + if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil { + logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value) + } + } - // so, you should know that the consumer's config will be override - metricConfig = providerConfig.MetricConfig - applicationConfig = providerConfig.ApplicationConfig + // so, you should know that the consumer's config will be override + metricConfig = providerConfig.MetricConfig + applicationConfig = providerConfig.ApplicationConfig - checkApplicationName(providerConfig.ApplicationConfig) - if err := configCenterRefreshProvider(); err != nil { - logger.Errorf("[provider config center refresh] %#v", err) + checkApplicationName(providerConfig.ApplicationConfig) + 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 { + logger.Warnf("%s does not exist!", key) + continue } - checkRegistries(providerConfig.Registries, providerConfig.Registry) - for key, svs := range providerConfig.Services { - rpcService := GetProviderService(key) - if rpcService == nil { - logger.Warnf("%s does not exist!", key) - continue - } - svs.id = key - svs.Implement(rpcService) - if err := svs.Export(); err != nil { - panic(fmt.Sprintf("service %s export failed! ", key)) - } + svs.id = key + svs.Implement(rpcService) + if err := svs.Export(); err != nil { + panic(fmt.Sprintf("service %s export failed! err: %#v", key, err)) } } +} + +func initRouter() { + if confRouterFile != "" { + if err := RouterInit(confRouterFile); err != nil { + log.Printf("[routerConfig init] %#v", err) + } + } +} + +// Load Dubbo Init +func Load() { + + // init router + initRouter() + + // reference config + loadConsumerConfig() + + // service config + loadProviderConfig() + // init the shutdown callback GracefulShutdownInit() } diff --git a/config/config_loader_test.go b/config/config_loader_test.go index 498f826780..6368fcbd2c 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -82,7 +82,8 @@ func TestLoad(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - common.ServiceMap.UnRegister("mock", "MockService") + err := common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") + assert.Nil(t, err) consumerConfig = nil providerConfig = nil } @@ -110,7 +111,7 @@ func TestLoadWithSingleReg(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - common.ServiceMap.UnRegister("mock", "MockService") + common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") consumerConfig = nil providerConfig = nil } @@ -139,7 +140,7 @@ func TestWithNoRegLoad(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - common.ServiceMap.UnRegister("mock", "MockService") + common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") consumerConfig = nil providerConfig = nil } diff --git a/config/consumer_config.go b/config/consumer_config.go index 1fa68415bf..1b563054ec 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -44,6 +44,7 @@ type ConsumerConfig struct { Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` // application ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"` + // client Connect_Timeout string `default:"100ms" yaml:"connect_timeout" json:"connect_timeout,omitempty" property:"connect_timeout"` ConnectTimeout time.Duration @@ -117,6 +118,7 @@ func ConsumerInit(confConFile string) error { return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout) } } + logger.Debugf("consumer config{%#v}\n", consumerConfig) return nil @@ -127,7 +129,7 @@ func configCenterRefreshConsumer() error { var err error if consumerConfig.ConfigCenterConfig != nil { consumerConfig.SetFatherConfig(consumerConfig) - if err := consumerConfig.startConfigCenter(); err != nil { + if err = consumerConfig.startConfigCenter(); err != nil { return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err)) } consumerConfig.fresh() @@ -142,6 +144,5 @@ func configCenterRefreshConsumer() error { return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout) } } - return nil } diff --git a/config/instance/metedata_report.go b/config/instance/metedata_report.go new file mode 100644 index 0000000000..cd54b0a794 --- /dev/null +++ b/config/instance/metedata_report.go @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package instance + +import ( + "sync" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/metadata" +) + +var ( + instance metadata.MetadataReport + once sync.Once +) + +// GetMetadataReportInstance ... +func GetMetadataReportInstance(url *common.URL) metadata.MetadataReport { + once.Do(func() { + instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url) + }) + return instance +} diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go new file mode 100644 index 0000000000..2b926c0b94 --- /dev/null +++ b/config/metadata_report_config.go @@ -0,0 +1,110 @@ +/* + * 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 config + +import ( + "net/url" +) + +import ( + "github.com/creasty/defaults" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config/instance" +) + +// MethodConfig ... +type MetadataReportConfig struct { + Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty"` + Address string `yaml:"address" json:"address,omitempty" property:"address"` + Username string `yaml:"username" json:"username,omitempty" property:"username"` + Password string `yaml:"password" json:"password,omitempty" property:"password"` + Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` + TimeoutStr string `yaml:"timeout" default:"5s" json:"timeout,omitempty" property:"timeout"` // unit: second + Group string `yaml:"group" json:"group,omitempty" property:"group"` +} + +// Prefix ... +func (c *MetadataReportConfig) Prefix() string { + return constant.MetadataReportPrefix +} + +// UnmarshalYAML ... +func (c *MetadataReportConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + if err := defaults.Set(c); err != nil { + return perrors.WithStack(err) + } + type plain MetadataReportConfig + if err := unmarshal((*plain)(c)); err != nil { + return perrors.WithStack(err) + } + return nil +} + +// ToUrl ... +func (c *MetadataReportConfig) ToUrl() (*common.URL, error) { + urlMap := make(url.Values) + + if c.Params != nil { + for k, v := range c.Params { + urlMap.Set(k, v) + } + } + + url, err := common.NewURL(c.Address, + common.WithParams(urlMap), + common.WithUsername(c.Username), + common.WithPassword(c.Password), + common.WithLocation(c.Address), + common.WithProtocol(c.Protocol), + ) + if err != nil || len(url.Protocol) == 0 { + return nil, perrors.New("Invalid MetadataReportConfig.") + } + url.SetParam("metadata", url.Protocol) + return &url, nil +} + +func (c *MetadataReportConfig) IsValid() bool { + return len(c.Protocol) != 0 +} + +// StartMetadataReport: The entry of metadata report start +func startMetadataReport(metadataType string, metadataReportConfig *MetadataReportConfig) error { + if metadataReportConfig == nil || metadataReportConfig.IsValid() { + return nil + } + + if metadataType == constant.METACONFIG_REMOTE { + return perrors.New("No MetadataConfig found, you must specify the remote Metadata Center address when 'metadata=remote' is enabled.") + } else if metadataType == constant.METACONFIG_REMOTE && len(metadataReportConfig.Address) == 0 { + return perrors.New("MetadataConfig address can not be empty.") + } + + if url, err := metadataReportConfig.ToUrl(); err == nil { + instance.GetMetadataReportInstance(url) + } else { + return perrors.Wrap(err, "Start MetadataReport failed.") + } + + return nil +} diff --git a/config/metadata_report_config_test.go b/config/metadata_report_config_test.go new file mode 100644 index 0000000000..635feecc2d --- /dev/null +++ b/config/metadata_report_config_test.go @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package config + +import "testing" + +import ( + "github.com/stretchr/testify/assert" +) + +func TestMetadataReportConfig_ToUrl(t *testing.T) { + metadataReportConfig := MetadataReportConfig{ + Protocol: "mock", + Address: "127.0.0.1:2181", + Username: "test", + Password: "test", + TimeoutStr: "3s", + Params: map[string]string{ + "k": "v", + }, + } + url, error := metadataReportConfig.ToUrl() + assert.NoError(t, error) + assert.Equal(t, "mock", url.Protocol) + assert.Equal(t, "127.0.0.1:2181", url.Location) + assert.Equal(t, "127.0.0.1", url.Ip) + assert.Equal(t, "2181", url.Port) + assert.Equal(t, "test", url.Username) + assert.Equal(t, "test", url.Password) + assert.Equal(t, "v", url.GetParam("k", "")) + assert.Equal(t, "mock", url.GetParam("metadata", "")) +} diff --git a/config/provider_config.go b/config/provider_config.go index 14b77cafb3..7956991745 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -41,6 +41,8 @@ type ProviderConfig struct { BaseConfig `yaml:",inline"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` + // metadata-report + MetadataReportConfig *MetadataReportConfig `yaml:"metadata_report" json:"metadata_report,omitempty" property:"metadata_report"` ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"` Registry *RegistryConfig `yaml:"registry" json:"registry,omitempty" property:"registry"` @@ -95,7 +97,10 @@ func ProviderInit(confProFile string) error { n.InterfaceId = k } } - + //start the metadata report if config set + if err := startMetadataReport(providerConfig.ApplicationConfig.MetadataType, providerConfig.MetadataReportConfig); err != nil { + return perrors.WithMessagef(err, "Provider starts metadata report error, and the error is {%#v}", err) + } logger.Debugf("provider config{%#v}\n", providerConfig) return nil diff --git a/config/reference_config.go b/config/reference_config.go index b9f3da1352..3710cbc4bc 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -188,7 +188,7 @@ func (c *ReferenceConfig) getUrlMap() url.Values { urlMap.Set(constant.VERSION_KEY, c.Version) urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(c.Generic)) urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) - urlMap.Set(constant.CATEGORY_KEY, (common.RoleType(common.CONSUMER)).String()) + urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version) urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.CONSUMER)).Role()) diff --git a/config/registry_config.go b/config/registry_config.go index 4e4b6e97d7..e877a2c19d 100644 --- a/config/registry_config.go +++ b/config/registry_config.go @@ -36,14 +36,15 @@ import ( // RegistryConfig ... type RegistryConfig struct { Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty" property:"protocol"` - //I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig + // I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig TimeoutStr string `yaml:"timeout" default:"5s" json:"timeout,omitempty" property:"timeout"` // unit: second Group string `yaml:"group" json:"group,omitempty" property:"group"` - //for registry - Address string `yaml:"address" json:"address,omitempty" property:"address"` - Username string `yaml:"username" json:"username,omitempty" property:"username"` - Password string `yaml:"password" json:"password,omitempty" property:"password"` - Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` + // for registry + Address string `yaml:"address" json:"address,omitempty" property:"address"` + Username string `yaml:"username" json:"username,omitempty" property:"username"` + Password string `yaml:"password" json:"password,omitempty" property:"password"` + Simplified bool `yaml:"simplified" json:"simplified,omitempty" property:"simplified"` + Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` } // UnmarshalYAML ... @@ -70,9 +71,11 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf for k, registryConf := range registries { target := false - // if user not config targetRegistries,default load all - // Notice:in func "func Split(s, sep string) []string" comment : if s does not contain sep and sep is not empty, SplitAfter returns a slice of length 1 whose only element is s. - // So we have to add the condition when targetRegistries string is not set (it will be "" when not set) + // if user not config targetRegistries, default load all + // Notice: in func "func Split(s, sep string) []string" comment: + // if s does not contain sep and sep is not empty, SplitAfter returns + // a slice of length 1 whose only element is s. So we have to add the + // condition when targetRegistries string is not set (it will be "" when not set) if len(trSlice) == 0 || (len(trSlice) == 1 && trSlice[0] == "") { target = true } else { @@ -86,29 +89,24 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf } if target { - var ( - url common.URL - err error - ) - addresses := strings.Split(registryConf.Address, ",") address := addresses[0] - address = traslateRegistryConf(address, registryConf) - url, err = common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address, + address = translateRegistryConf(address, registryConf) + url, err := common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address, common.WithParams(registryConf.getUrlMap(roleType)), + common.WithParamsValue("simplified", strconv.FormatBool(registryConf.Simplified)), common.WithUsername(registryConf.Username), common.WithPassword(registryConf.Password), common.WithLocation(registryConf.Address), ) if err != nil { - logger.Errorf("The registry id:%s url is invalid , error: %#v", k, err) + logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err) panic(err) } else { urls = append(urls, &url) } } - } return urls @@ -123,15 +121,14 @@ func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values { for k, v := range c.Params { urlMap.Set(k, v) } - return urlMap } -func traslateRegistryConf(address string, registryConf *RegistryConfig) string { +func translateRegistryConf(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) + logger.Errorf("The registry url is invalid, error: %#v", err) panic(err) } address = translatedUrl.Host diff --git a/config/service_config.go b/config/service_config.go index 5853146aa8..09308d032e 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -18,6 +18,7 @@ package config import ( + "container/list" "context" "fmt" "net/url" @@ -29,6 +30,7 @@ import ( import ( "github.com/creasty/defaults" + gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "go.uber.org/atomic" ) @@ -105,6 +107,24 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig { } } +// Get Random Port +func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List { + ports := list.New() + for _, proto := range protocolConfigs { + if len(proto.Port) > 0 { + continue + } + + tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip) + if err != nil { + panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err))) + } + defer tcp.Close() + ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1]) + } + return ports +} + // Export ... func (c *ServiceConfig) Export() error { // TODO: config center start here @@ -127,19 +147,29 @@ func (c *ServiceConfig) Export() error { logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol) return nil } + + ports := getRandomPort(protocolConfigs) + nextPort := ports.Front() for _, proto := range protocolConfigs { // registry the service reflect - methods, err := common.ServiceMap.Register(proto.Name, c.rpcService) + methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService) if err != nil { - err := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) - logger.Errorf(err.Error()) - return err + formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) + logger.Errorf(formatErr.Error()) + return formatErr + } + + port := proto.Port + + if len(proto.Port) == 0 { + port = nextPort.Value.(string) + nextPort = nextPort.Next() } ivkURL := common.NewURLWithOptions( common.WithPath(c.id), common.WithProtocol(proto.Name), common.WithIp(proto.Ip), - common.WithPort(proto.Port), + common.WithPort(port), common.WithParams(urlMap), common.WithParamsValue(constant.BEAN_NAME_KEY, c.id), common.WithMethods(strings.Split(methods, ",")), @@ -196,7 +226,6 @@ func (c *ServiceConfig) getUrlMap() url.Values { urlMap.Set(constant.GROUP_KEY, c.Group) urlMap.Set(constant.VERSION_KEY, c.Version) urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) - urlMap.Set(constant.CATEGORY_KEY, (common.RoleType(common.PROVIDER)).String()) urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version) urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.PROVIDER)).Role()) diff --git a/config/service_config_test.go b/config/service_config_test.go index 6f32308903..e39d32b7f7 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -21,6 +21,11 @@ import ( "testing" ) +import ( + gxnet "github.com/dubbogo/gost/net" + "github.com/stretchr/testify/assert" +) + import ( "github.com/apache/dubbo-go/common/extension" ) @@ -189,3 +194,35 @@ func Test_Export(t *testing.T) { } providerConfig = nil } + +func Test_getRandomPort(t *testing.T) { + protocolConfigs := make([]*ProtocolConfig, 0, 3) + + ip, err := gxnet.GetLocalIP() + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + assert.NoError(t, err) + ports := getRandomPort(protocolConfigs) + + assert.Equal(t, ports.Len(), len(protocolConfigs)) + + front := ports.Front() + for { + if front == nil { + break + } + t.Logf("port:%v", front.Value) + front = front.Next() + } + + protocolConfigs = make([]*ProtocolConfig, 0, 3) + ports = getRandomPort(protocolConfigs) + assert.Equal(t, ports.Len(), len(protocolConfigs)) +} diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index 3b5d1f4ebe..b049d334bc 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -25,7 +25,8 @@ import ( ) import ( - "github.com/pkg/errors" + gxset "github.com/dubbogo/gost/container/set" + perrors "github.com/pkg/errors" "github.com/zouyx/agollo" ) @@ -119,7 +120,7 @@ func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) { config := agollo.GetConfig(c.appConf.NamespaceName) if config == nil { - return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key)) + return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } return config.GetStringValue(key, ""), nil } @@ -128,6 +129,16 @@ func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, er return c.GetInternalProperty(key, opts...) } +// PublishConfig will publish the config with the (key, group, value) pair +func (c *apolloConfiguration) PublishConfig(string, string, string) error { + return perrors.New("unsupport operation") +} + +// GetConfigKeysByGroup will return all keys with the group +func (c *apolloConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + return nil, perrors.New("unsupport operation") +} + func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (string, error) { /** * when group is not null, we are getting startup configs(config file) from Config Center, for example: @@ -135,7 +146,7 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri */ config := agollo.GetConfig(key) if config == nil { - return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key)) + return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } return config.GetContent(agollo.Properties), nil } diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go index fb257a4828..1cf65ed22b 100644 --- a/config_center/apollo/listener.go +++ b/config_center/apollo/listener.go @@ -29,7 +29,7 @@ type apolloListener struct { listeners map[config_center.ConfigurationListener]struct{} } -// NewApolloListener ... +// NewApolloListener creates a new apolloListener func NewApolloListener() *apolloListener { return &apolloListener{ listeners: make(map[config_center.ConfigurationListener]struct{}, 0), @@ -49,7 +49,7 @@ func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) { } } -// AddListener ... +// AddListener adds a listener for apollo func (a *apolloListener) AddListener(l config_center.ConfigurationListener) { if _, ok := a.listeners[l]; !ok { a.listeners[l] = struct{}{} @@ -57,7 +57,7 @@ func (a *apolloListener) AddListener(l config_center.ConfigurationListener) { } } -// RemoveListener ... +// RemoveListener removes listeners of apollo func (a *apolloListener) RemoveListener(l config_center.ConfigurationListener) { delete(a.listeners, l) } diff --git a/config_center/configuration_listener.go b/config_center/configuration_listener.go index e70e4f6807..541cc09286 100644 --- a/config_center/configuration_listener.go +++ b/config_center/configuration_listener.go @@ -25,12 +25,12 @@ import ( "github.com/apache/dubbo-go/remoting" ) -// ConfigurationListener ... +// ConfigurationListener for changing listener's event type ConfigurationListener interface { Process(*ConfigChangeEvent) } -// ConfigChangeEvent ... +// ConfigChangeEvent for changing listener's event type ConfigChangeEvent struct { Key string Value interface{} diff --git a/config_center/configurator.go b/config_center/configurator.go index ffa9034e05..9db4804365 100644 --- a/config_center/configurator.go +++ b/config_center/configurator.go @@ -21,7 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) -// Configurator ... +// Configurator supports GetUrl and constructor type Configurator interface { GetUrl() *common.URL Configure(url *common.URL) diff --git a/config_center/configurator/mock.go b/config_center/configurator/mock.go index d294b9195d..7ec7179634 100644 --- a/config_center/configurator/mock.go +++ b/config_center/configurator/mock.go @@ -23,7 +23,7 @@ import ( "github.com/apache/dubbo-go/config_center" ) -// NewMockConfigurator ... +// NewMockConfigurator creates a new mockConfigurator func NewMockConfigurator(url *common.URL) config_center.Configurator { return &mockConfigurator{configuratorUrl: url} } @@ -32,12 +32,12 @@ type mockConfigurator struct { configuratorUrl *common.URL } -// GetUrl ... +// GetUrl gets a configuratorUrl func (c *mockConfigurator) GetUrl() *common.URL { return c.configuratorUrl } -// Configure ... +// Configure sets up param CLUSTER_KEY and cluster for url func (c *mockConfigurator) Configure(url *common.URL) { if cluster := c.GetUrl().GetParam(constant.CLUSTER_KEY, ""); cluster != "" { url.SetParam(constant.CLUSTER_KEY, cluster) diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go index 18415bee3a..ebd3dc601b 100644 --- a/config_center/configurator/override.go +++ b/config_center/configurator/override.go @@ -50,12 +50,12 @@ func (c *overrideConfigurator) GetUrl() *common.URL { } func (c *overrideConfigurator) Configure(url *common.URL) { - //remove configuratorUrl some param that can not be configured + // remove configuratorUrl some param that can not be configured if c.configuratorUrl.GetParam(constant.ENABLED_KEY, "true") == "false" || len(c.configuratorUrl.Location) == 0 { return } - //branch for version 2.7.x + // branch for version 2.7.x apiVersion := c.configuratorUrl.GetParam(constant.CONFIG_VERSION_KEY, "") if len(apiVersion) != 0 { currentSide := url.GetParam(constant.SIDE_KEY, "") @@ -67,12 +67,12 @@ func (c *overrideConfigurator) Configure(url *common.URL) { c.configureIfMatch(url.Ip, url) } } else { - //branch for version 2.6.x and less + // branch for version 2.6.x and less c.configureDeprecated(url) } } -//translate from java, compatible rules in java +// configureIfMatch translate from java, compatible rules in java func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) { if constant.ANYHOST_VALUE == c.configuratorUrl.Ip || host == c.configuratorUrl.Ip { providers := c.configuratorUrl.GetParam(constant.OVERRIDE_PROVIDERS_KEY, "") @@ -105,8 +105,7 @@ func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) { if returnUrl { return } - configUrl := c.configuratorUrl.Clone() - configUrl.RemoveParams(conditionKeys) + configUrl := c.configuratorUrl.CloneExceptParams(conditionKeys) url.SetParams(configUrl.GetParams()) } } diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index d6c3b06b32..540febc9d3 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -21,14 +21,18 @@ import ( "time" ) +import ( + gxset "github.com/dubbogo/gost/container/set" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/config_center/parser" ) -////////////////////////////////////////// +// //////////////////////////////////////// // DynamicConfiguration -////////////////////////////////////////// +// //////////////////////////////////////// const ( // DEFAULT_GROUP: default group DEFAULT_GROUP = "dubbo" @@ -36,20 +40,26 @@ const ( DEFAULT_CONFIG_TIMEOUT = "10s" ) -// DynamicConfiguration ... +// DynamicConfiguration for modify listener and get properties file type DynamicConfiguration interface { Parser() parser.ConfigurationParser SetParser(parser.ConfigurationParser) AddListener(string, ConfigurationListener, ...Option) RemoveListener(string, ConfigurationListener, ...Option) - //GetProperties get properties file + // GetProperties get properties file GetProperties(string, ...Option) (string, error) - //GetRule get Router rule properties file + // GetRule get Router rule properties file GetRule(string, ...Option) (string, error) - //GetInternalProperty get value by key in Default properties file(dubbo.properties) + // GetInternalProperty get value by key in Default properties file(dubbo.properties) GetInternalProperty(string, ...Option) (string, error) + + // PublishConfig will publish the config with the (key, group, value) pair + PublishConfig(string, string, string) error + + // GetConfigKeysByGroup will return all keys with the group + GetConfigKeysByGroup(group string) (*gxset.HashSet, error) } // Options ... @@ -61,21 +71,21 @@ type Options struct { // Option ... type Option func(*Options) -// WithGroup ... +// WithGroup assigns group to opt.Group func WithGroup(group string) Option { return func(opt *Options) { opt.Group = group } } -// WithTimeout ... +// WithTimeout assigns time to opt.Timeout func WithTimeout(time time.Duration) Option { return func(opt *Options) { opt.Timeout = time } } -//GetRuleKey The format is '{interfaceName}:[version]:[group]' +// GetRuleKey The format is '{interfaceName}:[version]:[group]' func GetRuleKey(url common.URL) string { return url.ColonSeparatedKey() } diff --git a/config_center/dynamic_configuration_factory.go b/config_center/dynamic_configuration_factory.go index 9f9b13227f..46faf86444 100644 --- a/config_center/dynamic_configuration_factory.go +++ b/config_center/dynamic_configuration_factory.go @@ -21,7 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) -// DynamicConfigurationFactory ... +// DynamicConfigurationFactory gets the DynamicConfiguration type DynamicConfigurationFactory interface { GetDynamicConfiguration(*common.URL) (DynamicConfiguration, error) } diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 4d972b629a..9cfb9e6078 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -22,6 +22,7 @@ import ( ) import ( + gxset "github.com/dubbogo/gost/container/set" "gopkg.in/yaml.v2" ) @@ -42,7 +43,7 @@ var ( dynamicConfiguration *MockDynamicConfiguration ) -// GetDynamicConfiguration ... +// GetDynamicConfiguration returns a DynamicConfiguration func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) (DynamicConfiguration, error) { var err error once.Do(func() { @@ -81,6 +82,16 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) } +// PublishConfig will publish the config with the (key, group, value) pair +func (c *MockDynamicConfiguration) PublishConfig(string, string, string) error { + return nil +} + +// GetConfigKeysByGroup will return all keys with the group +func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + return gxset.NewSet(c.content), nil +} + // MockDynamicConfiguration ... type MockDynamicConfiguration struct { parser parser.ConfigurationParser @@ -88,16 +99,16 @@ type MockDynamicConfiguration struct { listener map[string]ConfigurationListener } -// AddListener ... +// AddListener adds a listener for MockDynamicConfiguration func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, _ ...Option) { c.listener[key] = listener } -// RemoveListener ... +// RemoveListener removes the listener for MockDynamicConfiguration func (c *MockDynamicConfiguration) RemoveListener(_ string, _ ConfigurationListener, _ ...Option) { } -// GetConfig ... +// GetConfig returns content of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetConfig(_ string, _ ...Option) (string, error) { return c.content, nil @@ -108,17 +119,17 @@ func (c *MockDynamicConfiguration) GetConfigs(key string, opts ...Option) (strin return c.GetConfig(key, opts...) } -// Parser ... +// Parser returns a parser of MockDynamicConfiguration func (c *MockDynamicConfiguration) Parser() parser.ConfigurationParser { return c.parser } -// SetParser ... +// SetParser sets parser of MockDynamicConfiguration func (c *MockDynamicConfiguration) SetParser(p parser.ConfigurationParser) { c.parser = p } -// GetProperties ... +// GetProperties gets content of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetProperties(_ string, _ ...Option) (string, error) { return c.content, nil } @@ -128,7 +139,7 @@ func (c *MockDynamicConfiguration) GetInternalProperty(key string, opts ...Optio return c.GetProperties(key, opts...) } -// GetRule ... +// GetRule gets properties of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetRule(key string, opts ...Option) (string, error) { return c.GetProperties(key, opts...) } diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go index 60ab89b003..007b8be142 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -18,10 +18,12 @@ package nacos import ( + "strings" "sync" ) import ( + gxset "github.com/dubbogo/gost/container/set" "github.com/nacos-group/nacos-sdk-go/vo" perrors "github.com/pkg/errors" ) @@ -74,7 +76,7 @@ func (n *nacosDynamicConfiguration) RemoveListener(key string, listener config_c n.removeListener(key, listener) } -//nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key +// GetProperties nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key func (n *nacosDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) { return n.GetRule(key, opts...) } @@ -84,6 +86,33 @@ func (n *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...conf return n.GetProperties(key, opts...) } +// PublishConfig will publish the config with the (key, group, value) pair +func (n *nacosDynamicConfiguration) PublishConfig(key string, group string, value string) error { + + group = n.resolvedGroup(group) + + ok, err := (*n.client.Client()).PublishConfig(vo.ConfigParam{ + DataId: key, + Group: group, + Content: value, + }) + + if err != nil { + return perrors.WithStack(err) + } + if !ok { + return perrors.New("publish config to Nocos failed") + } + return nil +} + +// GetConfigKeysByGroup will return all keys with the group +func (n *nacosDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + // TODO (the golang client of nacos does not support batch API) + // we should build a issue and then think about how to resolve this problem + return nil, perrors.New("unsupport operation, wait for implement") +} + // GetRule Get router rule func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { tmpOpts := &config_center.Options{} @@ -92,12 +121,12 @@ func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Op } content, err := (*n.client.Client()).GetConfig(vo.ConfigParam{ DataId: key, - Group: tmpOpts.Group, + Group: n.resolvedGroup(tmpOpts.Group), }) if err != nil { return "", perrors.WithStack(err) } else { - return string(content), nil + return content, nil } } @@ -145,6 +174,15 @@ func (n *nacosDynamicConfiguration) Destroy() { n.closeConfigs() } +// resolvedGroup will regular the group. Now, it will replace the '/' with '-'. +// '/' is a special character for nacos +func (n *nacosDynamicConfiguration) resolvedGroup(group string) string { + if len(group) <= 0 { + return group + } + return strings.ReplaceAll(group, "/", "-") +} + // IsAvailable Get available status func (n *nacosDynamicConfiguration) IsAvailable() bool { select { @@ -155,12 +193,12 @@ func (n *nacosDynamicConfiguration) IsAvailable() bool { } } -func (r *nacosDynamicConfiguration) closeConfigs() { - r.cltLock.Lock() - client := r.client - r.client = nil - r.cltLock.Unlock() +func (n *nacosDynamicConfiguration) closeConfigs() { + n.cltLock.Lock() + client := n.client + n.client = nil + n.cltLock.Unlock() // Close the old client first to close the tmp node client.Close() - logger.Infof("begin to close provider nacos client") + logger.Infof("begin to close provider n client") } diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go index b4e6f1d025..4032c91cda 100644 --- a/config_center/nacos/impl_test.go +++ b/config_center/nacos/impl_test.go @@ -60,12 +60,7 @@ func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request), func mockCommonNacosServer() *httptest.Server { return runMockConfigServer(func(writer http.ResponseWriter, request *http.Request) { - data := ` - dubbo.service.com.ikurento.user.UserProvider.cluster=failback - dubbo.service.com.ikurento.user.UserProvider.protocol=myDubbo1 - dubbo.protocols.myDubbo.port=20000 - dubbo.protocols.myDubbo.name=dubbo -` + data := "true" fmt.Fprintf(writer, "%s", data) }, func(writer http.ResponseWriter, request *http.Request) { data := `dubbo.properties%02dubbo%02dubbo.service.com.ikurento.user.UserProvider.cluster=failback` @@ -93,6 +88,16 @@ func Test_GetConfig(t *testing.T) { assert.NoError(t, err) } +func TestNacosDynamicConfiguration_PublishConfig(t *testing.T) { + nacos, err := initNacosData(t) + assert.Nil(t, err) + key := "myKey" + group := "/custom/a/b" + value := "MyValue" + err = nacos.PublishConfig(key, group, value) + assert.Nil(t, err) +} + func Test_AddListener(t *testing.T) { nacos, err := initNacosData(t) assert.NoError(t, err) diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index 25c586586c..de74cff8f6 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -35,11 +35,11 @@ func callback(listener config_center.ConfigurationListener, namespace, group, da listener.Process(&config_center.ConfigChangeEvent{Key: dataId, Value: data, ConfigType: remoting.EventTypeUpdate}) } -func (l *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { - _, loaded := l.keyListeners.Load(key) +func (n *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { + _, loaded := n.keyListeners.Load(key) if !loaded { _, cancel := context.WithCancel(context.Background()) - err := (*l.client.Client()).ListenConfig(vo.ConfigParam{ + err := (*n.client.Client()).ListenConfig(vo.ConfigParam{ DataId: key, Group: "dubbo", OnChange: func(namespace, group, dataId, data string) { @@ -49,14 +49,14 @@ func (l *nacosDynamicConfiguration) addListener(key string, listener config_cent logger.Errorf("nacos : listen config fail, error:%v ", err) newListener := make(map[config_center.ConfigurationListener]context.CancelFunc) newListener[listener] = cancel - l.keyListeners.Store(key, newListener) + n.keyListeners.Store(key, newListener) } else { // TODO check goroutine alive, but this version of go_nacos_sdk is not support. logger.Infof("profile:%s. this profile is already listening", key) } } -func (l *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) { +func (n *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) { // TODO: not supported in current go_nacos_sdk version logger.Warn("not supported in current go_nacos_sdk version") } diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go index f33b4ba866..6fbdc27d43 100644 --- a/config_center/parser/configuration_parser.go +++ b/config_center/parser/configuration_parser.go @@ -47,7 +47,7 @@ type ConfigurationParser interface { ParseToUrls(content string) ([]*common.URL, error) } -// DefaultConfigurationParser for support properties file in config center +// DefaultConfigurationParser for supporting properties file in config center type DefaultConfigurationParser struct{} // ConfiguratorConfig ... @@ -71,7 +71,7 @@ type ConfigItem struct { Side string `yaml:"side"` } -// Parse ... +// Parse load content func (parser *DefaultConfigurationParser) Parse(content string) (map[string]string, error) { pps, err := properties.LoadString(content) if err != nil { diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index 404243d475..0a1ce35306 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -25,6 +25,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" ) @@ -39,8 +40,9 @@ import ( const ( // ZkClient - //zookeeper client name - ZkClient = "zk config_center" + // zookeeper client name + ZkClient = "zk config_center" + pathSeparator = "/" ) type zookeeperDynamicConfiguration struct { @@ -74,7 +76,7 @@ func newZookeeperDynamicConfiguration(url *common.URL) (*zookeeperDynamicConfigu c.cacheListener = NewCacheListener(c.rootPath) err = c.client.Create(c.rootPath) - c.listener.ListenServiceEvent(c.rootPath, c.cacheListener) + c.listener.ListenServiceEvent(url, c.rootPath, c.cacheListener) return c, err } @@ -100,7 +102,7 @@ func newMockZookeeperDynamicConfiguration(url *common.URL, opts ...zookeeper.Opt c.cacheListener = NewCacheListener(c.rootPath) err = c.client.Create(c.rootPath) - go c.listener.ListenServiceEvent(c.rootPath, c.cacheListener) + go c.listener.ListenServiceEvent(url, c.rootPath, c.cacheListener) return tc, c, err } @@ -143,11 +145,39 @@ func (c *zookeeperDynamicConfiguration) GetProperties(key string, opts ...config return string(content), nil } -//For zookeeper, getConfig and getConfigs have the same meaning. +// GetInternalProperty For zookeeper, getConfig and getConfigs have the same meaning. func (c *zookeeperDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) { return c.GetProperties(key, opts...) } +// PublishConfig will put the value into Zk with specific path +func (c *zookeeperDynamicConfiguration) PublishConfig(key string, group string, value string) error { + path := c.getPath(key, group) + err := c.client.CreateWithValue(path, []byte(value)) + if err != nil { + return perrors.WithStack(err) + } + return nil +} + +// GetConfigKeysByGroup will return all keys with the group +func (c *zookeeperDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + path := c.getPath("", group) + result, err := c.client.GetChildren(path) + if err != nil { + return nil, perrors.WithStack(err) + } + + if len(result) == 0 { + return nil, perrors.New("could not find keys with group: " + group) + } + set := gxset.NewSet() + for _, e := range result { + set.Add(e) + } + return set, nil +} + func (c *zookeeperDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { return c.GetProperties(key, opts...) } @@ -214,3 +244,17 @@ func (c *zookeeperDynamicConfiguration) closeConfigs() { func (c *zookeeperDynamicConfiguration) RestartCallBack() bool { return true } + +func (c *zookeeperDynamicConfiguration) getPath(key string, group string) string { + if len(key) == 0 { + return c.buildPath(group) + } + return c.buildPath(group) + pathSeparator + key +} + +func (c *zookeeperDynamicConfiguration) buildPath(group string) string { + if len(group) == 0 { + group = config_center.DEFAULT_GROUP + } + return c.rootPath + pathSeparator + group +} diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go index 22e15193cb..30389122a3 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -24,6 +24,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxset "github.com/dubbogo/gost/container/set" "github.com/stretchr/testify/assert" ) @@ -156,6 +157,26 @@ func Test_RemoveListener(t *testing.T) { assert.Equal(t, "", listener.event) } +func TestZookeeperDynamicConfiguration_PublishConfig(t *testing.T) { + value := "Test Data" + customGroup := "Custom Group" + key := "myKey" + ts, zk := initZkData(config_center.DEFAULT_GROUP, t) + defer ts.Stop() + err := zk.PublishConfig(key, customGroup, value) + assert.Nil(t, err) + result, err := zk.GetInternalProperty("myKey", config_center.WithGroup(customGroup)) + assert.Nil(t, err) + assert.Equal(t, value, result) + + var keys *gxset.HashSet + keys, err = zk.GetConfigKeysByGroup(customGroup) + assert.Nil(t, err) + assert.Equal(t, 1, keys.Size()) + assert.True(t, keys.Contains(key)) + +} + type mockDataListener struct { wg sync.WaitGroup event string diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go index 122dfaf4f2..747c4be352 100644 --- a/config_center/zookeeper/listener.go +++ b/config_center/zookeeper/listener.go @@ -33,12 +33,12 @@ type CacheListener struct { rootPath string } -// NewCacheListener ... +// NewCacheListener creates a new CacheListener func NewCacheListener(rootPath string) *CacheListener { return &CacheListener{rootPath: rootPath} } -// AddListener ... +// AddListener will add a listener if loaded func (l *CacheListener) AddListener(key string, listener config_center.ConfigurationListener) { // reference from https://stackoverflow.com/questions/34018908/golang-why-dont-we-have-a-set-datastructure @@ -50,7 +50,7 @@ func (l *CacheListener) AddListener(key string, listener config_center.Configura } } -// RemoveListener ... +// RemoveListener will delete a listener if loaded func (l *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) { listeners, loaded := l.keyListeners.Load(key) if loaded { @@ -58,7 +58,7 @@ func (l *CacheListener) RemoveListener(key string, listener config_center.Config } } -// DataChange ... +// DataChange changes all listeners' event func (l *CacheListener) DataChange(event remoting.Event) bool { if event.Content == "" { //meanings new node diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go index fbfe756517..49cdc2287c 100644 --- a/filter/filter_impl/access_log_filter.go +++ b/filter/filter_impl/access_log_filter.go @@ -71,14 +71,18 @@ func init() { * * the value of "accesslog" can be "true" or "default" too. * If the value is one of them, the access log will be record in log file which defined in log.yml + * AccessLogFilter is designed to be singleton */ type AccessLogFilter struct { logChan chan AccessLogData } -// Invoke ... +// Invoke will check whether user wants to use this filter. +// If we find the value of key constant.ACCESS_LOG_KEY, we will log the invocation info func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { accessLog := invoker.GetUrl().GetParam(constant.ACCESS_LOG_KEY, "") + + // the user do not if len(accessLog) > 0 { accessLogData := AccessLogData{data: ef.buildAccessLogData(invoker, invocation), accessLog: accessLog} ef.logIntoChannel(accessLogData) @@ -86,7 +90,7 @@ func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, return invoker.Invoke(ctx, invocation) } -// it won't block the invocation +// logIntoChannel won't block the invocation func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { select { case ef.logChan <- accessLogData: @@ -97,6 +101,7 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { } } +// buildAccessLogData builds the access log data func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string { dataMap := make(map[string]string, 16) attachments := invocation.Attachments() @@ -130,11 +135,12 @@ func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation pro return dataMap } -// OnResponse ... +// OnResponse do nothing func (ef *AccessLogFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } +// writeLogToFile actually write the logs into file func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) { accessLog := data.accessLog if isDefault(accessLog) { @@ -156,6 +162,12 @@ func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) { } } +// openLogFile will open the log file with append mode. +// If the file is not found, it will create the file. +// Actually, the accessLog is the filename +// You may find out that, once we want to write access log into log file, +// we open the file again and again. +// It needs to be optimized. func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { logFile, err := os.OpenFile(accessLog, os.O_CREATE|os.O_APPEND|os.O_RDWR, LogFileMode) if err != nil { @@ -169,6 +181,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { return nil, err } last := fileInfo.ModTime().Format(FileDateFormat) + + // this is confused. + // for example, if the last = '2020-03-04' + // and today is '2020-03-05' + // we will create one new file to log access data + // By this way, we can split the access log based on days. if now != last { err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now) if err != nil { @@ -180,11 +198,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { return logFile, err } +// isDefault check whether accessLog == true or accessLog == default func isDefault(accessLog string) bool { return strings.EqualFold("true", accessLog) || strings.EqualFold("default", accessLog) } -// GetAccessLogFilter ... +// GetAccessLogFilter return the instance of AccessLogFilter func GetAccessLogFilter() filter.Filter { accessLogFilter := &AccessLogFilter{logChan: make(chan AccessLogData, LogMaxBuffer)} go func() { @@ -195,12 +214,13 @@ func GetAccessLogFilter() filter.Filter { return accessLogFilter } -// AccessLogData ... +// AccessLogData defines the data that will be log into file type AccessLogData struct { accessLog string data map[string]string } +// toLogMessage convert the AccessLogData to String func (ef *AccessLogData) toLogMessage() string { builder := strings.Builder{} builder.WriteString("[") diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go index 37c6af7450..2a911659f0 100644 --- a/filter/filter_impl/generic_service_filter_test.go +++ b/filter/filter_impl/generic_service_filter_test.go @@ -96,7 +96,7 @@ func TestGenericServiceFilter_Invoke(t *testing.T) { hessian.Object("222")}, } s := &TestService{} - _, _ = common.ServiceMap.Register("testprotocol", s) + _, _ = common.ServiceMap.Register("TestService", "testprotocol", s) rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) filter := GetGenericServiceFilter() url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path") diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go index 9fd97b57b6..4c872bed3e 100644 --- a/filter/filter_impl/hystrix_filter.go +++ b/filter/filter_impl/hystrix_filter.go @@ -124,7 +124,7 @@ func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i _, _, err := hystrix.GetCircuit(cmdName) configLoadMutex.RUnlock() if err != nil { - logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: ", cmdName, err) + logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: %+v", cmdName, err) return invoker.Invoke(ctx, invocation) } logger.Infof("[Hystrix Filter]Using hystrix filter: %s", cmdName) diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go index d7a7f20a5d..b8b297e672 100644 --- a/filter/filter_impl/token_filter_test.go +++ b/filter/filter_impl/token_filter_test.go @@ -53,10 +53,10 @@ func TestTokenFilter_Invoke(t *testing.T) { func TestTokenFilter_InvokeEmptyToken(t *testing.T) { filter := GetTokenFilter() - url := common.URL{} + testUrl := common.URL{} attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "ori_key" - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -64,23 +64,23 @@ func TestTokenFilter_InvokeEmptyToken(t *testing.T) { func TestTokenFilter_InvokeEmptyAttach(t *testing.T) { filter := GetTokenFilter() - url := common.NewURLWithOptions( + testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } func TestTokenFilter_InvokeNotEqual(t *testing.T) { filter := GetTokenFilter() - url := common.NewURLWithOptions( + testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "err_key" result := filter.Invoke(context.Background(), - protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } diff --git a/filter/filter_impl/tps/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go index 7fe8de9237..2d44c688eb 100644 --- a/filter/filter_impl/tps/tps_limiter_method_service.go +++ b/filter/filter_impl/tps/tps_limiter_method_service.go @@ -115,7 +115,12 @@ type MethodServiceTpsLimiterImpl struct { tpsState *concurrent.Map } -// IsAllowable ... +// IsAllowable based on method-level and service-level. +// The method-level has high priority which means that if there is any rate limit configuration for the method, +// the service-level rate limit strategy will be ignored. +// The key point is how to keep thread-safe +// This implementation use concurrent map + loadOrStore to make implementation thread-safe +// You can image that even multiple threads create limiter, but only one could store the limiter into tpsState func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool { methodConfigPrefix := "methods." + invocation.MethodName() + "." @@ -123,23 +128,30 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio methodLimitRateConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_RATE_KEY, "") methodIntervalConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_INTERVAL_KEY, "") + // service-level tps limit limitTarget := url.ServiceKey() // method-level tps limit if len(methodIntervalConfig) > 0 || len(methodLimitRateConfig) > 0 { + // it means that if the method-level rate limit exist, we will use method-level rate limit strategy limitTarget = limitTarget + "#" + invocation.MethodName() } + // looking up the limiter from 'cache' limitState, found := limiter.tpsState.Load(limitTarget) if found { + // the limiter has been cached, we return its result return limitState.(filter.TpsLimitStrategy).IsAllowable() } + // we could not find the limiter, and try to create one. + limitRate := getLimitConfig(methodLimitRateConfig, url, invocation, constant.TPS_LIMIT_RATE_KEY, constant.DEFAULT_TPS_LIMIT_RATE) if limitRate < 0 { + // the limitTarget is not necessary to be limited. return true } @@ -150,13 +162,20 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio panic(fmt.Sprintf("The interval must be positive, please check your configuration! url: %s", url.String())) } + // find the strategy config and then create one limitStrategyConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_STRATEGY_KEY, url.GetParam(constant.TPS_LIMIT_STRATEGY_KEY, constant.DEFAULT_KEY)) limitStateCreator := extension.GetTpsLimitStrategyCreator(limitStrategyConfig) + + // we using loadOrStore to ensure thread-safe limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator.Create(int(limitRate), int(limitInterval))) + return limitState.(filter.TpsLimitStrategy).IsAllowable() } +// getLimitConfig will try to fetch the configuration from url. +// If we can convert the methodLevelConfig to int64, return; +// Or, we will try to look up server-level configuration and then convert it to int64 func getLimitConfig(methodLevelConfig string, url common.URL, invocation protocol.Invocation, @@ -172,6 +191,8 @@ func getLimitConfig(methodLevelConfig string, return result } + // actually there is no method-level configuration, so we use the service-level configuration + result, err := strconv.ParseInt(url.GetParam(configKey, defaultVal), 0, 0) if err != nil { @@ -183,7 +204,7 @@ func getLimitConfig(methodLevelConfig string, var methodServiceTpsLimiterInstance *MethodServiceTpsLimiterImpl var methodServiceTpsLimiterOnce sync.Once -// GetMethodServiceTpsLimiter ... +// GetMethodServiceTpsLimiter will return an MethodServiceTpsLimiterImpl instance. func GetMethodServiceTpsLimiter() filter.TpsLimiter { methodServiceTpsLimiterOnce.Do(func() { methodServiceTpsLimiterInstance = &MethodServiceTpsLimiterImpl{ diff --git a/filter/handler/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go index 0f9003c7df..fe9cf4869f 100644 --- a/filter/handler/rejected_execution_handler_only_log.go +++ b/filter/handler/rejected_execution_handler_only_log.go @@ -36,6 +36,7 @@ const ( ) func init() { + // this implementation is the the default implementation of RejectedExecutionHandler extension.SetRejectedExecutionHandler(HandlerName, GetOnlyLogRejectedExecutionHandler) extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, GetOnlyLogRejectedExecutionHandler) } @@ -56,11 +57,12 @@ var onlyLogHandlerOnce sync.Once * tps.limit.rejected.handler: "default" or "log" * methods: * - name: "GetUser" + * OnlyLogRejectedExecutionHandler is designed to be singleton */ type OnlyLogRejectedExecutionHandler struct { } -// RejectedExecution ... +// RejectedExecution will do nothing, it only log the invocation. func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, _ protocol.Invocation) protocol.Result { @@ -68,7 +70,7 @@ func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL return &protocol.RPCResult{} } -// GetOnlyLogRejectedExecutionHandler ... +// GetOnlyLogRejectedExecutionHandler will return the instance of OnlyLogRejectedExecutionHandler func GetOnlyLogRejectedExecutionHandler() filter.RejectedExecutionHandler { onlyLogHandlerOnce.Do(func() { onlyLogHandlerInstance = &OnlyLogRejectedExecutionHandler{} diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go index caeea1db66..d02481b98d 100644 --- a/filter/rejected_execution_handler.go +++ b/filter/rejected_execution_handler.go @@ -31,5 +31,7 @@ import ( * In such situation, implement this interface and register it by invoking extension.SetRejectedExecutionHandler. */ type RejectedExecutionHandler interface { + + // RejectedExecution will be called if the invocation was rejected by some component. RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result } diff --git a/filter/tps_limit_strategy.go b/filter/tps_limit_strategy.go index 5edf32ce19..e194f1da06 100644 --- a/filter/tps_limit_strategy.go +++ b/filter/tps_limit_strategy.go @@ -33,10 +33,16 @@ package filter * tps.limit.strategy: "name of implementation" # method-level */ type TpsLimitStrategy interface { + // IsAllowable will return true if this invocation is not over limitation IsAllowable() bool } -// TpsLimitStrategyCreator ... +// TpsLimitStrategyCreator, the creator abstraction for TpsLimitStrategy type TpsLimitStrategyCreator interface { - Create(rate int, interval int) TpsLimitStrategy + // Create will create an instance of TpsLimitStrategy + // It will be a little hard to understand this method. + // The unit of interval is ms + // for example, if the limit = 100, interval = 1000 + // which means that the tps limitation is 100 times per 1000ms (100/1000ms) + Create(limit int, interval int) TpsLimitStrategy } diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go index dbc9f76838..531eb09823 100644 --- a/filter/tps_limiter.go +++ b/filter/tps_limiter.go @@ -34,5 +34,6 @@ import ( * tps.limiter: "the name of limiter", */ type TpsLimiter interface { + // IsAllowable will check whether this invocation should be enabled for further process IsAllowable(common.URL, protocol.Invocation) bool } diff --git a/go.mod b/go.mod index 67ed1eb402..887df01aa2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/Workiva/go-datastructures v1.0.50 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect - github.com/apache/dubbo-go-hessian2 v1.4.0 + github.com/apache/dubbo-go-hessian2 v1.5.0 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible @@ -12,9 +12,9 @@ require ( github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/creasty/defaults v1.3.0 - github.com/dubbogo/getty v1.3.3 + github.com/dubbogo/getty v1.3.5 github.com/dubbogo/go-zookeeper v1.0.0 - github.com/dubbogo/gost v1.8.0 + github.com/dubbogo/gost v1.9.0 github.com/emicklei/go-restful/v3 v3.0.0 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/go-errors/errors v1.0.1 // indirect @@ -32,13 +32,15 @@ require ( github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/jonboulle/clockwork v0.1.0 // indirect + github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect + github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect github.com/magiconair/properties v1.8.1 github.com/mitchellh/mapstructure v1.1.2 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd - github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb + github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c github.com/opentracing/opentracing-go v1.1.0 github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.1.0 @@ -51,8 +53,7 @@ require ( github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 - go.etcd.io/bbolt v1.3.3 // indirect - go.etcd.io/etcd v3.3.13+incompatible + go.etcd.io/bbolt v1.3.4 // indirect go.uber.org/atomic v1.4.0 go.uber.org/zap v1.10.0 google.golang.org/grpc v1.22.1 diff --git a/go.sum b/go.sum index bbbaf34640..92ab16c434 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e h1:MSuLXx/mveDbpDNhVrcWTMeV4lbYWKcyO4rH+jAxmX0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/apache/dubbo-go-hessian2 v1.4.0 h1:Cb9FQVTy3G93dnDr7P93U8DeKFYpDTJjQp44JG5TafA= -github.com/apache/dubbo-go-hessian2 v1.4.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= +github.com/apache/dubbo-go-hessian2 v1.5.0 h1:fzulDG5G7nX0ccgKdiN9XipJ7tZ4WXKgmk4stdlDS6s= +github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -106,15 +106,13 @@ github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dubbogo/getty v1.3.3 h1:8m4zZBqFHO+NmhH7rMPlFuuYRVjcPD7cUhumevqMZZs= -github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvFTSPp0= +github.com/dubbogo/getty v1.3.5 h1:xJxdDj9jm7wlrRSsVZSk2TDNxJbbac5GpxV0QpjO+Tw= +github.com/dubbogo/getty v1.3.5/go.mod h1:T55vN8Q6tZjf2AQZiGmkujneD3LfqYbv2b3QjacwYOY= github.com/dubbogo/go-zookeeper v1.0.0 h1:RsYdlGwhDW+iKXM3eIIcvt34P2swLdmQfuIJxsHlGoM= github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8= -github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.8.0 h1:9ACbQe5OwMjqtinQcNJC5xp16kky27OsfSGw5L9A6vw= -github.com/dubbogo/gost v1.8.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y= @@ -324,6 +322,12 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 h1:hJix6idebFclqlfZCHE7EUX7uqLCyb70nHNHH1XKGBg= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt3aWYjoIsUGCbt21ekbeJcTWv0= +github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo= github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= @@ -383,8 +387,8 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb h1:lbmvw8r9W55w+aQgWn35W1nuleRIECMoqUrmwAOAvoI= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= +github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c h1:WoCa3AvgQMVKNs+RIFlWPRgY9QVJwUxJDrGxHs0fcRo= +github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/oklog/run v0.0.0-20180308005104-6934b124db28 h1:Hbr3fbVPXea52oPQeP7KLSxP52g6SFaNY1IqAmUyEW0= @@ -498,10 +502,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 h1:k8TV7Gz7cpWpOw/dz71fx8cCZdWoPuckHJ/wkJl+meg= github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw= -go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -548,6 +550,8 @@ golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= diff --git a/integrate_test.sh b/integrate_test.sh new file mode 100644 index 0000000000..c9c2f23b5b --- /dev/null +++ b/integrate_test.sh @@ -0,0 +1,66 @@ +# +# 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. + +#!/bin/bash + +set -e +set -x + +echo 'start integrate-test' + +# set root workspace +ROOT_DIR=$(pwd) +echo "integrate-test root work-space -> ${ROOT_DIR}" + +# show all travis-env +echo "travis current commit id -> ${TRAVIS_COMMIT}" +echo "travis pull request -> ${TRAVIS_PULL_REQUEST}" +echo "travis pull request branch -> ${TRAVIS_PULL_REQUEST_BRANCH}" +echo "travis pull request slug -> ${TRAVIS_PULL_REQUEST_SLUG}" +echo "travis pull request sha -> ${TRAVIS_PULL_REQUEST_SHA}" +echo "travis pull request repo slug -> ${TRAVIS_REPO_SLUG}" + + +# #start etcd registry insecure listen in [:]:2379 +# docker run -d --network host k8s.gcr.io/etcd:3.3.10 etcd +# echo "etcdv3 listen in [:]2379" + +# #start consul registry insecure listen in [:]:8500 +# docker run -d --network host consul +# echo "consul listen in [:]8500" + +# #start nacos registry insecure listen in [:]:8848 +# docker run -d --network host nacos/nacos-server:latest +# echo "ncacos listen in [:]8848" + +# default use zk as registry +#start zookeeper registry insecure listen in [:]:2181 +docker run -d --network host zookeeper +echo "zookeeper listen in [:]2181" + +# build go-server image +cd ./test/integrate/dubbo/go-server +docker build . -t ci-provider --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +cd ${ROOT_DIR} +docker run -d --network host ci-provider + +# build go-client image +cd ./test/integrate/dubbo/go-client +docker build . -t ci-consumer --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +cd ${ROOT_DIR} +# run provider +# check consumer status +docker run -it --network host ci-consumer diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go new file mode 100644 index 0000000000..ead984345e --- /dev/null +++ b/metadata/definition/definition.go @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package definition + +type ServiceDefinition struct { + CanonicalName string + CodeSource string + Methods []MethodDefinition + Types []TypeDefinition +} + +type MethodDefinition struct { + Name string + ParameterTypes []string + ReturnType string + Parameters []TypeDefinition +} + +type TypeDefinition struct { + Id string + Type string + Items []TypeDefinition + Enums []string + Properties map[string]TypeDefinition + TypeBuilderName string +} diff --git a/metadata/exporter.go b/metadata/exporter.go new file mode 100644 index 0000000000..5d47f8bd80 --- /dev/null +++ b/metadata/exporter.go @@ -0,0 +1,29 @@ +/* + * 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 metadata + +import ( + "github.com/apache/dubbo-go/common" +) + +type MetadataExporter interface { + Export() MetadataExporter + Unexport() MetadataExporter + GetExportedURLs() []*common.URL + IsExported() bool +} diff --git a/metadata/identifier/base_metadata_identifier.go b/metadata/identifier/base_metadata_identifier.go new file mode 100644 index 0000000000..a314671055 --- /dev/null +++ b/metadata/identifier/base_metadata_identifier.go @@ -0,0 +1,92 @@ +/* + * 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 identifier + +import ( + "encoding/base64" +) + +import ( + "github.com/apache/dubbo-go/common/constant" +) + +type BaseMetadataIdentifier interface { + getFilePathKey(params ...string) string + getIdentifierKey(params ...string) string +} + +type BaseServiceMetadataIdentifier struct { + serviceInterface string + version string + group string + side string +} + +// joinParams... +func joinParams(joinChar string, params []string) string { + var joinedStr string + for _, param := range params { + joinedStr += joinChar + joinedStr += param + } + return joinedStr +} + +// getIdentifierKey... +func (mdi *BaseServiceMetadataIdentifier) getIdentifierKey(params ...string) string { + return mdi.serviceInterface + + constant.KEY_SEPARATOR + mdi.version + + constant.KEY_SEPARATOR + mdi.group + + constant.KEY_SEPARATOR + mdi.side + + joinParams(constant.KEY_SEPARATOR, params) +} + +// getFilePathKey... +func (mdi *BaseServiceMetadataIdentifier) getFilePathKey(params ...string) string { + path := serviceToPath(mdi.serviceInterface) + + return constant.DEFAULT_PATH_TAG + + withPathSeparator(path) + + withPathSeparator(mdi.version) + + withPathSeparator(mdi.group) + + withPathSeparator(mdi.side) + + joinParams(constant.PATH_SEPARATOR, params) + +} + +// serviceToPath... +func serviceToPath(serviceInterface string) string { + if serviceInterface == constant.ANY_VALUE { + return "" + } else { + decoded, err := base64.URLEncoding.DecodeString(serviceInterface) + if err != nil { + return "" + } + return string(decoded) + } + +} + +//withPathSeparator... +func withPathSeparator(path string) string { + if len(path) != 0 { + path = constant.PATH_SEPARATOR + path + } + return path +} diff --git a/metadata/identifier/metadata_identifier.go b/metadata/identifier/metadata_identifier.go new file mode 100644 index 0000000000..f3df8f3654 --- /dev/null +++ b/metadata/identifier/metadata_identifier.go @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package identifier + +type MetadataIdentifier struct { + application string + BaseMetadataIdentifier +} + +// getIdentifierKey... +func (mdi *MetadataIdentifier) getIdentifierKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.application) +} + +// getIdentifierKey... +func (mdi *MetadataIdentifier) getFilePathKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.application) +} diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go new file mode 100644 index 0000000000..373df0130d --- /dev/null +++ b/metadata/identifier/service_metadata_identifier.go @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package identifier + +import ( + "github.com/apache/dubbo-go/common/constant" +) + +type ServiceMetadataIdentifier struct { + revision string + protocol string + BaseMetadataIdentifier +} + +// getIdentifierKey... +func (mdi *ServiceMetadataIdentifier) getIdentifierKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) +} + +// getIdentifierKey... +func (mdi *ServiceMetadataIdentifier) getFilePathKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) +} diff --git a/metadata/identifier/subscribe_metadata_identifier.go b/metadata/identifier/subscribe_metadata_identifier.go new file mode 100644 index 0000000000..321a216a3e --- /dev/null +++ b/metadata/identifier/subscribe_metadata_identifier.go @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package identifier + +type SubscriberMetadataIdentifier struct { + revision string + BaseMetadataIdentifier +} + +// getIdentifierKey... +func (mdi *SubscriberMetadataIdentifier) getIdentifierKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.revision) +} + +// getIdentifierKey... +func (mdi *SubscriberMetadataIdentifier) getFilePathKey(params ...string) string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.revision) +} diff --git a/metadata/namemapping/dynamic/service_name_mapping.go b/metadata/namemapping/dynamic/service_name_mapping.go new file mode 100644 index 0000000000..e93c256fe0 --- /dev/null +++ b/metadata/namemapping/dynamic/service_name_mapping.go @@ -0,0 +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 dynamic + +import ( + "strconv" + "time" +) + +import ( + "github.com/dubbogo/gost/container/set" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/metadata" +) + +const ( + defaultGroup = config_center.DEFAULT_GROUP + slash = "/" +) + +// DynamicConfigurationServiceNameMapping is the implementation based on config center +type DynamicConfigurationServiceNameMapping struct { + dc config_center.DynamicConfiguration +} + +// Map will map the service to this application-level service +func (d *DynamicConfigurationServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + // metadata service is admin service, should not be mapped + if constant.METADATA_SERVICE_NAME == serviceInterface { + return perrors.New("try to map the metadata service, will be ignored") + } + + appName := config.GetApplicationConfig().Name + value := time.Now().UnixNano() + + err := d.dc.PublishConfig(appName, + d.buildGroup(serviceInterface), + strconv.FormatInt(value, 10)) + if err != nil { + return perrors.WithStack(err) + } + return nil +} + +// Get will return the application-level services. If not found, the empty set will be returned. +// if the dynamic configuration got error, the error will return +func (d *DynamicConfigurationServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + return d.dc.GetConfigKeysByGroup(d.buildGroup(serviceInterface)) +} + +// buildGroup will return group, now it looks like defaultGroup/serviceInterface +func (d *DynamicConfigurationServiceNameMapping) buildGroup(serviceInterface string) string { + // the issue : https://github.com/apache/dubbo/issues/4671 + // so other params are ignored and remove, including group string, version string, protocol string + return defaultGroup + slash + serviceInterface +} + +// NewServiceNameMapping will create an instance of DynamicConfigurationServiceNameMapping +func NewServiceNameMapping(dc config_center.DynamicConfiguration) metadata.ServiceNameMapping { + return &DynamicConfigurationServiceNameMapping{dc: dc} +} diff --git a/metadata/namemapping/dynamic/service_name_mapping_test.go b/metadata/namemapping/dynamic/service_name_mapping_test.go new file mode 100644 index 0000000000..e3d620cd73 --- /dev/null +++ b/metadata/namemapping/dynamic/service_name_mapping_test.go @@ -0,0 +1,61 @@ +/* + * 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 dynamic + +import ( + "testing" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/config_center" +) + +func TestDynamicConfigurationServiceNameMapping(t *testing.T) { + + // mock data + appName := "myApp" + dc, err := (&config_center.MockDynamicConfigurationFactory{ + Content: appName, + }).GetDynamicConfiguration(nil) + config.GetApplicationConfig().Name = appName + + mapping := NewServiceNameMapping(dc) + intf := constant.METADATA_SERVICE_NAME + group := "myGroup" + version := "myVersion" + protocol := "myProtocol" + + err = mapping.Map(intf, group, version, protocol) + assert.NotNil(t, err) + intf = "MyService" + err = mapping.Map(intf, group, version, protocol) + assert.Nil(t, err) + + var result *gxset.HashSet + result, err = mapping.Get(intf, group, version, protocol) + assert.Nil(t, err) + assert.Equal(t, 1, result.Size()) + assert.True(t, result.Contains(appName)) +} diff --git a/metadata/namemapping/memory/service_name_mapping.go b/metadata/namemapping/memory/service_name_mapping.go new file mode 100644 index 0000000000..8a891491bd --- /dev/null +++ b/metadata/namemapping/memory/service_name_mapping.go @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package memory + +import ( + gxset "github.com/dubbogo/gost/container/set" +) + +import ( + "github.com/apache/dubbo-go/config" +) + +type InMemoryServiceNameMapping struct{} + +func (i InMemoryServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + return nil +} + +func (i InMemoryServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + return gxset.NewSet(config.GetApplicationConfig().Name), nil +} diff --git a/metadata/report.go b/metadata/report.go new file mode 100644 index 0000000000..3fcc712414 --- /dev/null +++ b/metadata/report.go @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package metadata + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/metadata/definition" + "github.com/apache/dubbo-go/metadata/identifier" +) + +type MetadataReport interface { + StoreProviderMetadata(*identifier.MetadataIdentifier, *definition.ServiceDefinition) + StoreConsumeretadata(*identifier.MetadataIdentifier, map[string]string) + SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) + RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) + GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string + SaveSubscribedData(*identifier.SubscriberMetadataIdentifier, []*common.URL) + GetSubscribedURLs(*identifier.SubscriberMetadataIdentifier) []string + GetServiceDefinition(*identifier.MetadataIdentifier) +} diff --git a/metadata/report_factory.go b/metadata/report_factory.go new file mode 100644 index 0000000000..19b1004eee --- /dev/null +++ b/metadata/report_factory.go @@ -0,0 +1,30 @@ +/* + * 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 metadata + +import ( + "github.com/apache/dubbo-go/common" +) + +var ( + MetadataReportInstance MetadataReport +) + +type MetadataReportFactory interface { + CreateMetadataReport(*common.URL) MetadataReport +} diff --git a/metadata/service.go b/metadata/service.go new file mode 100644 index 0000000000..d85703c95a --- /dev/null +++ b/metadata/service.go @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package metadata + +import ( + "github.com/apache/dubbo-go/common" + gxset "github.com/dubbogo/gost/container/set" +) + +type MetadataService interface { + ServiceName() string + ExportURL(url *common.URL) bool + UnexportURL(url *common.URL) bool + RefreshMetadata(exportedRevision string, subscribedRevision string) bool + SubscribeURL(url *common.URL) bool + UnsubscribeURL(url *common.URL) bool + PublishServiceDefinition(url *common.URL) + + GetExportedURLs(serviceInterface string, group string, version string, protocol string) gxset.HashSet + GetServiceDefinition(interfaceName string, version string, group string) string + GetServiceDefinitionByServiceKey(serviceKey string) string +} diff --git a/metadata/service_name_mapping.go b/metadata/service_name_mapping.go new file mode 100644 index 0000000000..c14e8ce2e7 --- /dev/null +++ b/metadata/service_name_mapping.go @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package metadata + +import ( + gxset "github.com/dubbogo/gost/container/set" +) + +// ServiceNameMapping try to build the mapping between application-level service and interface-level service. +type ServiceNameMapping interface { + + // Map will map the service to this application-level service + Map(serviceInterface string, group string, version string, protocol string) error + + // Get will return the application-level services + Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) +} diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go index 5ec7db51af..e6ffa64d80 100644 --- a/protocol/dubbo/client.go +++ b/protocol/dubbo/client.go @@ -88,7 +88,7 @@ func init() { rand.Seed(time.Now().UnixNano()) } -// SetClientConf ... +// SetClientConf set dubbo client config. func SetClientConf(c ClientConfig) { clientConf = &c err := clientConf.CheckValidity() @@ -99,7 +99,7 @@ func SetClientConf(c ClientConfig) { setClientGrpool() } -// GetClientConf ... +// GetClientConf get dubbo client config. func GetClientConf() ClientConfig { return *clientConf } @@ -129,7 +129,7 @@ type AsyncCallbackResponse struct { Reply interface{} } -// Client ... +// Client is dubbo protocol client. type Client struct { opts Options conf ClientConfig @@ -139,7 +139,7 @@ type Client struct { pendingResponses *sync.Map } -// NewClient ... +// NewClient create a new Client. func NewClient(opt Options) *Client { switch { @@ -167,7 +167,7 @@ func NewClient(opt Options) *Client { return c } -// Request ... +// Request is dubbo protocol request. type Request struct { addr string svcUrl common.URL @@ -176,7 +176,7 @@ type Request struct { atta map[string]string } -// NewRequest ... +// NewRequest create a new Request. func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request { return &Request{ addr: addr, @@ -187,13 +187,13 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, } } -// Response ... +// Response is dubbo protocol response. type Response struct { reply interface{} atta map[string]string } -// NewResponse ... +// NewResponse create a new Response. func NewResponse(reply interface{}, atta map[string]string) *Response { return &Response{ reply: reply, @@ -201,15 +201,14 @@ func NewResponse(reply interface{}, atta map[string]string) *Response { } } -// CallOneway call one way +// CallOneway call by one way func (c *Client) CallOneway(request *Request) error { return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) } -// Call if @response is nil, the transport layer will get the response without notify the invoker. +// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way. func (c *Client) Call(request *Request, response *Response) error { - ct := CT_TwoWay if response.reply == nil { ct = CT_OneWay @@ -218,14 +217,12 @@ func (c *Client) Call(request *Request, response *Response) error { return perrors.WithStack(c.call(ct, request, response, nil)) } -// AsyncCall ... +// AsyncCall call remoting by async with callback. func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error { - return perrors.WithStack(c.call(CT_TwoWay, request, response, callback)) } func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error { - p := &DubboPackage{} p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/") p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "") @@ -293,7 +290,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac return perrors.WithStack(err) } -// Close ... +// Close close the client pool. func (c *Client) Close() { if c.pool != nil { c.pool.close() diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go index 1e0a73fac1..744ffa80d6 100644 --- a/protocol/dubbo/client_test.go +++ b/protocol/dubbo/client_test.go @@ -162,7 +162,7 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) { hessian.RegisterPOJO(&User{}) - methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) + methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", &UserProvider{}) assert.NoError(t, err) assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go index 76416b2baf..620a57d4a7 100644 --- a/protocol/dubbo/codec.go +++ b/protocol/dubbo/codec.go @@ -69,7 +69,7 @@ func (p DubboPackage) String() string { return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) } -// Marshal ... +// Marshal encode hessian package. func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { codec := hessian.NewHessianCodec(nil) @@ -81,7 +81,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { return bytes.NewBuffer(pkg), nil } -// Unmarshal ... +// Unmarshal dncode hessian package. func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { // fix issue https://github.com/apache/dubbo-go/issues/380 bufLen := buf.Len() @@ -125,7 +125,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { // PendingResponse //////////////////////////////////////////// -// PendingResponse ... +// PendingResponse is a pending response. type PendingResponse struct { seq uint64 err error @@ -136,7 +136,7 @@ type PendingResponse struct { done chan struct{} } -// NewPendingResponse ... +// NewPendingResponse create a PendingResponses. func NewPendingResponse() *PendingResponse { return &PendingResponse{ start: time.Now(), @@ -145,7 +145,7 @@ func NewPendingResponse() *PendingResponse { } } -// GetCallResponse ... +// GetCallResponse get AsyncCallbackResponse. func (r PendingResponse) GetCallResponse() common.CallbackResponse { return AsyncCallbackResponse{ Cause: r.err, diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go index dbc6989c54..6a1daf857a 100644 --- a/protocol/dubbo/config.go +++ b/protocol/dubbo/config.go @@ -147,7 +147,7 @@ func GetDefaultServerConfig() ServerConfig { } } -// CheckValidity ... +// CheckValidity confirm getty sessian params. func (c *GettySessionParam) CheckValidity() error { var err error @@ -170,7 +170,7 @@ func (c *GettySessionParam) CheckValidity() error { return nil } -// CheckValidity ... +// CheckValidity confirm client params. func (c *ClientConfig) CheckValidity() error { var err error @@ -192,7 +192,7 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithStack(c.GettySessionParam.CheckValidity()) } -// CheckValidity ... +// CheckValidity confirm server params. func (c *ServerConfig) CheckValidity() error { var err error diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go index f4cd0cc123..dd80937c5b 100644 --- a/protocol/dubbo/dubbo_exporter.go +++ b/protocol/dubbo/dubbo_exporter.go @@ -28,23 +28,24 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// DubboExporter ... +// DubboExporter is dubbo service exporter. type DubboExporter struct { protocol.BaseExporter } -// NewDubboExporter ... +// NewDubboExporter get a DubboExporter. func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter { return &DubboExporter{ BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap), } } -// Unexport ... +// Unexport unexport dubbo service exporter. func (de *DubboExporter) Unexport() { serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") + interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") de.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(DUBBO, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, DUBBO, serviceId) if err != nil { logger.Errorf("[DubboExporter.Unexport] error: %v", err) } diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 09c3725710..59202d5f49 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -39,8 +39,9 @@ import ( ) var ( - // ErrNoReply ... - ErrNoReply = perrors.New("request need @response") + // ErrNoReply + ErrNoReply = perrors.New("request need @response") + // ErrDestroyedInvoker ErrDestroyedInvoker = perrors.New("request Destroyed invoker") ) @@ -48,7 +49,7 @@ var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} ) -// DubboInvoker ... +// DubboInvoker is dubbo client invoker. type DubboInvoker struct { protocol.BaseInvoker client *Client @@ -57,7 +58,7 @@ type DubboInvoker struct { reqNum int64 } -// NewDubboInvoker ... +// NewDubboInvoker create dubbo client invoker. func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { return &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), @@ -66,7 +67,7 @@ func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { } } -// Invoke ... +// Invoke call remoting. func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( err error @@ -122,7 +123,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati return &result } -// Destroy ... +// Destroy destroy dubbo client invoker. func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { for { diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 355dbc8024..b7d0a8a268 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -45,14 +45,14 @@ var ( dubboProtocol *DubboProtocol ) -// DubboProtocol ... +// DubboProtocol is a dubbo protocol implement. type DubboProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } -// NewDubboProtocol ... +// NewDubboProtocol create a dubbo protocol. func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -60,7 +60,7 @@ func NewDubboProtocol() *DubboProtocol { } } -// Export ... +// Export export dubbo service. func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -73,7 +73,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } -// Refer ... +// Refer create dubbo service reference. func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { //default requestTimeout var requestTimeout = config.GetConsumerConfig().RequestTimeout @@ -92,7 +92,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { return invoker } -// Destroy ... +// Destroy destroy dubbo service. func (dp *DubboProtocol) Destroy() { logger.Infof("DubboProtocol destroy.") @@ -124,7 +124,7 @@ func (dp *DubboProtocol) openServer(url common.URL) { } } -// GetProtocol ... +// GetProtocol get a single dubbo protocol. func GetProtocol() protocol.Protocol { if dubboProtocol == nil { dubboProtocol = NewDubboProtocol() diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go index 0251b78a2b..f57d89d1a7 100644 --- a/protocol/dubbo/listener.go +++ b/protocol/dubbo/listener.go @@ -86,7 +86,7 @@ func (h *RpcClientHandler) OnOpen(session getty.Session) error { // OnError ... func (h *RpcClientHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.conn.removeSession(session) } @@ -143,7 +143,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { // OnCron ... func (h *RpcClientHandler) OnCron(session getty.Session) { - rpcSession, err := h.conn.getClientRpcSession(session) + clientRpcSession, err := h.conn.getClientRpcSession(session) if err != nil { logger.Errorf("client.getClientSession(session{%s}) = error{%v}", session.Stat(), perrors.WithStack(err)) @@ -151,7 +151,7 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { } if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) + session.Stat(), time.Since(session.GetActive()).String(), clientRpcSession.reqNum) h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) return } @@ -201,7 +201,7 @@ func (h *RpcServerHandler) OnOpen(session getty.Session) error { // OnError ... func (h *RpcServerHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.rwlock.Lock() delete(h.sessionMap, session) h.rwlock.Unlock() diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go index 918514c267..f0bd09ba7c 100644 --- a/protocol/dubbo/pool.go +++ b/protocol/dubbo/pool.go @@ -219,25 +219,25 @@ func (c *gettyRPCClient) updateSession(session getty.Session) { func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { var ( - err error - rpcSession rpcSession + err error + rpcClientSession rpcSession ) c.lock.RLock() defer c.lock.RUnlock() if c.sessions == nil { - return rpcSession, errClientClosed + return rpcClientSession, errClientClosed } err = errSessionNotExist for _, s := range c.sessions { if s.session == session { - rpcSession = *s + rpcClientSession = *s err = nil break } } - return rpcSession, perrors.WithStack(err) + return rpcClientSession, perrors.WithStack(err) } func (c *gettyRPCClient) isAvailable() bool { @@ -319,7 +319,8 @@ func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPC conn, err := p.get() if err == nil && conn == nil { // create new conn - rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr) + var rpcClientConn *gettyRPCClient + rpcClientConn, err = newGettyRPCClientConn(p, protocol, addr) return rpcClientConn, perrors.WithStack(err) } return conn, perrors.WithStack(err) diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go index b5c4f50919..9cc7ea25cd 100644 --- a/protocol/dubbo/readwriter.go +++ b/protocol/dubbo/readwriter.go @@ -38,16 +38,17 @@ import ( // RpcClientPackageHandler //////////////////////////////////////////// -// RpcClientPackageHandler ... +// RpcClientPackageHandler handle package for client in getty. type RpcClientPackageHandler struct { client *Client } -// NewRpcClientPackageHandler ... +// NewRpcClientPackageHandler create a RpcClientPackageHandler. func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { return &RpcClientPackageHandler{client: client} } +// Read decode @data to DubboPackage. func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { pkg := &DubboPackage{} @@ -72,6 +73,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } +// Write encode @pkg. func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { req, ok := pkg.(*DubboPackage) if !ok { @@ -96,9 +98,10 @@ var ( rpcServerPkgHandler = &RpcServerPackageHandler{} ) -// RpcServerPackageHandler ... +// RpcServerPackageHandler handle package for server in getty. type RpcServerPackageHandler struct{} +// Read decode @data to DubboPackage. func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { pkg := &DubboPackage{ Body: make([]interface{}, 7), @@ -169,6 +172,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } +// Write encode @pkg. func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { res, ok := pkg.(*DubboPackage) if !ok { diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go index bd2b37b7a9..8de353a0b3 100644 --- a/protocol/dubbo/server.go +++ b/protocol/dubbo/server.go @@ -71,10 +71,10 @@ func init() { if err := srvConf.CheckValidity(); err != nil { panic(err) } - SetServerGrpool() + setServerGrpool() } -// SetServerConfig ... +// SetServerConfig set dubbo server config. func SetServerConfig(s ServerConfig) { srvConf = &s err := srvConf.CheckValidity() @@ -82,30 +82,29 @@ func SetServerConfig(s ServerConfig) { logger.Warnf("[ServerConfig CheckValidity] error: %v", err) return } - SetServerGrpool() + setServerGrpool() } -// GetServerConfig ... +// GetServerConfig get dubbo server config. func GetServerConfig() ServerConfig { return *srvConf } -// SetServerGrpool ... -func SetServerGrpool() { +func setServerGrpool() { if srvConf.GrPoolSize > 1 { srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) } } -// Server ... +// Server is dubbo protocol server. type Server struct { conf ServerConfig tcpServer getty.Server rpcHandler *RpcServerHandler } -// NewServer ... +// NewServer create a new Server. func NewServer() *Server { s := &Server{ @@ -156,7 +155,7 @@ func (s *Server) newSession(session getty.Session) error { return nil } -// Start ... +// Start start dubbo server. func (s *Server) Start(url common.URL) { var ( addr string @@ -173,7 +172,7 @@ func (s *Server) Start(url common.URL) { } -// Stop ... +// Stop stop dubbo server. func (s *Server) Stop() { s.tcpServer.Close() } diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go index 1acd2fec39..0296ad8b5b 100644 --- a/protocol/grpc/grpc_exporter.go +++ b/protocol/grpc/grpc_exporter.go @@ -43,8 +43,9 @@ func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map // Unexport ... func (gg *GrpcExporter) Unexport() { serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") + interfaceName := gg.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") gg.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(GRPC, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, GRPC, serviceId) if err != nil { logger.Errorf("[GrpcExporter.Unexport] error: %v", err) } diff --git a/protocol/invocation.go b/protocol/invocation.go index f32f2c3449..eedf5f0253 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -30,5 +30,8 @@ type Invocation interface { Reply() interface{} Attachments() map[string]string AttachmentsByKey(string, string) string + // Refer to dubbo 2.7.6. It is different from attachment. It is used in internal process. + Attributes() map[string]interface{} + AttributeByKey(string, interface{}) interface{} Invoker() Invoker } diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index b207fd0b0c..68fe7b9204 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -40,8 +40,10 @@ type RPCInvocation struct { reply interface{} callBack interface{} attachments map[string]string - invoker protocol.Invoker - lock sync.RWMutex + // Refer to dubbo 2.7.6. It is different from attachment. It is used in internal process. + attributes map[string]interface{} + invoker protocol.Invoker + lock sync.RWMutex } // NewRPCInvocation ... @@ -50,6 +52,7 @@ func NewRPCInvocation(methodName string, arguments []interface{}, attachments ma methodName: methodName, arguments: arguments, attachments: attachments, + attributes: make(map[string]interface{}, 8), } } @@ -59,6 +62,9 @@ func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { for _, opt := range opts { opt(invo) } + if invo.attributes == nil { + invo.attributes = make(map[string]interface{}) + } return invo } @@ -111,6 +117,22 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string return defaultValue } +// get attributes +func (r *RPCInvocation) Attributes() map[string]interface{} { + return r.attributes +} + +// get attribute by key. If it is not exist, it will return default value +func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) interface{} { + r.lock.RLock() + defer r.lock.RUnlock() + value, ok := r.attributes[key] + if ok { + return value + } + return defaultValue +} + // SetAttachments ... func (r *RPCInvocation) SetAttachments(key string, value string) { r.lock.Lock() @@ -121,6 +143,13 @@ func (r *RPCInvocation) SetAttachments(key string, value string) { r.attachments[key] = value } +// SetAttribute. If Attributes is nil, it will be inited. +func (r *RPCInvocation) SetAttribute(key string, value interface{}) { + r.lock.Lock() + defer r.lock.Unlock() + r.attributes[key] = value +} + // Invoker ... func (r *RPCInvocation) Invoker() protocol.Invoker { return r.invoker diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go index ba7197dbc8..70b3abd24f 100644 --- a/protocol/jsonrpc/http.go +++ b/protocol/jsonrpc/http.go @@ -172,7 +172,7 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) httpReq.Close = true reqBuf := bytes.NewBuffer(make([]byte, 0)) - if err := httpReq.Write(reqBuf); err != nil { + if err = httpReq.Write(reqBuf); err != nil { return nil, perrors.WithStack(err) } @@ -191,7 +191,7 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) } setNetConnTimeout(tcpConn, c.options.HTTPTimeout) - if _, err := reqBuf.WriteTo(tcpConn); err != nil { + if _, err = reqBuf.WriteTo(tcpConn); err != nil { return nil, perrors.WithStack(err) } diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index 0cb88b36a8..f8480bf32e 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -50,7 +50,7 @@ type ( func TestHTTPClient_Call(t *testing.T) { - methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{}) + methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", &UserProvider{}) assert.NoError(t, err) assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods) diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go index d1c2a858b4..3176e19381 100644 --- a/protocol/jsonrpc/json.go +++ b/protocol/jsonrpc/json.go @@ -67,8 +67,8 @@ type Error struct { func (e *Error) Error() string { buf, err := json.Marshal(e) if err != nil { - msg, err := json.Marshal(err.Error()) - if err != nil { + msg, retryErr := json.Marshal(err.Error()) + if retryErr != nil { msg = []byte("jsonrpc2.Error: json.Marshal failed") } return fmt.Sprintf(`{"code":%d,"message":%s}`, -32001, string(msg)) @@ -133,7 +133,7 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { } case reflect.Array, reflect.Struct: case reflect.Ptr: - switch k := reflect.TypeOf(param).Elem().Kind(); k { + switch ptrK := reflect.TypeOf(param).Elem().Kind(); ptrK { case reflect.Map: if reflect.TypeOf(param).Elem().Key().Kind() == reflect.String { if reflect.ValueOf(param).Elem().IsNil() { @@ -146,7 +146,7 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { } case reflect.Array, reflect.Struct: default: - return nil, perrors.New("unsupported param type: Ptr to " + k.String()) + return nil, perrors.New("unsupported param type: Ptr to " + ptrK.String()) } default: return nil, perrors.New("unsupported param type: " + k.String()) diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go index 7f8fd49185..c61cf9adae 100644 --- a/protocol/jsonrpc/jsonrpc_exporter.go +++ b/protocol/jsonrpc/jsonrpc_exporter.go @@ -43,8 +43,9 @@ func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync. // Unexport ... func (je *JsonrpcExporter) Unexport() { serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") + interfaceName := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") je.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(JSONRPC, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, serviceId) if err != nil { logger.Errorf("[JsonrpcExporter.Unexport] error: %v", err) } diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go index 9e08eed2b4..0f14ba11e2 100644 --- a/protocol/jsonrpc/jsonrpc_invoker_test.go +++ b/protocol/jsonrpc/jsonrpc_invoker_test.go @@ -36,7 +36,7 @@ import ( func TestJsonrpcInvoker_Invoke(t *testing.T) { - methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{}) + methods, err := common.ServiceMap.Register("UserProvider", "jsonrpc", &UserProvider{}) assert.NoError(t, err) assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods) diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index bed7099ab6..64f708652d 100644 --- a/protocol/jsonrpc/jsonrpc_protocol.go +++ b/protocol/jsonrpc/jsonrpc_protocol.go @@ -109,8 +109,8 @@ func (jp *JsonrpcProtocol) Destroy() { func (jp *JsonrpcProtocol) openServer(url common.URL) { _, ok := jp.serverMap[url.Location] if !ok { - _, ok := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/")) - if !ok { + _, loadOk := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/")) + if !loadOk { panic("[JsonrpcProtocol]" + url.Key() + "is not existing") } diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go index 8600f02dad..fcea66632e 100644 --- a/protocol/jsonrpc/server.go +++ b/protocol/jsonrpc/server.go @@ -349,9 +349,9 @@ func serveRequest(ctx context.Context, constant.PATH_KEY: path, constant.VERSION_KEY: codec.req.Version})) if err := result.Error(); err != nil { - rspStream, err := codec.Write(err.Error(), invalidRequest) - if err != nil { - return perrors.WithStack(err) + rspStream, codecErr := codec.Write(err.Error(), invalidRequest) + if codecErr != nil { + return perrors.WithStack(codecErr) } if errRsp := sendErrorResp(header, rspStream); errRsp != nil { logger.Warnf("Exporter: sendErrorResp(header:%#v, error:%v) = error:%s", diff --git a/protocol/rest/client/client_impl/resty_client.go b/protocol/rest/client/client_impl/resty_client.go index aa6c23137d..b60f50a5a7 100644 --- a/protocol/rest/client/client_impl/resty_client.go +++ b/protocol/rest/client/client_impl/resty_client.go @@ -40,10 +40,12 @@ func init() { extension.SetRestClient(constant.DEFAULT_REST_CLIENT, NewRestyClient) } +// RestyClient a rest client implement by Resty type RestyClient struct { client *resty.Client } +// NewRestyClient a constructor of RestyClient func NewRestyClient(restOption *client.RestOptions) client.RestClient { client := resty.New() client.SetTransport( @@ -65,21 +67,21 @@ func NewRestyClient(restOption *client.RestOptions) client.RestClient { } } -func (rc *RestyClient) Do(restRequest *client.RestRequest, res interface{}) error { - r, err := rc.client.R(). - SetHeader("Content-Type", restRequest.Consumes). - SetHeader("Accept", restRequest.Produces). +// Do send request by RestyClient +func (rc *RestyClient) Do(restRequest *client.RestClientRequest, res interface{}) error { + req := rc.client.R() + req.Header = restRequest.Header + resp, err := req. SetPathParams(restRequest.PathParams). SetQueryParams(restRequest.QueryParams). - SetHeaders(restRequest.Headers). SetBody(restRequest.Body). SetResult(res). Execute(restRequest.Method, "http://"+path.Join(restRequest.Location, restRequest.Path)) if err != nil { return perrors.WithStack(err) } - if r.IsError() { - return perrors.New(r.String()) + if resp.IsError() { + return perrors.New(resp.String()) } return nil } diff --git a/protocol/rest/client/rest_client.go b/protocol/rest/client/rest_client.go index 7d020abc81..d63c5e0bd0 100644 --- a/protocol/rest/client/rest_client.go +++ b/protocol/rest/client/rest_client.go @@ -18,26 +18,28 @@ package client import ( + "net/http" "time" ) +// RestOptions type RestOptions struct { RequestTimeout time.Duration ConnectTimeout time.Duration } -type RestRequest struct { +// RestClientRequest +type RestClientRequest struct { + Header http.Header Location string Path string - Produces string - Consumes string Method string PathParams map[string]string QueryParams map[string]string Body interface{} - Headers map[string]string } +// RestClient user can implement this client interface to send request type RestClient interface { - Do(request *RestRequest, res interface{}) error + Do(request *RestClientRequest, res interface{}) error } diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go index 470d525ad8..1ee208615e 100644 --- a/protocol/rest/rest_exporter.go +++ b/protocol/rest/rest_exporter.go @@ -40,8 +40,9 @@ func NewRestExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map func (re *RestExporter) Unexport() { serviceId := re.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") + interfaceName := re.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") re.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(REST, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, REST, serviceId) if err != nil { logger.Errorf("[RestExporter.Unexport] error: %v", err) } diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go index 0c82035ac5..121d1217ef 100644 --- a/protocol/rest/rest_invoker.go +++ b/protocol/rest/rest_invoker.go @@ -20,6 +20,7 @@ package rest import ( "context" "fmt" + "net/http" ) import ( @@ -56,7 +57,7 @@ func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio body interface{} pathParams map[string]string queryParams map[string]string - headers map[string]string + header http.Header err error ) if methodConfig == nil { @@ -71,24 +72,21 @@ func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio result.Err = err return &result } - if headers, err = restStringMapTransform(methodConfig.HeadersMap, inv.Arguments()); err != nil { + if header, err = getRestHttpHeader(methodConfig, inv.Arguments()); err != nil { result.Err = err return &result } if len(inv.Arguments()) > methodConfig.Body && methodConfig.Body >= 0 { body = inv.Arguments()[methodConfig.Body] } - - req := &client.RestRequest{ + req := &client.RestClientRequest{ Location: ri.GetUrl().Location, - Produces: methodConfig.Produces, - Consumes: methodConfig.Consumes, Method: methodConfig.MethodType, Path: methodConfig.Path, PathParams: pathParams, QueryParams: queryParams, Body: body, - Headers: headers, + Header: header, } result.Err = ri.client.Do(req, inv.Reply()) if result.Err == nil { @@ -107,3 +105,17 @@ func restStringMapTransform(paramsMap map[int]string, args []interface{}) (map[s } return resMap, nil } + +func getRestHttpHeader(methodConfig *config.RestMethodConfig, args []interface{}) (http.Header, error) { + header := http.Header{} + headersMap := methodConfig.HeadersMap + header.Set("Content-Type", methodConfig.Consumes) + header.Set("Accept", methodConfig.Produces) + for k, v := range headersMap { + if k >= len(args) || k < 0 { + return nil, perrors.Errorf("[Rest Invoke] Index %v is out of bundle", k) + } + header.Set(v, fmt.Sprint(args[k])) + } + return header, nil +} diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go index e44c5d9a21..2ea260c58d 100644 --- a/protocol/rest/rest_invoker_test.go +++ b/protocol/rest/rest_invoker_test.go @@ -61,7 +61,7 @@ func TestRestInvoker_Invoke(t *testing.T) { "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) - _, err = common.ServiceMap.Register(url.Protocol, &UserProvider{}) + _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) assert.NoError(t, err) con := config.ProviderConfig{} config.SetProviderConfig(con) @@ -206,6 +206,6 @@ func TestRestInvoker_Invoke(t *testing.T) { assert.Error(t, res.Error(), "test error") assert.Equal(t, filterNum, 12) - err = common.ServiceMap.UnRegister(url.Protocol, "com.ikurento.user.UserProvider") + err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider") assert.NoError(t, err) } diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go index 47ecb6093b..e15eeb39d7 100644 --- a/protocol/rest/rest_protocol.go +++ b/protocol/rest/rest_protocol.go @@ -75,7 +75,9 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter { } rp.SetExporterMap(serviceKey, exporter) restServer := rp.getServer(url, restServiceConfig.Server) - restServer.Deploy(invoker, restServiceConfig.RestMethodConfigsMap) + for _, methodConfig := range restServiceConfig.RestMethodConfigsMap { + restServer.Deploy(methodConfig, server.GetRouteFunc(invoker, methodConfig)) + } return exporter } diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go index 8af73a1839..9117148777 100644 --- a/protocol/rest/rest_protocol_test.go +++ b/protocol/rest/rest_protocol_test.go @@ -80,7 +80,7 @@ func TestRestProtocol_Export(t *testing.T) { "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) - _, err = common.ServiceMap.Register(url.Protocol, &UserProvider{}) + _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) assert.NoError(t, err) con := config.ProviderConfig{} config.SetProviderConfig(con) @@ -128,7 +128,7 @@ func TestRestProtocol_Export(t *testing.T) { proto.Destroy() _, ok = proto.(*RestProtocol).serverMap[url.Location] assert.False(t, ok) - err = common.ServiceMap.UnRegister(url.Protocol, "com.ikurento.user.UserProvider") + err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider") assert.NoError(t, err) } diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go index c10c98a7b6..fbd6fb7ad9 100644 --- a/protocol/rest/server/rest_server.go +++ b/protocol/rest/server/rest_server.go @@ -17,15 +17,306 @@ package server +import ( + "context" + "errors" + "net/http" + "reflect" + "strconv" + "strings" +) + +import ( + perrors "github.com/pkg/errors" +) + import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/rest/config" + "github.com/apache/dubbo-go/protocol/invocation" + rest_config "github.com/apache/dubbo-go/protocol/rest/config" ) +const parseParameterErrorStr = "An error occurred while parsing parameters on the server" + +// RestServer user can implement this server interface type RestServer interface { + // Start rest server Start(url common.URL) - Deploy(invoker protocol.Invoker, restMethodConfig map[string]*config.RestMethodConfig) - UnDeploy(restMethodConfig map[string]*config.RestMethodConfig) + // Deploy a http api + Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse)) + // UnDeploy a http api + UnDeploy(restMethodConfig *rest_config.RestMethodConfig) + // Destroy rest server Destroy() } + +// RestServerRequest interface +type RestServerRequest interface { + // RawRequest get the Ptr of http.Request + RawRequest() *http.Request + // PathParameter get the path parameter by name + PathParameter(name string) string + // PathParameters get the map of the path parameters + PathParameters() map[string]string + // QueryParameter get the query parameter by name + QueryParameter(name string) string + // QueryParameters get the map of query parameters + QueryParameters(name string) []string + // BodyParameter get the body parameter of name + BodyParameter(name string) (string, error) + // HeaderParameter get the header parameter of name + HeaderParameter(name string) string + // ReadEntity checks the Accept header and reads the content into the entityPointer. + ReadEntity(entityPointer interface{}) error +} + +// RestServerResponse interface +type RestServerResponse interface { + http.ResponseWriter + // WriteError writes the http status and the error string on the response. err can be nil. + // Return an error if writing was not successful. + WriteError(httpStatus int, err error) (writeErr error) + // WriteEntity marshals the value using the representation denoted by the Accept Header. + WriteEntity(value interface{}) error +} + +// GetRouteFunc +// A route function will be invoked by http server +func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethodConfig) func(req RestServerRequest, resp RestServerResponse) { + return func(req RestServerRequest, resp RestServerResponse) { + var ( + err error + args []interface{} + ) + svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/")) + // get method + method := svc.Method()[methodConfig.MethodName] + argsTypes := method.ArgsType() + replyType := method.ReplyType() + // two ways to prepare arguments + // if method like this 'func1(req []interface{}, rsp *User) error' + // we don't have arguments type + if (len(argsTypes) == 1 || len(argsTypes) == 2 && replyType == nil) && + argsTypes[0].String() == "[]interface {}" { + args, err = getArgsInterfaceFromRequest(req, methodConfig) + } else { + args, err = getArgsFromRequest(req, argsTypes, methodConfig) + } + if err != nil { + logger.Errorf("[Go Restful] parsing http parameters error:%v", err) + err = resp.WriteError(http.StatusInternalServerError, errors.New(parseParameterErrorStr)) + if err != nil { + logger.Errorf("[Go Restful] WriteErrorString error:%v", err) + } + } + result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]string))) + if result.Error() != nil { + err = resp.WriteError(http.StatusInternalServerError, result.Error()) + if err != nil { + logger.Errorf("[Go Restful] WriteError error:%v", err) + } + return + } + err = resp.WriteEntity(result.Result()) + if err != nil { + logger.Errorf("[Go Restful] WriteEntity error:%v", err) + } + } +} + +// getArgsInterfaceFromRequest when service function like GetUser(req []interface{}, rsp *User) error +// use this method to get arguments +func getArgsInterfaceFromRequest(req RestServerRequest, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) { + argsMap := make(map[int]interface{}, 8) + maxKey := 0 + for k, v := range methodConfig.PathParamsMap { + if maxKey < k { + maxKey = k + } + argsMap[k] = req.PathParameter(v) + } + for k, v := range methodConfig.QueryParamsMap { + if maxKey < k { + maxKey = k + } + params := req.QueryParameters(v) + if len(params) == 1 { + argsMap[k] = params[0] + } else { + argsMap[k] = params + } + } + for k, v := range methodConfig.HeadersMap { + if maxKey < k { + maxKey = k + } + argsMap[k] = req.HeaderParameter(v) + } + if methodConfig.Body >= 0 { + if maxKey < methodConfig.Body { + maxKey = methodConfig.Body + } + m := make(map[string]interface{}) + // TODO read as a slice + if err := req.ReadEntity(&m); err != nil { + return nil, perrors.Errorf("[Go restful] Read body entity as map[string]interface{} error:%v", err) + } + argsMap[methodConfig.Body] = m + } + args := make([]interface{}, maxKey+1) + for k, v := range argsMap { + if k >= 0 { + args[k] = v + } + } + return args, nil +} + +// getArgsFromRequest get arguments from server.RestServerRequest +func getArgsFromRequest(req RestServerRequest, argsTypes []reflect.Type, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) { + argsLength := len(argsTypes) + args := make([]interface{}, argsLength) + for i, t := range argsTypes { + args[i] = reflect.Zero(t).Interface() + } + if err := assembleArgsFromPathParams(methodConfig, argsLength, argsTypes, req, args); err != nil { + return nil, err + } + if err := assembleArgsFromQueryParams(methodConfig, argsLength, argsTypes, req, args); err != nil { + return nil, err + } + if err := assembleArgsFromBody(methodConfig, argsTypes, req, args); err != nil { + return nil, err + } + if err := assembleArgsFromHeaders(methodConfig, req, argsLength, argsTypes, args); err != nil { + return nil, err + } + return args, nil +} + +// assembleArgsFromHeaders assemble arguments from headers +func assembleArgsFromHeaders(methodConfig *rest_config.RestMethodConfig, req RestServerRequest, argsLength int, argsTypes []reflect.Type, args []interface{}) error { + for k, v := range methodConfig.HeadersMap { + param := req.HeaderParameter(v) + if k < 0 || k >= argsLength { + return perrors.Errorf("[Go restful] Header param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName) + } + t := argsTypes[k] + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() == reflect.String { + args[k] = param + } else { + return perrors.Errorf("[Go restful] Header param parse error, the index %v args's type isn't string", k) + } + } + return nil +} + +// assembleArgsFromBody assemble arguments from body +func assembleArgsFromBody(methodConfig *rest_config.RestMethodConfig, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error { + if methodConfig.Body >= 0 && methodConfig.Body < len(argsTypes) { + t := argsTypes[methodConfig.Body] + kind := t.Kind() + if kind == reflect.Ptr { + t = t.Elem() + } + var ni interface{} + if t.String() == "[]interface {}" { + ni = make([]map[string]interface{}, 0) + } else if t.String() == "interface {}" { + ni = make(map[string]interface{}) + } else { + n := reflect.New(t) + if n.CanInterface() { + ni = n.Interface() + } + } + if err := req.ReadEntity(&ni); err != nil { + return perrors.Errorf("[Go restful] Read body entity error, error is %v", perrors.WithStack(err)) + } + args[methodConfig.Body] = ni + } + return nil +} + +// assembleArgsFromQueryParams assemble arguments from query params +func assembleArgsFromQueryParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error { + var ( + err error + param interface{} + i64 int64 + ) + for k, v := range methodConfig.QueryParamsMap { + if k < 0 || k >= argsLength { + return perrors.Errorf("[Go restful] Query param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName) + } + t := argsTypes[k] + kind := t.Kind() + if kind == reflect.Ptr { + t = t.Elem() + } + if kind == reflect.Slice { + param = req.QueryParameters(v) + } else if kind == reflect.String { + param = req.QueryParameter(v) + } else if kind == reflect.Int { + param, err = strconv.Atoi(req.QueryParameter(v)) + } else if kind == reflect.Int32 { + i64, err = strconv.ParseInt(req.QueryParameter(v), 10, 32) + if err == nil { + param = int32(i64) + } + } else if kind == reflect.Int64 { + param, err = strconv.ParseInt(req.QueryParameter(v), 10, 64) + } else { + return perrors.Errorf("[Go restful] Query param parse error, the index %v args's type isn't int or string or slice", k) + } + if err != nil { + return perrors.Errorf("[Go restful] Query param parse error, error:%v", perrors.WithStack(err)) + } + args[k] = param + } + return nil +} + +// assembleArgsFromPathParams assemble arguments from path params +func assembleArgsFromPathParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error { + var ( + err error + param interface{} + i64 int64 + ) + for k, v := range methodConfig.PathParamsMap { + if k < 0 || k >= argsLength { + return perrors.Errorf("[Go restful] Path param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName) + } + t := argsTypes[k] + kind := t.Kind() + if kind == reflect.Ptr { + t = t.Elem() + } + if kind == reflect.Int { + param, err = strconv.Atoi(req.PathParameter(v)) + } else if kind == reflect.Int32 { + i64, err = strconv.ParseInt(req.PathParameter(v), 10, 32) + if err == nil { + param = int32(i64) + } + } else if kind == reflect.Int64 { + param, err = strconv.ParseInt(req.PathParameter(v), 10, 64) + } else if kind == reflect.String { + param = req.PathParameter(v) + } else { + return perrors.Errorf("[Go restful] Path param parse error, the index %v args's type isn't int or string", k) + } + if err != nil { + return perrors.Errorf("[Go restful] Path param parse error, error is %v", perrors.WithStack(err)) + } + args[k] = param + } + return nil +} diff --git a/protocol/rest/server/server_impl/go_restful_server.go b/protocol/rest/server/server_impl/go_restful_server.go index 69f36a5c80..c7d971fcaa 100644 --- a/protocol/rest/server/server_impl/go_restful_server.go +++ b/protocol/rest/server/server_impl/go_restful_server.go @@ -22,8 +22,6 @@ import ( "fmt" "net" "net/http" - "reflect" - "strconv" "strings" "time" ) @@ -38,27 +36,29 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" - "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/protocol/rest/config" "github.com/apache/dubbo-go/protocol/rest/server" ) func init() { - extension.SetRestServer(constant.DEFAULT_REST_SERVER, GetNewGoRestfulServer) + extension.SetRestServer(constant.DEFAULT_REST_SERVER, NewGoRestfulServer) } var filterSlice []restful.FilterFunction +// GoRestfulServer a rest server implement by go-restful type GoRestfulServer struct { srv *http.Server container *restful.Container } -func NewGoRestfulServer() *GoRestfulServer { +// NewGoRestfulServer a constructor of GoRestfulServer +func NewGoRestfulServer() server.RestServer { return &GoRestfulServer{} } +// Start go-restful server +// It will add all go-restful filters func (grs *GoRestfulServer) Start(url common.URL) { grs.container = restful.NewContainer() for _, filter := range filterSlice { @@ -80,61 +80,32 @@ func (grs *GoRestfulServer) Start(url common.URL) { }() } -func (grs *GoRestfulServer) Deploy(invoker protocol.Invoker, restMethodConfig map[string]*config.RestMethodConfig) { - svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/")) - for methodName, config := range restMethodConfig { - // get method - method := svc.Method()[methodName] - argsTypes := method.ArgsType() - replyType := method.ReplyType() - ws := new(restful.WebService) - ws.Path(config.Path). - Produces(strings.Split(config.Produces, ",")...). - Consumes(strings.Split(config.Consumes, ",")...). - Route(ws.Method(config.MethodType).To(getFunc(methodName, invoker, argsTypes, replyType, config))) - grs.container.Add(ws) +// Publish a http api in go-restful server +// The routeFunc should be invoked when the server receive a request +func (grs *GoRestfulServer) Deploy(restMethodConfig *config.RestMethodConfig, routeFunc func(request server.RestServerRequest, response server.RestServerResponse)) { + ws := &restful.WebService{} + rf := func(req *restful.Request, resp *restful.Response) { + routeFunc(NewGoRestfulRequestAdapter(req), resp) } + ws.Path(restMethodConfig.Path). + Produces(strings.Split(restMethodConfig.Produces, ",")...). + Consumes(strings.Split(restMethodConfig.Consumes, ",")...). + Route(ws.Method(restMethodConfig.MethodType).To(rf)) + grs.container.Add(ws) } -func getFunc(methodName string, invoker protocol.Invoker, argsTypes []reflect.Type, - replyType reflect.Type, config *config.RestMethodConfig) func(req *restful.Request, resp *restful.Response) { - return func(req *restful.Request, resp *restful.Response) { - var ( - err error - args []interface{} - ) - if (len(argsTypes) == 1 || len(argsTypes) == 2 && replyType == nil) && - argsTypes[0].String() == "[]interface {}" { - args = getArgsInterfaceFromRequest(req, config) - } else { - args = getArgsFromRequest(req, argsTypes, config) - } - result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodName, args, make(map[string]string))) - if result.Error() != nil { - err = resp.WriteError(http.StatusInternalServerError, result.Error()) - if err != nil { - logger.Errorf("[Go Restful] WriteError error:%v", err) - } - return - } - err = resp.WriteEntity(result.Result()) - if err != nil { - logger.Error("[Go Restful] WriteEntity error:%v", err) - } - } -} -func (grs *GoRestfulServer) UnDeploy(restMethodConfig map[string]*config.RestMethodConfig) { - for _, config := range restMethodConfig { - ws := new(restful.WebService) - ws.Path(config.Path) - err := grs.container.Remove(ws) - if err != nil { - logger.Warnf("[Go restful] Remove web service error:%v", err) - } +// Delete a http api in go-restful server +func (grs *GoRestfulServer) UnDeploy(restMethodConfig *config.RestMethodConfig) { + ws := new(restful.WebService) + ws.Path(restMethodConfig.Path) + err := grs.container.Remove(ws) + if err != nil { + logger.Warnf("[Go restful] Remove web service error:%v", err) } } +// Destroy the go-restful server func (grs *GoRestfulServer) Destroy() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -144,179 +115,59 @@ func (grs *GoRestfulServer) Destroy() { logger.Infof("[Go Restful] Server exiting") } -func getArgsInterfaceFromRequest(req *restful.Request, config *config.RestMethodConfig) []interface{} { - argsMap := make(map[int]interface{}, 8) - maxKey := 0 - for k, v := range config.PathParamsMap { - if maxKey < k { - maxKey = k - } - argsMap[k] = req.PathParameter(v) - } - for k, v := range config.QueryParamsMap { - if maxKey < k { - maxKey = k - } - params := req.QueryParameters(v) - if len(params) == 1 { - argsMap[k] = params[0] - } else { - argsMap[k] = params - } - } - for k, v := range config.HeadersMap { - if maxKey < k { - maxKey = k - } - argsMap[k] = req.HeaderParameter(v) - } - if config.Body >= 0 { - if maxKey < config.Body { - maxKey = config.Body - } - m := make(map[string]interface{}) - // TODO read as a slice - if err := req.ReadEntity(&m); err != nil { - logger.Warnf("[Go restful] Read body entity as map[string]interface{} error:%v", perrors.WithStack(err)) - } else { - argsMap[config.Body] = m - } - } - args := make([]interface{}, maxKey+1) - for k, v := range argsMap { - if k >= 0 { - args[k] = v - } - } - return args +// AddGoRestfulServerFilter let user add the http server of go-restful +// addFilter should before config.Load() +func AddGoRestfulServerFilter(filterFuc restful.FilterFunction) { + filterSlice = append(filterSlice, filterFuc) } -func getArgsFromRequest(req *restful.Request, argsTypes []reflect.Type, config *config.RestMethodConfig) []interface{} { - argsLength := len(argsTypes) - args := make([]interface{}, argsLength) - for i, t := range argsTypes { - args[i] = reflect.Zero(t).Interface() - } - var ( - err error - param interface{} - i64 int64 - ) - for k, v := range config.PathParamsMap { - if k < 0 || k >= argsLength { - logger.Errorf("[Go restful] Path param parse error, the args:%v doesn't exist", k) - continue - } - t := argsTypes[k] - kind := t.Kind() - if kind == reflect.Ptr { - t = t.Elem() - } - if kind == reflect.Int { - param, err = strconv.Atoi(req.PathParameter(v)) - } else if kind == reflect.Int32 { - i64, err = strconv.ParseInt(req.PathParameter(v), 10, 32) - if err == nil { - param = int32(i64) - } - } else if kind == reflect.Int64 { - param, err = strconv.ParseInt(req.PathParameter(v), 10, 64) - } else if kind == reflect.String { - param = req.PathParameter(v) - } else { - logger.Warnf("[Go restful] Path param parse error, the args:%v of type isn't int or string", k) - continue - } - if err != nil { - logger.Errorf("[Go restful] Path param parse error, error is %v", err) - continue - } - args[k] = param - } - for k, v := range config.QueryParamsMap { - if k < 0 || k >= argsLength { - logger.Errorf("[Go restful] Query param parse error, the args:%v doesn't exist", k) - continue - } - t := argsTypes[k] - kind := t.Kind() - if kind == reflect.Ptr { - t = t.Elem() - } - if kind == reflect.Slice { - param = req.QueryParameters(v) - } else if kind == reflect.String { - param = req.QueryParameter(v) - } else if kind == reflect.Int { - param, err = strconv.Atoi(req.QueryParameter(v)) - } else if kind == reflect.Int32 { - i64, err = strconv.ParseInt(req.QueryParameter(v), 10, 32) - if err == nil { - param = int32(i64) - } - } else if kind == reflect.Int64 { - param, err = strconv.ParseInt(req.QueryParameter(v), 10, 64) - } else { - logger.Errorf("[Go restful] Query param parse error, the args:%v of type isn't int or string or slice", k) - continue - } - if err != nil { - logger.Errorf("[Go restful] Query param parse error, error is %v", err) - continue - } - args[k] = param - } +// GoRestfulRequestAdapter a adapter struct about RestServerRequest +type GoRestfulRequestAdapter struct { + server.RestServerRequest + request *restful.Request +} - if config.Body >= 0 && config.Body < len(argsTypes) { - t := argsTypes[config.Body] - kind := t.Kind() - if kind == reflect.Ptr { - t = t.Elem() - } - var ni interface{} - if t.String() == "[]interface {}" { - ni = make([]map[string]interface{}, 0) - } else if t.String() == "interface {}" { - ni = make(map[string]interface{}) - } else { - n := reflect.New(t) - if n.CanInterface() { - ni = n.Interface() - } - } - if err := req.ReadEntity(&ni); err != nil { - logger.Errorf("[Go restful] Read body entity error:%v", err) - } else { - args[config.Body] = ni - } - } +// NewGoRestfulRequestAdapter a constructor of GoRestfulRequestAdapter +func NewGoRestfulRequestAdapter(request *restful.Request) *GoRestfulRequestAdapter { + return &GoRestfulRequestAdapter{request: request} +} - for k, v := range config.HeadersMap { - param := req.HeaderParameter(v) - if k < 0 || k >= argsLength { - logger.Errorf("[Go restful] Header param parse error, the args:%v doesn't exist", k) - continue - } - t := argsTypes[k] - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - if t.Kind() == reflect.String { - args[k] = param - } else { - logger.Errorf("[Go restful] Header param parse error, the args:%v of type isn't string", k) - } - } +// RawRequest a adapter function of server.RestServerRequest's RawRequest +func (grra *GoRestfulRequestAdapter) RawRequest() *http.Request { + return grra.request.Request +} - return args +// PathParameter a adapter function of server.RestServerRequest's PathParameter +func (grra *GoRestfulRequestAdapter) PathParameter(name string) string { + return grra.request.PathParameter(name) } -func GetNewGoRestfulServer() server.RestServer { - return NewGoRestfulServer() +// PathParameters a adapter function of server.RestServerRequest's QueryParameter +func (grra *GoRestfulRequestAdapter) PathParameters() map[string]string { + return grra.request.PathParameters() } -// Let user addFilter -// addFilter should before config.Load() -func AddGoRestfulServerFilter(filterFuc restful.FilterFunction) { - filterSlice = append(filterSlice, filterFuc) +// QueryParameter a adapter function of server.RestServerRequest's QueryParameters +func (grra *GoRestfulRequestAdapter) QueryParameter(name string) string { + return grra.request.QueryParameter(name) +} + +// QueryParameters a adapter function of server.RestServerRequest's QueryParameters +func (grra *GoRestfulRequestAdapter) QueryParameters(name string) []string { + return grra.request.QueryParameters(name) +} + +// BodyParameter a adapter function of server.RestServerRequest's BodyParameter +func (grra *GoRestfulRequestAdapter) BodyParameter(name string) (string, error) { + return grra.request.BodyParameter(name) +} + +// HeaderParameter a adapter function of server.RestServerRequest's HeaderParameter +func (grra *GoRestfulRequestAdapter) HeaderParameter(name string) string { + return grra.request.HeaderParameter(name) +} + +// ReadEntity a adapter func of server.RestServerRequest's ReadEntity +func (grra *GoRestfulRequestAdapter) ReadEntity(entityPointer interface{}) error { + return grra.request.ReadEntity(entityPointer) } diff --git a/registry/base_registry.go b/registry/base_registry.go index 04694da25a..3e1bddf233 100644 --- a/registry/base_registry.go +++ b/registry/base_registry.go @@ -121,6 +121,7 @@ func (r *BaseRegistry) Destroy() { close(r.done) // wait waitgroup done (wait listeners outside close over) r.wg.Wait() + //close registry client r.closeRegisters() } @@ -178,7 +179,10 @@ func (r *BaseRegistry) RestartCallBack() bool { } logger.Infof("success to re-register service :%v", confIf.Key()) } - r.facadeBasedRegistry.InitListeners() + + if flag { + r.facadeBasedRegistry.InitListeners() + } return flag } diff --git a/registry/consul/listener.go b/registry/consul/listener.go index b047a4c08c..5fac9ec0f9 100644 --- a/registry/consul/listener.go +++ b/registry/consul/listener.go @@ -142,7 +142,6 @@ func (l *consulListener) run() { func (l *consulListener) handler(idx uint64, raw interface{}) { var ( service *consul.ServiceEntry - event *registry.ServiceEvent url common.URL ok bool err error @@ -183,7 +182,7 @@ func (l *consulListener) handler(idx uint64, raw interface{}) { } l.urls = newUrls - for _, event = range events { + for _, event := range events { l.eventCh <- event } } diff --git a/registry/directory/directory.go b/registry/directory/directory.go index 20be268d74..49b0027f43 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -19,7 +19,6 @@ package directory import ( "sync" - "time" ) import ( @@ -28,6 +27,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" @@ -42,15 +42,11 @@ import ( "github.com/apache/dubbo-go/remoting" ) -// Options ... -type Options struct { - serviceTTL time.Duration +func init() { + extension.SetDefaultRegistryDirectory(NewRegistryDirectory) } -// Option ... -type Option func(*Options) - -type registryDirectory struct { +type RegistryDirectory struct { directory.BaseDirectory cacheInvokers []protocol.Invoker listenerLock sync.Mutex @@ -61,48 +57,42 @@ type registryDirectory struct { configurators []config_center.Configurator consumerConfigurationListener *consumerConfigurationListener referenceConfigurationListener *referenceConfigurationListener - Options - serviceKey string - forbidden atomic.Bool + serviceKey string + forbidden atomic.Bool } -// NewRegistryDirectory ... -func NewRegistryDirectory(url *common.URL, registry registry.Registry, opts ...Option) (*registryDirectory, error) { - options := Options{ - //default 300s - serviceTTL: time.Duration(300e9), - } - for _, opt := range opts { - opt(&options) - } +// NewRegistryDirectory will create a new RegistryDirectory +func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.Directory, error) { if url.SubURL == nil { return nil, perrors.Errorf("url is invalid, suburl can not be nil") } - dir := ®istryDirectory{ + dir := &RegistryDirectory{ BaseDirectory: directory.NewBaseDirectory(url), cacheInvokers: []protocol.Invoker{}, cacheInvokersMap: &sync.Map{}, serviceType: url.SubURL.Service(), registry: registry, - Options: options, } dir.consumerConfigurationListener = newConsumerConfigurationListener(dir) + + go dir.subscribe(url.SubURL) return dir, nil } -//subscribe from registry -func (dir *registryDirectory) Subscribe(url *common.URL) { +// subscribe from registry +func (dir *RegistryDirectory) subscribe(url *common.URL) { dir.consumerConfigurationListener.addNotifyListener(dir) dir.referenceConfigurationListener = newReferenceConfigurationListener(dir, url) dir.registry.Subscribe(url, dir) } -func (dir *registryDirectory) Notify(event *registry.ServiceEvent) { +// Notify monitor changes from registry,and update the cacheServices +func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) { go dir.update(event) } -//subscribe service from registry, and update the cacheServices -func (dir *registryDirectory) update(res *registry.ServiceEvent) { +// update the cacheServices and subscribe service from registry +func (dir *RegistryDirectory) update(res *registry.ServiceEvent) { if res == nil { return } @@ -111,7 +101,7 @@ func (dir *registryDirectory) update(res *registry.ServiceEvent) { dir.refreshInvokers(res) } -func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) { +func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) { var ( url *common.URL oldInvoker protocol.Invoker = nil @@ -162,10 +152,12 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) { } -func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker { - newInvokersList := []protocol.Invoker{} +func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { + var ( + err error + newInvokersList []protocol.Invoker + ) groupInvokersMap := make(map[string][]protocol.Invoker) - groupInvokersList := []protocol.Invoker{} dir.cacheInvokersMap.Range(func(key, value interface{}) bool { newInvokersList = append(newInvokersList, value.(protocol.Invoker)) @@ -181,6 +173,7 @@ func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker { groupInvokersMap[group] = []protocol.Invoker{invoker} } } + groupInvokersList := make([]protocol.Invoker, 0, len(groupInvokersMap)) if len(groupInvokersMap) == 1 { //len is 1 it means no group setting ,so do not need cluster again for _, invokers := range groupInvokersMap { @@ -189,17 +182,21 @@ func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker { } else { for _, invokers := range groupInvokersMap { staticDir := directory.NewStaticDirectory(invokers) - cluster := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) - staticDir.BuildRouterChain(invokers) - groupInvokersList = append(groupInvokersList, cluster.Join(staticDir)) + cst := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) + err = staticDir.BuildRouterChain(invokers) + if err != nil { + logger.Error(err) + continue + } + groupInvokersList = append(groupInvokersList, cst.Join(staticDir)) } } return groupInvokersList } -// uncacheInvoker return abandoned Invoker,if no Invoker to be abandoned,return nil -func (dir *registryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker { +// uncacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil +func (dir *RegistryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker { logger.Debugf("service will be deleted in cache invokers: invokers key is %s!", url.Key()) if cacheInvoker, ok := dir.cacheInvokersMap.Load(url.Key()); ok { dir.cacheInvokersMap.Delete(url.Key()) @@ -208,8 +205,8 @@ func (dir *registryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker { return nil } -// cacheInvoker return abandoned Invoker,if no Invoker to be abandoned,return nil -func (dir *registryDirectory) cacheInvoker(url *common.URL) protocol.Invoker { +// cacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil +func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker { dir.overrideUrl(dir.GetDirectoryUrl()) referenceUrl := dir.GetDirectoryUrl().SubURL @@ -244,8 +241,8 @@ func (dir *registryDirectory) cacheInvoker(url *common.URL) protocol.Invoker { return nil } -//select the protocol invokers from the directory -func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.Invoker { +// List selected protocol invokers from the directory +func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.Invoker { invokers := dir.cacheInvokers routerChain := dir.RouterChain() @@ -255,7 +252,8 @@ func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.In return routerChain.Route(invokers, dir.cacheOriginUrl, invocation) } -func (dir *registryDirectory) IsAvailable() bool { +// IsAvailable whether the directory is available +func (dir *RegistryDirectory) IsAvailable() bool { if !dir.BaseDirectory.IsAvailable() { return dir.BaseDirectory.IsAvailable() } @@ -269,7 +267,8 @@ func (dir *registryDirectory) IsAvailable() bool { return false } -func (dir *registryDirectory) Destroy() { +// Destroy method +func (dir *RegistryDirectory) Destroy() { //TODO:unregister & unsubscribe dir.BaseDirectory.Destroy(func() { invokers := dir.cacheInvokers @@ -280,7 +279,7 @@ func (dir *registryDirectory) Destroy() { }) } -func (dir *registryDirectory) overrideUrl(targetUrl *common.URL) { +func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) { doOverrideUrl(dir.configurators, targetUrl) doOverrideUrl(dir.consumerConfigurationListener.Configurators(), targetUrl) doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl) @@ -294,11 +293,11 @@ func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common type referenceConfigurationListener struct { registry.BaseConfigurationListener - directory *registryDirectory + directory *RegistryDirectory url *common.URL } -func newReferenceConfigurationListener(dir *registryDirectory, url *common.URL) *referenceConfigurationListener { +func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL) *referenceConfigurationListener { listener := &referenceConfigurationListener{directory: dir, url: url} listener.InitWith( url.EncodedServiceKey()+constant.CONFIGURATORS_SUFFIX, @@ -308,6 +307,7 @@ func newReferenceConfigurationListener(dir *registryDirectory, url *common.URL) return listener } +// Process handle events and update Invokers func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) l.directory.refreshInvokers(nil) @@ -316,10 +316,10 @@ func (l *referenceConfigurationListener) Process(event *config_center.ConfigChan type consumerConfigurationListener struct { registry.BaseConfigurationListener listeners []registry.NotifyListener - directory *registryDirectory + directory *RegistryDirectory } -func newConsumerConfigurationListener(dir *registryDirectory) *consumerConfigurationListener { +func newConsumerConfigurationListener(dir *RegistryDirectory) *consumerConfigurationListener { listener := &consumerConfigurationListener{directory: dir} listener.InitWith( config.GetConsumerConfig().ApplicationConfig.Name+constant.CONFIGURATORS_SUFFIX, @@ -333,6 +333,7 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti l.listeners = append(l.listeners, listener) } +// Process handles events from Configuration Center and update Invokers func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) l.directory.refreshInvokers(nil) diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go index 0dde44e73c..ac3f7124c1 100644 --- a/registry/directory/directory_test.go +++ b/registry/directory/directory_test.go @@ -25,6 +25,7 @@ import ( ) import ( + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -37,6 +38,7 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/mock" "github.com/apache/dubbo-go/protocol/protocolwrapper" "github.com/apache/dubbo-go/registry" "github.com/apache/dubbo-go/remoting" @@ -79,10 +81,9 @@ func TestSubscribe_Group(t *testing.T) { suburl.SetParam(constant.CLUSTER_KEY, "mock") regurl.SubURL = &suburl mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) - registryDirectory, _ := NewRegistryDirectory(®url, mockRegistry) - - go registryDirectory.Subscribe(common.NewURLWithOptions(common.WithPath("testservice"))) + dir, _ := NewRegistryDirectory(®url, mockRegistry) + go dir.(*RegistryDirectory).subscribe(common.NewURLWithOptions(common.WithPath("testservice"))) //for group1 urlmap := url.Values{} urlmap.Set(constant.GROUP_KEY, "group1") @@ -101,7 +102,7 @@ func TestSubscribe_Group(t *testing.T) { } time.Sleep(1e9) - assert.Len(t, registryDirectory.cacheInvokers, 2) + assert.Len(t, dir.(*RegistryDirectory).cacheInvokers, 2) } func Test_Destroy(t *testing.T) { @@ -170,10 +171,24 @@ Loop1: break Loop1 } } +} +func Test_toGroupInvokers(t *testing.T) { + registryDirectory, _ := normalRegistryDir() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + newUrl, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + invoker.EXPECT().GetUrl().Return(newUrl).AnyTimes() + + registryDirectory.cacheInvokersMap.Store("group1", invoker) + registryDirectory.cacheInvokersMap.Store("group2", invoker) + registryDirectory.cacheInvokersMap.Store("group1", invoker) + groupInvokers := registryDirectory.toGroupInvokers() + assert.Len(t, groupInvokers, 2) } -func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockRegistry) { +func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockRegistry) { extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) url, _ := common.NewURL("mock://127.0.0.1:1111") @@ -185,9 +200,9 @@ func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockR ) url.SubURL = &suburl mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) - registryDirectory, _ := NewRegistryDirectory(&url, mockRegistry) + dir, _ := NewRegistryDirectory(&url, mockRegistry) - go registryDirectory.Subscribe(&suburl) + go dir.(*RegistryDirectory).subscribe(&suburl) if len(noMockEvent) == 0 { for i := 0; i < 3; i++ { mockRegistry.(*registry.MockRegistry).MockEvent( @@ -201,5 +216,5 @@ func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockR ) } } - return registryDirectory, mockRegistry.(*registry.MockRegistry) + return dir.(*RegistryDirectory), mockRegistry.(*registry.MockRegistry) } diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index e691ae3cf1..f27e7ce8ba 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -24,9 +24,9 @@ import ( ) import ( + "github.com/coreos/etcd/embed" "github.com/dubbogo/getty" "github.com/stretchr/testify/suite" - "go.etcd.io/etcd/embed" ) import ( diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go index e1c2576811..5d389c3637 100644 --- a/registry/etcdv3/registry.go +++ b/registry/etcdv3/registry.go @@ -164,9 +164,7 @@ func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) //register the svc to dataListener r.dataListener.AddInterestedURL(svc) - for _, v := range strings.Split(svc.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") { - go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, svc.Service()), r.dataListener) - } + go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, svc.Service()), r.dataListener) return configListener, nil } diff --git a/registry/event.go b/registry/event.go index 37d863d216..be9f11d00b 100644 --- a/registry/event.go +++ b/registry/event.go @@ -32,9 +32,9 @@ func init() { rand.Seed(time.Now().UnixNano()) } -////////////////////////////////////////// +// //////////////////////////////////////// // service event -////////////////////////////////////////// +// //////////////////////////////////////// // ServiceEvent ... type ServiceEvent struct { @@ -42,6 +42,69 @@ type ServiceEvent struct { Service common.URL } +// String return the description of event func (e ServiceEvent) String() string { return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}}", e.Action, e.Service) } + +// Event is align with Event interface in Java. +// it's the top abstraction +// Align with 2.7.5 +type Event interface { + fmt.Stringer + GetSource() interface{} + GetTimestamp() time.Time +} + +// baseEvent is the base implementation of Event +// You should never use it directly +type baseEvent struct { + source interface{} + timestamp time.Time +} + +// GetSource return the source +func (b *baseEvent) GetSource() interface{} { + return b.source +} + +// GetTimestamp return the timestamp when the event is created +func (b *baseEvent) GetTimestamp() time.Time { + return b.timestamp +} + +// String return a human readable string representing this event +func (b *baseEvent) String() string { + return fmt.Sprintf("baseEvent[source = %#v]", b.source) +} + +func newBaseEvent(source interface{}) *baseEvent { + return &baseEvent{ + source: source, + timestamp: time.Now(), + } +} + +// ServiceInstancesChangedEvent represents service instances make some changing +type ServiceInstancesChangedEvent struct { + baseEvent + ServiceName string + Instances []ServiceInstance +} + +// String return the description of the event +func (s *ServiceInstancesChangedEvent) String() string { + return fmt.Sprintf("ServiceInstancesChangedEvent[source=%s]", s.ServiceName) +} + +// NewServiceInstancesChangedEvent will create the ServiceInstanceChangedEvent instance +func NewServiceInstancesChangedEvent(serviceName string, instances []ServiceInstance) *ServiceInstancesChangedEvent { + return &ServiceInstancesChangedEvent{ + baseEvent: baseEvent{ + source: serviceName, + timestamp: time.Now(), + }, + ServiceName: serviceName, + Instances: instances, + } +} diff --git a/registry/event_listener.go b/registry/event_listener.go new file mode 100644 index 0000000000..b8d6148442 --- /dev/null +++ b/registry/event_listener.go @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package registry + +import ( + gxsort "github.com/dubbogo/gost/sort" +) + +// EventListener is an new interface used to align with dubbo 2.7.5 +// It contains the Prioritized means that the listener has its priority +type EventListener interface { + gxsort.Prioritizer + // OnEvent handle this event + OnEvent(e Event) error +} + +// ConditionalEventListener only handle the event which it can handle +type ConditionalEventListener interface { + EventListener + // Accept will make the decision whether it should handle this event + Accept(e Event) bool +} + +// TODO (implement ConditionalEventListener) +type ServiceInstancesChangedListener struct { + ServiceName string +} diff --git a/registry/kubernetes/listener_test.go b/registry/kubernetes/listener_test.go index c50b5b670a..1c9d8bdd5e 100644 --- a/registry/kubernetes/listener_test.go +++ b/registry/kubernetes/listener_test.go @@ -191,7 +191,6 @@ type KubernetesRegistryTestSuite struct { } func (s *KubernetesRegistryTestSuite) initRegistry() *kubernetesRegistry { - t := s.T() regurl, err := common.NewURL("registry://127.0.0.1:443", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) @@ -204,7 +203,7 @@ func (s *KubernetesRegistryTestSuite) initRegistry() *kubernetesRegistry { out := fake.NewSimpleClientset() // mock current pod - if _, err := out.CoreV1().Pods(s.currentPod.GetNamespace()).Create(&s.currentPod); err != nil { + if _, err = out.CoreV1().Pods(s.currentPod.GetNamespace()).Create(&s.currentPod); err != nil { t.Fatal(err) } return out, nil diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go index 7212a83d63..8a02d0e3e6 100644 --- a/registry/kubernetes/registry.go +++ b/registry/kubernetes/registry.go @@ -21,7 +21,6 @@ import ( "fmt" "os" "path" - "strings" "sync" "time" ) @@ -135,9 +134,7 @@ func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, er //register the svc to dataListener r.dataListener.AddInterestedURL(svc) - for _, v := range strings.Split(svc.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") { - go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, svc.Service()), r.dataListener) - } + go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, svc.Service()), r.dataListener) return configListener, nil } diff --git a/registry/kubernetes/registry_test.go b/registry/kubernetes/registry_test.go index ea6d7663a9..a650b189c3 100644 --- a/registry/kubernetes/registry_test.go +++ b/registry/kubernetes/registry_test.go @@ -68,9 +68,9 @@ func (s *KubernetesRegistryTestSuite) TestSubscribe() { time.Sleep(1e9) go func() { - err := r.Register(url) - if err != nil { - t.Fatal(err) + registerErr := r.Register(url) + if registerErr != nil { + t.Fatal(registerErr) } }() diff --git a/registry/nacos/base_registry.go b/registry/nacos/base_registry.go new file mode 100644 index 0000000000..63f4999675 --- /dev/null +++ b/registry/nacos/base_registry.go @@ -0,0 +1,102 @@ +/* + * 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 nacos + +import ( + "net" + "strconv" + "strings" + "time" +) + +import ( + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" +) + +// baseRegistry is the parent of both interface-level registry +// and service discovery(related to application-level registry) +type nacosBaseRegistry struct { + *common.URL + namingClient naming_client.INamingClient +} + +// newBaseRegistry will create new instance +func newBaseRegistry(url *common.URL) (nacosBaseRegistry, error) { + nacosConfig, err := getNacosConfig(url) + if err != nil { + return nacosBaseRegistry{}, err + } + client, err := clients.CreateNamingClient(nacosConfig) + if err != nil { + return nacosBaseRegistry{}, err + } + registry := nacosBaseRegistry{ + URL: url, + namingClient: client, + } + return registry, nil +} + +// getNacosConfig will return the nacos config +func getNacosConfig(url *common.URL) (map[string]interface{}, error) { + if url == nil { + return nil, perrors.New("url is empty!") + } + if len(url.Location) == 0 { + return nil, perrors.New("url.location is empty!") + } + configMap := make(map[string]interface{}, 2) + + addresses := strings.Split(url.Location, ",") + serverConfigs := make([]nacosConstant.ServerConfig, 0, len(addresses)) + for _, addr := range addresses { + ip, portStr, err := net.SplitHostPort(addr) + if err != nil { + return nil, perrors.WithMessagef(err, "split [%s] ", addr) + } + port, _ := strconv.Atoi(portStr) + serverConfigs = append(serverConfigs, nacosConstant.ServerConfig{ + IpAddr: ip, + Port: uint64(port), + }) + } + configMap["serverConfigs"] = serverConfigs + + var clientConfig nacosConstant.ClientConfig + timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) + if err != nil { + return nil, err + } + clientConfig.TimeoutMs = uint64(timeout.Seconds() * 1000) + clientConfig.ListenInterval = 2 * clientConfig.TimeoutMs + clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "") + clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "") + clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "") + clientConfig.NotLoadCacheAtStart = true + configMap["clientConfig"] = clientConfig + + return configMap, nil +} diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go index 965e91e894..a436b85064 100644 --- a/registry/nacos/registry.go +++ b/registry/nacos/registry.go @@ -19,7 +19,6 @@ package nacos import ( "bytes" - "net" "strconv" "strings" "time" @@ -27,9 +26,6 @@ import ( import ( gxnet "github.com/dubbogo/gost/net" - "github.com/nacos-group/nacos-sdk-go/clients" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/vo" perrors "github.com/pkg/errors" ) @@ -57,64 +53,18 @@ func init() { } type nacosRegistry struct { - *common.URL - namingClient naming_client.INamingClient -} - -func getNacosConfig(url *common.URL) (map[string]interface{}, error) { - if url == nil { - return nil, perrors.New("url is empty!") - } - if len(url.Location) == 0 { - return nil, perrors.New("url.location is empty!") - } - configMap := make(map[string]interface{}, 2) - - addresses := strings.Split(url.Location, ",") - serverConfigs := make([]nacosConstant.ServerConfig, 0, len(addresses)) - for _, addr := range addresses { - ip, portStr, err := net.SplitHostPort(addr) - if err != nil { - return nil, perrors.WithMessagef(err, "split [%s] ", addr) - } - port, _ := strconv.Atoi(portStr) - serverConfigs = append(serverConfigs, nacosConstant.ServerConfig{ - IpAddr: ip, - Port: uint64(port), - }) - } - configMap["serverConfigs"] = serverConfigs - - var clientConfig nacosConstant.ClientConfig - timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) - if err != nil { - return nil, err - } - clientConfig.TimeoutMs = uint64(timeout.Seconds() * 1000) - clientConfig.ListenInterval = 2 * clientConfig.TimeoutMs - clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "") - clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "") - clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "") - clientConfig.NotLoadCacheAtStart = true - configMap["clientConfig"] = clientConfig - - return configMap, nil + nacosBaseRegistry } +// newNacosRegistry will create an instance func newNacosRegistry(url *common.URL) (registry.Registry, error) { - nacosConfig, err := getNacosConfig(url) + base, err := newBaseRegistry(url) if err != nil { - return nil, err - } - client, err := clients.CreateNamingClient(nacosConfig) - if err != nil { - return nil, err - } - registry := nacosRegistry{ - URL: url, - namingClient: client, + return nil, perrors.WithStack(err) } - return ®istry, nil + return &nacosRegistry{ + base, + }, nil } func getCategory(url common.URL) string { diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go index 7475b455c0..d0311b200b 100644 --- a/registry/nacos/registry_test.go +++ b/registry/nacos/registry_test.go @@ -42,7 +42,7 @@ func TestNacosRegistry_Register(t *testing.T) { urlMap.Set(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider") urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") - url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, err := newNacosRegistry(®url) assert.Nil(t, err) @@ -50,7 +50,7 @@ func TestNacosRegistry_Register(t *testing.T) { t.Errorf("new nacos registry error:%s \n", err.Error()) return } - err = reg.Register(url) + err = reg.Register(testUrl) assert.Nil(t, err) if err != nil { t.Errorf("register error:%s \n", err.Error()) @@ -72,10 +72,10 @@ func TestNacosRegistry_Subscribe(t *testing.T) { urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") - url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, _ := newNacosRegistry(®url) - err := reg.Register(url) + err := reg.Register(testUrl) assert.Nil(t, err) if err != nil { t.Errorf("new nacos registry error:%s \n", err.Error()) @@ -84,7 +84,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) { regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) reg2, _ := newNacosRegistry(®url) - listener, err := reg2.(*nacosRegistry).subscribe(&url) + listener, err := reg2.(*nacosRegistry).subscribe(&testUrl) assert.Nil(t, err) if err != nil { t.Errorf("subscribe error:%s \n", err.Error()) diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go new file mode 100644 index 0000000000..7d3406cac2 --- /dev/null +++ b/registry/nacos/service_discovery.go @@ -0,0 +1,285 @@ +/* + * 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 nacos + +import ( + "github.com/dubbogo/gost/container/set" + "github.com/dubbogo/gost/page" + "github.com/nacos-group/nacos-sdk-go/model" + "github.com/nacos-group/nacos-sdk-go/vo" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/registry" +) + +const ( + defaultGroup = "DEFAULT_GROUP" + idKey = "id" +) + +// init will put the service discovery into extension +func init() { + extension.SetServiceDiscovery(constant.NACOS_KEY, newNacosServiceDiscovery) +} + +// nacosServiceDiscovery is the implementation of service discovery based on nacos. +// There is a problem, the go client for nacos does not support the id field. +// we will use the metadata to store the id of ServiceInstance +type nacosServiceDiscovery struct { + nacosBaseRegistry + group string +} + +// Destroy will close the service discovery. +// Actually, it only marks the naming client as null and then return +func (n *nacosServiceDiscovery) Destroy() error { + n.namingClient = nil + return nil +} + +// Register will register the service to nacos +func (n *nacosServiceDiscovery) Register(instance registry.ServiceInstance) error { + ins := n.toRegisterInstance(instance) + ok, err := n.namingClient.RegisterInstance(ins) + if err != nil || !ok { + return perrors.WithMessage(err, "Could not register the instance. "+instance.GetServiceName()) + } + return nil +} + +// Update will update the information +// However, because nacos client doesn't support the update API, +// so we should unregister the instance and then register it again. +// the error handling is hard to implement +func (n *nacosServiceDiscovery) Update(instance registry.ServiceInstance) error { + // TODO(wait for nacos support) + err := n.Unregister(instance) + if err != nil { + return perrors.WithStack(err) + } + return n.Register(instance) +} + +// Unregister will unregister the instance +func (n *nacosServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + ok, err := n.namingClient.DeregisterInstance(n.toDeregisterInstance(instance)) + if err != nil || !ok { + return perrors.WithMessage(err, "Could not unregister the instance. "+instance.GetServiceName()) + } + return nil +} + +// GetDefaultPageSize will return the constant registry.DefaultPageSize +func (n *nacosServiceDiscovery) GetDefaultPageSize() int { + return registry.DefaultPageSize +} + +// GetServices will return the all services +func (n *nacosServiceDiscovery) GetServices() *gxset.HashSet { + services, err := n.namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + GroupName: n.group, + }) + + res := gxset.NewSet() + if err != nil { + logger.Errorf("Could not query the services: %v", err) + return res + } + + for _, e := range services { + res.Add(e.Name) + } + return res +} + +// GetInstances will return the instances of serviceName and the group +func (n *nacosServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + instances, err := n.namingClient.SelectAllInstances(vo.SelectAllInstancesParam{ + ServiceName: serviceName, + GroupName: n.group, + }) + if err != nil { + logger.Errorf("Could not query the instances for service: " + serviceName + ", group: " + n.group) + return make([]registry.ServiceInstance, 0, 0) + } + res := make([]registry.ServiceInstance, 0, len(instances)) + for _, ins := range instances { + metadata := ins.Metadata + id := metadata[idKey] + + delete(metadata, idKey) + + res = append(res, ®istry.DefaultServiceInstance{ + Id: id, + ServiceName: ins.ServiceName, + Host: ins.Ip, + Port: int(ins.Port), + Enable: ins.Enable, + Healthy: ins.Healthy, + Metadata: metadata, + }) + } + + return res +} + +// GetInstancesByPage will return the instances +// Due to nacos client does not support pagination, so we have to query all instances and then return part of them +func (n *nacosServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + all := n.GetInstances(serviceName) + res := make([]interface{}, 0, pageSize) + // could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance + for i := offset; i < len(all) && i < offset+pageSize; i++ { + res = append(res, all[i]) + } + return gxpage.New(offset, pageSize, res, len(all)) +} + +// GetHealthyInstancesByPage will return the instance +// The nacos client has an API SelectInstances, which has a parameter call HealthyOnly. +// However, the healthy parameter in this method maybe false. So we can not use that API. +// Thus, we must query all instances and then do filter +func (n *nacosServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { + all := n.GetInstances(serviceName) + res := make([]interface{}, 0, pageSize) + // could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance + var ( + i = offset + count = 0 + ) + for i < len(all) && count < pageSize { + ins := all[i] + if ins.IsHealthy() == healthy { + res = append(res, all[i]) + count++ + } + i++ + } + return gxpage.New(offset, pageSize, res, len(all)) +} + +// GetRequestInstances will return the instances +// The nacos client doesn't have batch API, so we should query those serviceNames one by one. +func (n *nacosServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { + res := make(map[string]gxpage.Pager, len(serviceNames)) + for _, name := range serviceNames { + res[name] = n.GetInstancesByPage(name, offset, requestedSize) + } + return res +} + +// AddListener will add a listener +func (n *nacosServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + return n.namingClient.Subscribe(&vo.SubscribeParam{ + ServiceName: listener.ServiceName, + SubscribeCallback: func(services []model.SubscribeService, err error) { + if err != nil { + logger.Errorf("Could not handle the subscribe notification because the err is not nil."+ + " service name: %s, err: %v", listener.ServiceName, err) + } + instances := make([]registry.ServiceInstance, 0, len(services)) + for _, service := range services { + // we won't use the nacos instance id here but use our instance id + metadata := service.Metadata + id := metadata[idKey] + + delete(metadata, idKey) + + instances = append(instances, ®istry.DefaultServiceInstance{ + Id: id, + ServiceName: service.ServiceName, + Host: service.Ip, + Port: int(service.Port), + Enable: service.Enable, + Healthy: true, + Metadata: metadata, + }) + } + + e := n.DispatchEventForInstances(listener.ServiceName, instances) + if e != nil { + logger.Errorf("Dispatching event got exception, service name: %s, err: %v", listener.ServiceName, err) + } + }, + }) +} + +// DispatchEventByServiceName will dispatch the event for the service with the service name +func (n *nacosServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + return n.DispatchEventForInstances(serviceName, n.GetInstances(serviceName)) +} + +// DispatchEventForInstances will dispatch the event to those instances +func (n *nacosServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { + return n.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances)) +} + +// DispatchEvent will dispatch the event +func (n *nacosServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + // TODO(waiting for event dispatcher, another task) + return nil +} + +// toRegisterInstance convert the ServiceInstance to RegisterInstanceParam +// the Ephemeral will be true +func (n *nacosServiceDiscovery) toRegisterInstance(instance registry.ServiceInstance) vo.RegisterInstanceParam { + metadata := instance.GetMetadata() + if metadata == nil { + metadata = make(map[string]string, 1) + } + metadata[idKey] = instance.GetId() + return vo.RegisterInstanceParam{ + ServiceName: instance.GetServiceName(), + Ip: instance.GetHost(), + Port: uint64(instance.GetPort()), + Metadata: metadata, + Enable: instance.IsEnable(), + Healthy: instance.IsHealthy(), + GroupName: n.group, + Ephemeral: true, + } +} + +// toDeregisterInstance will convert the ServiceInstance to DeregisterInstanceParam +func (n *nacosServiceDiscovery) toDeregisterInstance(instance registry.ServiceInstance) vo.DeregisterInstanceParam { + return vo.DeregisterInstanceParam{ + ServiceName: instance.GetServiceName(), + Ip: instance.GetHost(), + Port: uint64(instance.GetPort()), + GroupName: n.group, + } +} + +// toDeregisterInstance will create new service discovery instance +func newNacosServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) { + + base, err := newBaseRegistry(url) + if err != nil { + return nil, perrors.WithStack(err) + } + return &nacosServiceDiscovery{ + nacosBaseRegistry: base, + group: url.GetParam(constant.NACOS_GROUP, defaultGroup), + }, nil +} diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go new file mode 100644 index 0000000000..a756e86693 --- /dev/null +++ b/registry/nacos/service_discovery_test.go @@ -0,0 +1,122 @@ +/* + * 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 nacos + +import ( + "strconv" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/registry" +) + +func TestNacosServiceDiscovery_Destroy(t *testing.T) { + serviceDiscovry, err := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl()) + assert.Nil(t, err) + assert.NotNil(t, serviceDiscovry) + err = serviceDiscovry.Destroy() + assert.Nil(t, err) + assert.Nil(t, serviceDiscovry.(*nacosServiceDiscovery).namingClient) +} + +func TestNacosServiceDiscovery_CRUD(t *testing.T) { + serviceName := "service-name" + id := "id" + host := "host" + port := 123 + instance := ®istry.DefaultServiceInstance{ + Id: id, + ServiceName: serviceName, + Host: host, + Port: port, + Enable: true, + Healthy: true, + Metadata: nil, + } + + // clean data + + serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl()) + + // clean data for local test + serviceDiscovry.Unregister(®istry.DefaultServiceInstance{ + Id: id, + ServiceName: serviceName, + Host: host, + Port: port, + }) + + err := serviceDiscovry.Register(instance) + assert.Nil(t, err) + + page := serviceDiscovry.GetHealthyInstancesByPage(serviceName, 0, 10, true) + assert.NotNil(t, page) + + assert.Equal(t, 0, page.GetOffset()) + assert.Equal(t, 10, page.GetPageSize()) + assert.Equal(t, 1, page.GetDataSize()) + + instance = page.GetData()[0].(*registry.DefaultServiceInstance) + assert.NotNil(t, instance) + assert.Equal(t, id, instance.GetId()) + assert.Equal(t, host, instance.GetHost()) + assert.Equal(t, port, instance.GetPort()) + assert.Equal(t, serviceName, instance.GetServiceName()) + assert.Equal(t, 0, len(instance.GetMetadata())) + + instance.Metadata["a"] = "b" + + err = serviceDiscovry.Update(instance) + assert.Nil(t, err) + + pageMap := serviceDiscovry.GetRequestInstances([]string{serviceName}, 0, 1) + assert.Equal(t, 1, len(pageMap)) + page = pageMap[serviceName] + assert.NotNil(t, page) + assert.Equal(t, 1, len(page.GetData())) + + instance = page.GetData()[0].(*registry.DefaultServiceInstance) + v, _ := instance.Metadata["a"] + assert.Equal(t, "b", v) + + // test dispatcher event + err = serviceDiscovry.DispatchEventByServiceName(serviceName) + assert.Nil(t, err) + + // test AddListener + err = serviceDiscovry.AddListener(®istry.ServiceInstancesChangedListener{}) + assert.Nil(t, err) +} + +func TestNacosServiceDiscovery_GetDefaultPageSize(t *testing.T) { + serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl()) + assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize()) +} + +func mockUrl() *common.URL { + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + return ®url +} diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index a7678ba4e2..a936db80bf 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -22,7 +22,6 @@ import ( "strings" "sync" ) - import ( gxset "github.com/dubbogo/gost/container/set" ) @@ -39,20 +38,26 @@ import ( "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/protocolwrapper" "github.com/apache/dubbo-go/registry" - directory2 "github.com/apache/dubbo-go/registry/directory" + _ "github.com/apache/dubbo-go/registry/directory" "github.com/apache/dubbo-go/remoting" ) var ( - regProtocol *registryProtocol + regProtocol *registryProtocol + once sync.Once + reserveParams = []string{ + "application", "codec", "exchanger", "serialization", "cluster", "connections", "deprecated", "group", + "loadbalance", "mock", "path", "timeout", "token", "version", "warmup", "weight", "timestamp", "dubbo", + "release", "interface", + } ) type registryProtocol struct { invokers []protocol.Invoker // Registry Map registries *sync.Map - //To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. - //providerurl <--> exporter + // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. + // providerurl <--> exporter bounds *sync.Map overrideListeners *sync.Map serviceConfigurationListeners *sync.Map @@ -65,10 +70,8 @@ func init() { } func getCacheKey(url *common.URL) string { - newUrl := url.Clone() delKeys := gxset.NewSet("dynamic", "enabled") - newUrl.RemoveParams(delKeys) - return newUrl.String() + return url.CloneExceptParams(delKeys).String() } func newRegistryProtocol() *registryProtocol { @@ -87,6 +90,27 @@ func getRegistry(regUrl *common.URL) registry.Registry { return reg } +func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common.URL { + if registryUrl.GetParamBool("simplified", false) { + return providerUrl.CloneWithParams(reserveParams) + } else { + return filterHideKey(providerUrl) + } +} + +// filterHideKey filter the parameters that do not need to be output in url(Starting with .) +func filterHideKey(url *common.URL) *common.URL { + + // be careful params maps in url is map type + removeSet := gxset.NewSet() + for k, _ := range url.GetParams() { + if strings.HasPrefix(k, ".") { + removeSet.Add(k) + } + } + return url.CloneExceptParams(removeSet) +} + func (proto *registryProtocol) initConfigurationListeners() { proto.overrideListeners = &sync.Map{} proto.serviceConfigurationListeners = &sync.Map{} @@ -110,8 +134,8 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { reg = regI.(registry.Registry) } - //new registry directory for store service url from registry - directory, err := directory2.NewRegistryDirectory(®istryUrl, reg) + // new registry directory for store service url from registry + directory, err := extension.GetDefaultRegistryDirectory(®istryUrl, reg) if err != nil { logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!", serviceUrl.String(), err.Error()) @@ -123,9 +147,8 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { logger.Errorf("consumer service %v register registry %v error, error message is %s", serviceUrl.String(), registryUrl.String(), err.Error()) } - go directory.Subscribe(serviceUrl) - //new cluster invoker + // new cluster invoker cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) invoker := cluster.Join(directory) @@ -150,7 +173,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte serviceConfigurationListener.OverrideUrl(providerUrl) var reg registry.Registry - if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { reg = getRegistry(registryUrl) proto.registries.Store(registryUrl.Key(), reg) @@ -158,7 +180,8 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte reg = regI.(registry.Registry) } - err := reg.Register(*providerUrl) + registeredProviderUrl := getUrlToRegistry(providerUrl, registryUrl) + err := reg.Register(*registeredProviderUrl) if err != nil { logger.Errorf("provider service %v register registry %v error, error message is %s", providerUrl.Key(), registryUrl.Key(), err.Error()) @@ -190,7 +213,7 @@ func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common oldExporter.(protocol.Exporter).Unexport() proto.bounds.Delete(key) proto.Export(wrappedNewInvoker) - //TODO: unregister & unsubscribe + // TODO: unregister & unsubscribe } } @@ -273,7 +296,7 @@ func isMatched(providerUrl *common.URL, consumerUrl *common.URL) bool { providerGroup := providerUrl.GetParam(constant.GROUP_KEY, "") providerVersion := providerUrl.GetParam(constant.VERSION_KEY, "") providerClassifier := providerUrl.GetParam(constant.CLASSIFIER_KEY, "") - //todo: public static boolean isContains(String values, String value) { + // todo: public static boolean isContains(String values, String value) { // return isNotEmpty(values) && isContains(COMMA_SPLIT_PATTERN.split(values), value); // } return (consumerGroup == constant.ANY_VALUE || consumerGroup == providerGroup || @@ -326,9 +349,9 @@ func (proto *registryProtocol) Destroy() { } func getRegistryUrl(invoker protocol.Invoker) *common.URL { - //here add * for return a new url + // here add * for return a new url url := invoker.GetUrl() - //if the protocol == registry ,set protocol the registry value in url.params + // if the protocol == registry ,set protocol the registry value in url.params if url.Protocol == constant.REGISTRY_PROTOCOL { protocol := url.GetParam(constant.REGISTRY_KEY, "") url.Protocol = protocol @@ -338,7 +361,7 @@ func getRegistryUrl(invoker protocol.Invoker) *common.URL { func getProviderUrl(invoker protocol.Invoker) *common.URL { url := invoker.GetUrl() - //be careful params maps in url is map type + // be careful params maps in url is map type return url.SubURL.Clone() } @@ -346,12 +369,12 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) { regURL.SubURL = providerURL } -// GetProtocol ... +// GetProtocol return the singleton RegistryProtocol func GetProtocol() protocol.Protocol { - if regProtocol != nil { - return regProtocol - } - return newRegistryProtocol() + once.Do(func() { + regProtocol = newRegistryProtocol() + }) + return regProtocol } type wrappedInvoker struct { diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go index cee2a6a625..15fd3cacfa 100644 --- a/registry/protocol/protocol_test.go +++ b/registry/protocol/protocol_test.go @@ -284,3 +284,12 @@ func TestExportWithApplicationConfig(t *testing.T) { v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl)) assert.NotNil(t, v2) } + +func TestGetProviderUrlWithHideKey(t *testing.T) { + url, _ := common.NewURL("dubbo://127.0.0.1:1111?a=a1&b=b1&.c=c1&.d=d1&e=e1&protocol=registry") + providerUrl := getUrlToRegistry(&url, &url) + assert.NotContains(t, providerUrl.GetParams(), ".c") + assert.NotContains(t, providerUrl.GetParams(), ".d") + assert.Contains(t, providerUrl.GetParams(), "a") + +} diff --git a/registry/service_discovery.go b/registry/service_discovery.go new file mode 100644 index 0000000000..a8228a4abe --- /dev/null +++ b/registry/service_discovery.go @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package registry + +import ( + "fmt" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + gxpage "github.com/dubbogo/gost/page" +) + +const DefaultPageSize = 100 + +type ServiceDiscovery interface { + fmt.Stringer + + // ----------------- lifecycle ------------------- + + // Destroy will destroy the service discovery. + // If the discovery cannot be destroy, it will return an error. + Destroy() error + + // ----------------- registration ---------------- + + // Register will register an instance of ServiceInstance to registry + Register(instance ServiceInstance) error + + // Update will update the data of the instance in registry + Update(instance ServiceInstance) error + + // Unregister will unregister this instance from registry + Unregister(instance ServiceInstance) error + + // ----------------- discovery ------------------- + // GetDefaultPageSize will return the default page size + GetDefaultPageSize() int + + // GetServices will return the all service names. + GetServices() *gxset.HashSet + + // GetInstances will return all service instances with serviceName + GetInstances(serviceName string) []ServiceInstance + + // GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName + // the page will start at offset + GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager + + // GetHealthyInstancesByPage will return a page containing instances of ServiceInstance. + // The param healthy indices that the instance should be healthy or not. + // The page will start at offset + GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager + + // Batch get all instances by the specified service names + GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager + + // ----------------- event ---------------------- + // AddListener adds a new ServiceInstancesChangedListener + // see addServiceInstancesChangedListener in Java + AddListener(listener *ServiceInstancesChangedListener) error + + // DispatchEventByServiceName dispatches the ServiceInstancesChangedEvent to service instance whose name is serviceName + DispatchEventByServiceName(serviceName string) error + + // DispatchEventForInstances dispatches the ServiceInstancesChangedEvent to target instances + DispatchEventForInstances(serviceName string, instances []ServiceInstance) error + + // DispatchEvent dispatches the event + DispatchEvent(event *ServiceInstancesChangedEvent) error +} diff --git a/registry/service_instance.go b/registry/service_instance.go new file mode 100644 index 0000000000..2cc229ee3b --- /dev/null +++ b/registry/service_instance.go @@ -0,0 +1,89 @@ +/* + * 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 registry + +type ServiceInstance interface { + + // GetId will return this instance's id. It should be unique. + GetId() string + + // GetServiceName will return the serviceName + GetServiceName() string + + // GetHost will return the hostname + GetHost() string + + // GetPort will return the port. + GetPort() int + + // IsEnable will return the enable status of this instance + IsEnable() bool + + // IsHealthy will return the value represent the instance whether healthy or not + IsHealthy() bool + + // GetMetadata will return the metadata + GetMetadata() map[string]string +} + +// DefaultServiceInstance the default implementation of ServiceInstance +// or change the ServiceInstance to be struct??? +type DefaultServiceInstance struct { + Id string + ServiceName string + Host string + Port int + Enable bool + Healthy bool + Metadata map[string]string +} + +// GetId will return this instance's id. It should be unique. +func (d *DefaultServiceInstance) GetId() string { + return d.Id +} + +// GetServiceName will return the serviceName +func (d *DefaultServiceInstance) GetServiceName() string { + return d.ServiceName +} + +// GetHost will return the hostname +func (d *DefaultServiceInstance) GetHost() string { + return d.Host +} + +// GetPort will return the port. +func (d *DefaultServiceInstance) GetPort() int { + return d.Port +} + +// IsEnable will return the enable status of this instance +func (d *DefaultServiceInstance) IsEnable() bool { + return d.Enable +} + +// IsHealthy will return the value represent the instance whether healthy or not +func (d *DefaultServiceInstance) IsHealthy() bool { + return d.Healthy +} + +// GetMetadata will return the metadata +func (d *DefaultServiceInstance) GetMetadata() map[string]string { + return d.Metadata +} diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go index bef1760e04..c5b2f33c61 100644 --- a/registry/zookeeper/listener.go +++ b/registry/zookeeper/listener.go @@ -35,23 +35,28 @@ import ( zk "github.com/apache/dubbo-go/remoting/zookeeper" ) -// RegistryDataListener ... +// RegistryDataListener contains all URL information subscribed by zookeeper registry type RegistryDataListener struct { - interestedURL []*common.URL - listener config_center.ConfigurationListener + subscribed map[*common.URL]config_center.ConfigurationListener + mutex sync.Mutex + closed bool } -// NewRegistryDataListener ... -func NewRegistryDataListener(listener config_center.ConfigurationListener) *RegistryDataListener { - return &RegistryDataListener{listener: listener} +// NewRegistryDataListener constructs a new RegistryDataListener +func NewRegistryDataListener() *RegistryDataListener { + return &RegistryDataListener{ + subscribed: make(map[*common.URL]config_center.ConfigurationListener)} } -// AddInterestedURL ... -func (l *RegistryDataListener) AddInterestedURL(url *common.URL) { - l.interestedURL = append(l.interestedURL, url) +// SubscribeURL is used to set a watch listener for url +func (l *RegistryDataListener) SubscribeURL(url *common.URL, listener config_center.ConfigurationListener) { + if l.closed { + return + } + l.subscribed[url] = listener } -// DataChange ... +// DataChange accepts all events sent from the zookeeper server and trigger the corresponding listener for processing func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { // Intercept the last bit index := strings.Index(eventType.Path, "/providers/") @@ -65,10 +70,14 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { logger.Errorf("Listen NewURL(r{%s}) = error{%v} eventType.Path={%v}", url, err, eventType.Path) return false } - - for _, v := range l.interestedURL { - if serviceURL.URLEqual(*v) { - l.listener.Process( + l.mutex.Lock() + defer l.mutex.Unlock() + if l.closed { + return false + } + for url, listener := range l.subscribed { + if serviceURL.URLEqual(*url) { + listener.Process( &config_center.ConfigChangeEvent{ Key: eventType.Path, Value: serviceURL, @@ -81,38 +90,48 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { return false } -// RegistryConfigurationListener ... +// Close all RegistryConfigurationListener in subscribed +func (l *RegistryDataListener) Close() { + l.mutex.Lock() + defer l.mutex.Unlock() + for _, listener := range l.subscribed { + listener.(*RegistryConfigurationListener).Close() + } +} + +// RegistryConfigurationListener represent the processor of zookeeper watcher type RegistryConfigurationListener struct { client *zk.ZookeeperClient registry *zkRegistry events chan *config_center.ConfigChangeEvent isClosed bool + close chan struct{} closeOnce sync.Once } // NewRegistryConfigurationListener for listening the event of zk. func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry) *RegistryConfigurationListener { reg.WaitGroup().Add(1) - return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false} + return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false, close: make(chan struct{}, 1)} } -// Process ... +// Process submit the ConfigChangeEvent to the event chan to notify all observer func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) { l.events <- configType } -// Next ... +// Next will observe the registry state and events chan func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { for { select { case <-l.client.Done(): - logger.Warnf("listener's zk client connection is broken, so zk event listener exit now.") - return nil, perrors.New("listener stopped") - + logger.Warnf("listener's zk client connection (address {%s}) is broken, so zk event listener exit now.", l.client.ZkAddrs) + return nil, perrors.New("zookeeper client stopped") + case <-l.close: + return nil, perrors.New("listener have been closed") case <-l.registry.Done(): - logger.Warnf("zk consumer register has quit, so zk event listener exit now.") - return nil, perrors.New("listener stopped") - + logger.Warnf("zk consumer register has quit, so zk event listener exit now. (registry url {%v}", l.registry.BaseRegistry.URL) + return nil, perrors.New("zookeeper registry, (registry url{%v}) stopped") case e := <-l.events: logger.Debugf("got zk event %s", e) if e.ConfigType == remoting.EventTypeDel && !l.valid() { @@ -127,15 +146,17 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { } } -// Close ... +// Close RegistryConfigurationListener only once func (l *RegistryConfigurationListener) Close() { // ensure that the listener will be closed at most once. l.closeOnce.Do(func() { l.isClosed = true + l.close <- struct{}{} l.registry.WaitGroup().Done() }) } +// valid return the true if the client conn isn't nil func (l *RegistryConfigurationListener) valid() bool { return l.client.ZkConnValid() } diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go index 1a76b29a6f..a0e9147a9e 100644 --- a/registry/zookeeper/listener_test.go +++ b/registry/zookeeper/listener_test.go @@ -32,15 +32,15 @@ import ( ) func Test_DataChange(t *testing.T) { - listener := NewRegistryDataListener(&MockDataListener{}) + listener := NewRegistryDataListener() url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") - listener.AddInterestedURL(&url) + listener.SubscribeURL(&url, &MockConfigurationListener{}) int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"}) assert.Equal(t, true, int) } -type MockDataListener struct { +type MockConfigurationListener struct { } -func (*MockDataListener) Process(configType *config_center.ConfigChangeEvent) { +func (*MockConfigurationListener) Process(configType *config_center.ConfigChangeEvent) { } diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go index e13443d57d..88d5d6221b 100644 --- a/registry/zookeeper/registry.go +++ b/registry/zookeeper/registry.go @@ -20,7 +20,6 @@ package zookeeper import ( "fmt" "net/url" - "strings" "sync" "time" ) @@ -54,12 +53,11 @@ func init() { type zkRegistry struct { registry.BaseRegistry - client *zookeeper.ZookeeperClient - listenerLock sync.Mutex - listener *zookeeper.ZkEventListener - dataListener *RegistryDataListener - configListener *RegistryConfigurationListener - cltLock sync.Mutex + client *zookeeper.ZookeeperClient + listenerLock sync.Mutex + listener *zookeeper.ZkEventListener + dataListener *RegistryDataListener + cltLock sync.Mutex //for provider zkPath map[string]int // key = protocol://ip:port/interface } @@ -78,13 +76,12 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { if err != nil { return nil, err } - r.WaitGroup().Add(1) //zk client start successful, then wg +1 go zookeeper.HandleClientRestart(r) r.listener = zookeeper.NewZkEventListener(r.client) - r.configListener = NewRegistryConfigurationListener(r.client, r) - r.dataListener = NewRegistryDataListener(r.configListener) + + r.dataListener = NewRegistryDataListener() return r, nil } @@ -121,8 +118,27 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust func (r *zkRegistry) InitListeners() { r.listener = zookeeper.NewZkEventListener(r.client) - r.configListener = NewRegistryConfigurationListener(r.client, r) - r.dataListener = NewRegistryDataListener(r.configListener) + newDataListener := NewRegistryDataListener() + // should recover if dataListener isn't nil before + if r.dataListener != nil { + // close all old listener + oldDataListener := r.dataListener + oldDataListener.mutex.Lock() + defer oldDataListener.mutex.Unlock() + recoverd := r.dataListener.subscribed + if recoverd != nil && len(recoverd) > 0 { + // recover all subscribed url + for conf, oldListener := range recoverd { + if regConfigListener, ok := oldListener.(*RegistryConfigurationListener); ok { + regConfigListener.Close() + } + newDataListener.SubscribeURL(conf, NewRegistryConfigurationListener(r.client, r)) + go r.listener.ListenServiceEvent(conf, fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, url.QueryEscape(conf.Service())), newDataListener) + + } + } + } + r.dataListener = newDataListener } func (r *zkRegistry) CreatePath(path string) error { @@ -155,8 +171,8 @@ func (r *zkRegistry) ZkClientLock() *sync.Mutex { } func (r *zkRegistry) CloseListener() { - if r.configListener != nil { - r.configListener.Close() + if r.dataListener != nil { + r.dataListener.Close() } } @@ -173,32 +189,49 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { logger.Errorf("zk.Create(root{%s}) = err{%v}", root, perrors.WithStack(err)) return perrors.WithStack(err) } + + // try to register the node zkPath, err = r.client.RegisterTemp(root, node) if err != nil { - if err == zk.ErrNodeExists { - logger.Warnf("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) - } else { - logger.Errorf("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) + logger.Errorf("Register temp node(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) + if perrors.Cause(err) == zk.ErrNodeExists { + // should delete the old node + logger.Info("Register temp node failed, try to delete the old and recreate (root{%s}, node{%s}) , ignore!", root, node) + if err = r.client.Delete(zkPath); err == nil { + _, err = r.client.RegisterTemp(root, node) + } + if err != nil { + logger.Errorf("Recreate the temp node failed, (root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) + } } return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node) } - logger.Debugf("create a zookeeper node:%s", zkPath) + logger.Debugf("Create a zookeeper node:%s", zkPath) return nil } func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListener, error) { - var ( - zkListener *RegistryConfigurationListener - ) - r.listenerLock.Lock() - if r.configListener.isClosed { - r.listenerLock.Unlock() - return nil, perrors.New("configListener already been closed") + var zkListener *RegistryConfigurationListener + dataListener := r.dataListener + dataListener.mutex.Lock() + defer dataListener.mutex.Unlock() + if r.dataListener.subscribed[conf] != nil { + + zkListener, _ := r.dataListener.subscribed[conf].(*RegistryConfigurationListener) + if zkListener != nil { + r.listenerLock.Lock() + defer r.listenerLock.Unlock() + if zkListener.isClosed { + return nil, perrors.New("configListener already been closed") + } else { + return zkListener, nil + } + } } - zkListener = r.configListener - r.listenerLock.Unlock() + + zkListener = NewRegistryConfigurationListener(r.client, r) if r.listener == nil { r.cltLock.Lock() client := r.client @@ -216,10 +249,9 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen } //Interested register to dataconfig. - r.dataListener.AddInterestedURL(conf) - for _, v := range strings.Split(conf.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") { - go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, url.QueryEscape(conf.Service())), r.dataListener) - } + r.dataListener.SubscribeURL(conf, zkListener) + + go r.listener.ListenServiceEvent(conf, fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, url.QueryEscape(conf.Service())), r.dataListener) return zkListener, nil } diff --git a/remoting/etcdv3/client_test.go b/remoting/etcdv3/client_test.go index 895cc2954a..e37b6383df 100644 --- a/remoting/etcdv3/client_test.go +++ b/remoting/etcdv3/client_test.go @@ -29,11 +29,11 @@ import ( ) import ( + "github.com/coreos/etcd/embed" "github.com/coreos/etcd/mvcc/mvccpb" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" - "go.etcd.io/etcd/embed" "google.golang.org/grpc/connectivity" ) diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index 21486aab59..92ea76046f 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -87,8 +87,6 @@ func StateToString(state zk.State) string { default: return state.String() } - - return "zookeeper unknown state" } // Options ... @@ -111,14 +109,12 @@ func WithZkName(name string) Option { // ValidateZookeeperClient ... func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { - var ( - err error - ) - opions := &Options{} + var err error + options := &Options{} for _, opt := range opts { - opt(opions) + opt(options) } - + connected := false err = nil lock := container.ZkClientLock() @@ -129,20 +125,22 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { if container.ZkClient() == nil { //in dubbo ,every registry only connect one node ,so this is []string{r.Address} - timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) + var timeout time.Duration + timeout, err = time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) if err != nil { logger.Errorf("timeout config %v is invalid ,err is %v", url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), err.Error()) return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) } zkAddresses := strings.Split(url.Location, ",") - newClient, err := newZookeeperClient(opions.zkName, zkAddresses, timeout) + newClient, err := newZookeeperClient(options.zkName, zkAddresses, timeout) if err != nil { logger.Warnf("newZookeeperClient(name{%s}, zk address{%v}, timeout{%d}) = error{%v}", - opions.zkName, url.Location, timeout.String(), err) + options.zkName, url.Location, timeout.String(), err) return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) } container.SetZkClient(newClient) + connected = true } if container.ZkClient().Conn == nil { @@ -150,10 +148,16 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { container.ZkClient().Conn, event, err = zk.Connect(container.ZkClient().ZkAddrs, container.ZkClient().Timeout) if err == nil { container.ZkClient().Wait.Add(1) + connected = true go container.ZkClient().HandleZkEvent(event) } } + if connected { + logger.Info("Connect to zookeeper successfully, name{%s}, zk address{%v}", options.zkName, url.Location) + container.WaitGroup().Add(1) //zk client start successful, then registry wg +1 + } + return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.PrimitiveURL) } @@ -386,14 +390,23 @@ func (z *ZookeeperClient) Close() { z.Conn = nil z.Unlock() if conn != nil { + logger.Warnf("zkClient Conn{name:%s, zk addr:%s} exit now.", z.name, conn.SessionID()) conn.Close() } logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs) } -// Create ... +// Create will create the node recursively, which means that if the parent node is absent, +// it will create parent node first. +// And the value for the basePath is "" func (z *ZookeeperClient) Create(basePath string) error { + return z.CreateWithValue(basePath, []byte("")) +} + +// CreateWithValue will create the node recursively, which means that if the parent node is absent, +// it will create parent node first. +func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { var ( err error tmpPath string @@ -407,7 +420,7 @@ func (z *ZookeeperClient) Create(basePath string) error { conn := z.Conn z.Unlock() if conn != nil { - _, err = conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll)) + _, err = conn.Create(tmpPath, value, 0, zk.WorldACL(zk.PermAll)) } if err != nil { @@ -462,7 +475,7 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er //if err != nil && err != zk.ErrNodeExists { if err != nil { logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err)) - return "", perrors.WithStack(err) + return zkPath, perrors.WithStack(err) } logger.Debugf("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath) diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index 055db4f716..4e3945388f 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -48,11 +48,11 @@ func HandleClientRestart(r zkClientFacade) { failTimes int ) - defer r.WaitGroup().Done() LOOP: for { select { case <-r.Done(): + r.WaitGroup().Done() // dec the wg when registry is closed logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") break LOOP // re-register all services @@ -63,12 +63,14 @@ LOOP: zkAddress := r.ZkClient().ZkAddrs r.SetZkClient(nil) r.ZkClientLock().Unlock() + r.WaitGroup().Done() // dec the wg when zk client is closed // Connect zk until success. failTimes = 0 for { select { case <-r.Done(): + r.WaitGroup().Done() // dec the wg when registry is closed logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") break LOOP case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // Prevent crazy reconnection zk. diff --git a/remoting/zookeeper/facade_test.go b/remoting/zookeeper/facade_test.go index a41f6cd323..01d46da6cc 100644 --- a/remoting/zookeeper/facade_test.go +++ b/remoting/zookeeper/facade_test.go @@ -38,6 +38,16 @@ type mockFacade struct { done chan struct{} } +func newMockFacade(client *ZookeeperClient, url *common.URL) zkClientFacade { + mock := &mockFacade{ + client: client, + URL: url, + } + + mock.wg.Add(1) + return mock +} + func (r *mockFacade) ZkClient() *ZookeeperClient { return r.client } @@ -80,7 +90,7 @@ func Test_Facade(t *testing.T) { assert.NoError(t, err) defer ts.Stop() url, _ := common.NewURL("mock://127.0.0.1") - mock := &mockFacade{client: z, URL: &url} + mock := newMockFacade(z, &url) go HandleClientRestart(mock) states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} verifyEventStateOrder(t, event, states, "event channel") diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index eaf259f441..b3f6e29bf8 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -31,6 +31,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/remoting" @@ -95,8 +96,6 @@ func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener ...remo return false } } - - return false } func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, listener remoting.DataListener) { @@ -173,7 +172,7 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li } } -func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataListener) { +func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listener remoting.DataListener) { defer l.wg.Done() var ( @@ -224,7 +223,16 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi } failTimes = 0 for _, c := range children { - // listen l service node + + // Only need to compare Path when subscribing to provider + if strings.LastIndex(zkPath, constant.PROVIDER_CATEGORY) != -1 { + provider, _ := common.NewURL(c) + if provider.Path != conf.Path { + continue + } + } + + //listen l service node dubboPath := path.Join(zkPath, c) //Save the path to avoid listen repeatedly @@ -232,7 +240,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi _, ok := l.pathMap[dubboPath] l.pathMapLock.Unlock() if ok { - logger.Warnf("@zkPath %s has already been listened.", zkPath) + logger.Warnf("@zkPath %s has already been listened.", dubboPath) continue } @@ -263,7 +271,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi strings.LastIndex(zkPath, constant.CONSUMER_CATEGORY) == -1 { l.wg.Add(1) go func(zkPath string, listener remoting.DataListener) { - l.listenDirEvent(zkPath, listener) + l.listenDirEvent(conf, zkPath, listener) logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath) }(dubboPath, listener) } @@ -291,11 +299,11 @@ func timeSecondDuration(sec int) time.Duration { // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent // | // --------> ListenServiceNodeEvent -func (l *ZkEventListener) ListenServiceEvent(zkPath string, listener remoting.DataListener) { +func (l *ZkEventListener) ListenServiceEvent(conf *common.URL, zkPath string, listener remoting.DataListener) { logger.Infof("listen dubbo path{%s}", zkPath) l.wg.Add(1) go func(zkPath string, listener remoting.DataListener) { - l.listenDirEvent(zkPath, listener) + l.listenDirEvent(conf, zkPath, listener) logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath) }(zkPath, listener) } diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go index 7301cd52c3..ba7d6ba81b 100644 --- a/remoting/zookeeper/listener_test.go +++ b/remoting/zookeeper/listener_test.go @@ -97,7 +97,7 @@ func TestListener(t *testing.T) { go client.HandleZkEvent(event) listener := NewZkEventListener(client) dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait} - listener.ListenServiceEvent("/dubbo", dataListener) + listener.ListenServiceEvent(nil, "/dubbo", dataListener) time.Sleep(1 * time.Second) _, err := client.Conn.Set("/dubbo/dubbo.properties", []byte(changedData), 1) assert.NoError(t, err) diff --git a/test/integrate/dubbo/go-client/Dockerfile b/test/integrate/dubbo/go-client/Dockerfile new file mode 100644 index 0000000000..d48df36dc7 --- /dev/null +++ b/test/integrate/dubbo/go-client/Dockerfile @@ -0,0 +1,36 @@ +# +#Licensed to the Apache Software Foundation (ASF) under one or more +#contributor license agreements. See the NOTICE file distributed with +#this work for additional information regarding copyright ownership. +#The ASF licenses this file to You under the Apache License, Version 2.0 +#(the "License"); you may not use this file except in compliance with +#the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# + +FROM golang + +WORKDIR /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +ENV CONF_CONSUMER_FILE_PATH "client.yml" +ENV APP_LOG_CONF_FILE "log.yml" + +ARG PR_ORIGIN_REPO +ARG PR_ORIGIN_COMMITID + +ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +# update dubbo-go to current commit id +RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' +RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop + +RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +CMD go-client \ No newline at end of file diff --git a/test/integrate/dubbo/go-client/client.go b/test/integrate/dubbo/go-client/client.go new file mode 100644 index 0000000000..4c62674d33 --- /dev/null +++ b/test/integrate/dubbo/go-client/client.go @@ -0,0 +1,70 @@ +/* + * 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 main + +import ( + "context" + "fmt" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "github.com/apache/dubbo-go/filter/filter_impl" + + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" +) + +var ( + survivalTimeout int = 10e9 +) + +func println(format string, args ...interface{}) { + fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...) +} + +// they are necessary: +// export CONF_CONSUMER_FILE_PATH="xxx" +// export APP_LOG_CONF_FILE="xxx" +func main() { + hessian.RegisterPOJO(&User{}) + config.Load() + time.Sleep(3e9) + + println("\n\n\nstart to test dubbo") + + go func() { + select { + case <-time.After(time.Minute): + panic("provider not start after client already running 1min") + } + }() + user := &User{} + err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user) + if err != nil { + panic(err) + } + println("response result: %v\n", user) +} diff --git a/test/integrate/dubbo/go-client/client.yml b/test/integrate/dubbo/go-client/client.yml new file mode 100644 index 0000000000..df44a13da3 --- /dev/null +++ b/test/integrate/dubbo/go-client/client.yml @@ -0,0 +1,61 @@ +# dubbo client yaml configure file + + +check: true +# client +request_timeout : "3s" +# connect timeout +connect_timeout : "3s" + +# application config +application: + organization : "ikurento.com" + name : "BDTService" + module : "dubbogo user-info client" + version : "0.0.1" + owner : "ZX" + environment : "dev" + +registries : + "demoZk": + protocol: "zookeeper" + timeout : "3s" + address: "127.0.0.1:2181" + username: "" + password: "" + + +references: + "UserProvider": + # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册 + registry: "demoZk" + protocol : "dubbo" + interface : "com.ikurento.user.UserProvider" + cluster: "failover" + methods : + - name: "GetUser" + retries: 3 + + +protocol_conf: + dubbo: + reconnect_interval: 0 + connection_number: 2 + heartbeat_period: "5s" + session_timeout: "20s" + pool_size: 64 + pool_ttl: 600 + getty_session_param: + compress_encoding: false + tcp_no_delay: true + tcp_keep_alive: true + keep_alive_period: "120s" + tcp_r_buf_size: 262144 + tcp_w_buf_size: 65536 + pkg_rq_size: 1024 + pkg_wq_size: 512 + tcp_read_timeout: "1s" + tcp_write_timeout: "5s" + wait_timeout: "1s" + max_msg_len: 1024000 + session_name: "client" diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod new file mode 100644 index 0000000000..4708eb1f0f --- /dev/null +++ b/test/integrate/dubbo/go-client/go.mod @@ -0,0 +1,3 @@ +module github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +go 1.13 diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum new file mode 100644 index 0000000000..687fd0f337 --- /dev/null +++ b/test/integrate/dubbo/go-client/go.sum @@ -0,0 +1,389 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Jeffail/gabs v1.1.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= +github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0= +github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/apache/dubbo-go v1.4.1/go.mod h1:hzP9PQkcYFcBUgedttDeimugDNqbmGzh18QQy/vBjnw= +github.com/apache/dubbo-go-hessian2 v1.4.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= +github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= +github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvFTSPp0= +github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= +github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= +github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.0.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk= +github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= +github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/consul v1.5.3/go.mod h1:61E2GJCPEP3oq8La7sfDdWGQ66+Zbxzw5ecOdFD7xIE= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.0/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU= +github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd/go.mod h1:ueUgD9BeIocT7QNuvxSyJyPAM9dfifBcaWmeybb67OY= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ= +github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= +github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= +github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0= +github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= +github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM= +github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= +github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= +github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= +github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= +github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= +github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= +github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI= +k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apimachinery v0.0.0-20180821005732-488889b0007f/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/test/integrate/dubbo/go-client/log.yml b/test/integrate/dubbo/go-client/log.yml new file mode 100644 index 0000000000..59fa4279ad --- /dev/null +++ b/test/integrate/dubbo/go-client/log.yml @@ -0,0 +1,28 @@ + +level: "debug" +development: true +disableCaller: false +disableStacktrace: false +sampling: +encoding: "console" + +# encoder +encoderConfig: + messageKey: "message" + levelKey: "level" + timeKey: "time" + nameKey: "logger" + callerKey: "caller" + stacktraceKey: "stacktrace" + lineEnding: "" + levelEncoder: "capitalColor" + timeEncoder: "iso8601" + durationEncoder: "seconds" + callerEncoder: "short" + nameEncoder: "" + +outputPaths: + - "stderr" +errorOutputPaths: + - "stderr" +initialFields: diff --git a/test/integrate/dubbo/go-client/user.go b/test/integrate/dubbo/go-client/user.go new file mode 100644 index 0000000000..ff4486f079 --- /dev/null +++ b/test/integrate/dubbo/go-client/user.go @@ -0,0 +1,54 @@ +/* + * 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 main + +import ( + "context" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" +) + +var userProvider = new(UserProvider) + +func init() { + config.SetConsumerService(userProvider) + hessian.RegisterPOJO(&User{}) +} + +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { + GetUser func(ctx context.Context, req []interface{}, rsp *User) error +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/test/integrate/dubbo/go-client/version.go b/test/integrate/dubbo/go-client/version.go new file mode 100644 index 0000000000..c6138584f1 --- /dev/null +++ b/test/integrate/dubbo/go-client/version.go @@ -0,0 +1,22 @@ +/* + * 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 main + +var ( + Version = "2.6.0" +) diff --git a/test/integrate/dubbo/go-server/Dockerfile b/test/integrate/dubbo/go-server/Dockerfile new file mode 100644 index 0000000000..c2f2d63462 --- /dev/null +++ b/test/integrate/dubbo/go-server/Dockerfile @@ -0,0 +1,35 @@ +# +#Licensed to the Apache Software Foundation (ASF) under one or more +#contributor license agreements. See the NOTICE file distributed with +#this work for additional information regarding copyright ownership. +#The ASF licenses this file to You under the Apache License, Version 2.0 +#(the "License"); you may not use this file except in compliance with +#the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# + +FROM golang + +WORKDIR /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +ENV CONF_PROVIDER_FILE_PATH "server.yml" +ENV APP_LOG_CONF_FILE "log.yml" + +ARG PR_ORIGIN_REPO +ARG PR_ORIGIN_COMMITID + +ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-server +# update dubbo-go to current commit id +RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' +RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop + +RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +CMD go-server diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod new file mode 100644 index 0000000000..9e1162327d --- /dev/null +++ b/test/integrate/dubbo/go-server/go.mod @@ -0,0 +1,3 @@ +module github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +go 1.13 diff --git a/test/integrate/dubbo/go-server/log.yml b/test/integrate/dubbo/go-server/log.yml new file mode 100644 index 0000000000..59fa4279ad --- /dev/null +++ b/test/integrate/dubbo/go-server/log.yml @@ -0,0 +1,28 @@ + +level: "debug" +development: true +disableCaller: false +disableStacktrace: false +sampling: +encoding: "console" + +# encoder +encoderConfig: + messageKey: "message" + levelKey: "level" + timeKey: "time" + nameKey: "logger" + callerKey: "caller" + stacktraceKey: "stacktrace" + lineEnding: "" + levelEncoder: "capitalColor" + timeEncoder: "iso8601" + durationEncoder: "seconds" + callerEncoder: "short" + nameEncoder: "" + +outputPaths: + - "stderr" +errorOutputPaths: + - "stderr" +initialFields: diff --git a/test/integrate/dubbo/go-server/server.go b/test/integrate/dubbo/go-server/server.go new file mode 100644 index 0000000000..115bf0a4d7 --- /dev/null +++ b/test/integrate/dubbo/go-server/server.go @@ -0,0 +1,56 @@ +/* + * 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 main + +import ( + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/filter_impl" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + _ "github.com/apache/dubbo-go/registry/zookeeper" +) + +var ( + stopC = make(chan struct{}) +) + +// they are necessary: +// export CONF_PROVIDER_FILE_PATH="xxx" +// export APP_LOG_CONF_FILE="xxx" +func main() { + + hessian.RegisterPOJO(&User{}) + config.Load() + + select { + case <-stopC: + // wait getty send resp to consumer + time.Sleep(3*time.Second) + return + case <-time.After(time.Minute): + panic("provider already running 1 min, but can't be call by consumer") + } +} diff --git a/test/integrate/dubbo/go-server/server.yml b/test/integrate/dubbo/go-server/server.yml new file mode 100644 index 0000000000..8a17297b10 --- /dev/null +++ b/test/integrate/dubbo/go-server/server.yml @@ -0,0 +1,57 @@ +# dubbo server yaml configure file + + +# application config +application: + organization : "ikurento.com" + name : "BDTService" + module : "dubbogo user-info server" + version : "0.0.1" + owner : "ZX" + environment : "dev" + +registries : + "demoZk": + protocol: "zookeeper" + timeout : "3s" + address: "127.0.0.1:2181" + +services: + "UserProvider": + # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册 + registry: "demoZk" + protocol : "dubbo" + # 相当于dubbo.xml中的interface + interface : "com.ikurento.user.UserProvider" + loadbalance: "random" + warmup: "100" + cluster: "failover" + methods: + - name: "GetUser" + retries: 1 + loadbalance: "random" + +protocols: + "dubbo": + name: "dubbo" + port: 20000 + + +protocol_conf: + dubbo: + session_number: 700 + session_timeout: "20s" + getty_session_param: + compress_encoding: false + tcp_no_delay: true + tcp_keep_alive: true + keep_alive_period: "120s" + tcp_r_buf_size: 262144 + tcp_w_buf_size: 65536 + pkg_rq_size: 1024 + pkg_wq_size: 512 + tcp_read_timeout: "1s" + tcp_write_timeout: "5s" + wait_timeout: "1s" + max_msg_len: 1024000 + session_name: "server" diff --git a/test/integrate/dubbo/go-server/user.go b/test/integrate/dubbo/go-server/user.go new file mode 100644 index 0000000000..7bff415661 --- /dev/null +++ b/test/integrate/dubbo/go-server/user.go @@ -0,0 +1,65 @@ +/* + * 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 main + +import ( + "context" + "fmt" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" +) + +func init() { + config.SetProviderService(new(UserProvider)) + // ------for hessian2------ + hessian.RegisterPOJO(&User{}) +} + +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) { + println("req:%#v", req) + rsp := User{"A001", "Alex Stocks", 18, time.Now()} + println("rsp:%#v", rsp) + close(stopC) + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} + +func println(format string, args ...interface{}) { + fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...) +} diff --git a/test/integrate/dubbo/go-server/version.go b/test/integrate/dubbo/go-server/version.go new file mode 100644 index 0000000000..c6138584f1 --- /dev/null +++ b/test/integrate/dubbo/go-server/version.go @@ -0,0 +1,22 @@ +/* + * 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 main + +var ( + Version = "2.6.0" +)