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

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


The following commit(s) were added to refs/heads/main by this push:
     new 8efb9ea90 feat: deep copy the configuration when instanceOptions flow 
to ServerOptions/ClientOptions (#2596)
8efb9ea90 is described below

commit 8efb9ea902733e0fd7d947df13e0091c07b820ea
Author: setcy <[email protected]>
AuthorDate: Thu Feb 29 10:26:10 2024 +0800

    feat: deep copy the configuration when instanceOptions flow to 
ServerOptions/ClientOptions (#2596)
    
    fixes #2592
---
 dubbo.go                         |  24 ++--
 dubbo_test.go                    |  98 +++++++++++++
 global/application_config.go     |  19 +++
 global/config_center_config.go   |  27 ++++
 global/config_test.go            | 300 +++++++++++++++++++++++++++++++++++++++
 global/consumer_config.go        |  30 ++++
 global/custom_config.go          |  20 ++-
 global/logger_config.go          |  36 +++++
 global/metadata_report_config.go |  24 ++++
 global/method_config.go          |  23 +++
 global/metric_config.go          | 109 ++++++++++++++
 global/otel_config.go            |  33 +++++
 global/profiles_config.go        |  11 ++
 global/protocol_config.go        |  16 +++
 global/provider_config.go        |  38 +++++
 global/reference_config.go       |  54 +++++++
 global/registry_config.go        |  31 ++++
 global/service_config.go         |  66 +++++++++
 global/shutdown_config.go        |  29 ++++
 global/tls_config.go             |  14 ++
 options.go                       | 106 ++++++++++++++
 21 files changed, 1095 insertions(+), 13 deletions(-)

diff --git a/dubbo.go b/dubbo.go
index b779b78f7..ea9739b81 100644
--- a/dubbo.go
+++ b/dubbo.go
@@ -70,12 +70,12 @@ func (ins *Instance) NewClient(opts ...client.ClientOption) 
(*client.Client, err
        }
 
        var cliOpts []client.ClientOption
-       conCfg := ins.insOpts.Consumer
-       appCfg := ins.insOpts.Application
-       regsCfg := ins.insOpts.Registries
-       sdCfg := ins.insOpts.Shutdown
-       metricsCfg := ins.insOpts.Metrics
-       otelCfg := ins.insOpts.Otel
+       conCfg := ins.insOpts.CloneConsumer()
+       appCfg := ins.insOpts.CloneApplication()
+       regsCfg := ins.insOpts.CloneRegistries()
+       sdCfg := ins.insOpts.CloneShutdown()
+       metricsCfg := ins.insOpts.CloneMetrics()
+       otelCfg := ins.insOpts.CloneOtel()
 
        if conCfg != nil {
                if conCfg.Check {
@@ -125,12 +125,12 @@ func (ins *Instance) NewServer(opts 
...server.ServerOption) (*server.Server, err
        }
 
        var srvOpts []server.ServerOption
-       appCfg := ins.insOpts.Application
-       regsCfg := ins.insOpts.Registries
-       prosCfg := ins.insOpts.Protocols
-       sdCfg := ins.insOpts.Shutdown
-       metricsCfg := ins.insOpts.Metrics
-       otelCfg := ins.insOpts.Otel
+       appCfg := ins.insOpts.CloneApplication()
+       regsCfg := ins.insOpts.CloneRegistries()
+       prosCfg := ins.insOpts.CloneProtocols()
+       sdCfg := ins.insOpts.CloneShutdown()
+       metricsCfg := ins.insOpts.CloneMetrics()
+       otelCfg := ins.insOpts.CloneOtel()
 
        if appCfg != nil {
                srvOpts = append(srvOpts,
diff --git a/dubbo_test.go b/dubbo_test.go
new file mode 100644
index 000000000..e23c97394
--- /dev/null
+++ b/dubbo_test.go
@@ -0,0 +1,98 @@
+/*
+ * 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 dubbo
+
+import (
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/client"
+       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/registry"
+       "dubbo.apache.org/dubbo-go/v3/server"
+)
+
+// TestIndependentConfig tests the configurations of the `instance`, `client`, 
and `server` are independent.
+func TestIndependentConfig(t *testing.T) {
+       // instance configuration
+       ins, err := NewInstance(
+               WithName("dubbo_test"),
+               WithRegistry(
+                       registry.WithZookeeper(),
+                       registry.WithAddress("127.0.0.1:2181"),
+               ),
+       )
+       if err != nil {
+               panic(err)
+       }
+
+       // client configuration, ensure that the `instance` configuration can 
be passed to the `client`.
+       _, err = ins.NewClient(
+               func(options *client.ClientOptions) {
+                       assert.Equal(t, "dubbo_test", options.Application.Name)
+                       options.Application.Name = "dubbo_test_client"
+                       assert.Equal(t, "dubbo_test_client", 
options.Application.Name)
+
+                       assert.Equal(t, "127.0.0.1:2181", 
options.Registries[constant.ZookeeperKey].Address)
+                       options.Registries[constant.ZookeeperKey].Address = 
"127.0.0.1:2182"
+                       assert.Equal(t, "127.0.0.1:2182", 
options.Registries[constant.ZookeeperKey].Address)
+               },
+       )
+       if err != nil {
+               panic(err)
+       }
+
+       // server configuration, ensure that the `instance` configuration can 
be passed to the `server`, and the
+       // `instance` configuration is not affected by the `client` 
configuration.
+       _, err = ins.NewServer(
+               func(options *server.ServerOptions) {
+                       assert.Equal(t, "dubbo_test", options.Application.Name)
+                       options.Application.Name = "dubbo_test_server"
+                       assert.Equal(t, "dubbo_test_server", 
options.Application.Name)
+
+                       assert.Equal(t, "127.0.0.1:2181", 
options.Registries[constant.ZookeeperKey].Address)
+                       options.Registries[constant.ZookeeperKey].Address = 
"127.0.0.1:2183"
+                       assert.Equal(t, "127.0.0.1:2183", 
options.Registries[constant.ZookeeperKey].Address)
+               },
+       )
+       if err != nil {
+               panic(err)
+       }
+
+       // check client configuration again, ensure that the `instance` and 
`client` configuration is not affected
+       // by the `server` configuration.
+       _, err = ins.NewClient(
+               func(options *client.ClientOptions) {
+                       assert.Equal(t, "dubbo_test", options.Application.Name)
+                       options.Application.Name = "dubbo_test_client"
+                       assert.Equal(t, "dubbo_test_client", 
options.Application.Name)
+
+                       assert.Equal(t, "127.0.0.1:2181", 
options.Registries[constant.ZookeeperKey].Address)
+                       options.Registries[constant.ZookeeperKey].Address = 
"127.0.0.1:2184"
+                       assert.Equal(t, "127.0.0.1:2184", 
options.Registries[constant.ZookeeperKey].Address)
+               },
+       )
+       if err != nil {
+               panic(err)
+       }
+}
diff --git a/global/application_config.go b/global/application_config.go
index 426378caf..0eab19d8e 100644
--- a/global/application_config.go
+++ b/global/application_config.go
@@ -35,3 +35,22 @@ func DefaultApplicationConfig() *ApplicationConfig {
        // return a new config without setting any field means there is not any 
default value for initialization
        return &ApplicationConfig{}
 }
+
+// Clone a new ApplicationConfig
+func (c *ApplicationConfig) Clone() *ApplicationConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &ApplicationConfig{
+               Organization: c.Organization,
+               Name:         c.Name,
+               Module:       c.Module,
+               Group:        c.Group,
+               Version:      c.Version,
+               Owner:        c.Owner,
+               Environment:  c.Environment,
+               MetadataType: c.MetadataType,
+               Tag:          c.Tag,
+       }
+}
diff --git a/global/config_center_config.go b/global/config_center_config.go
index 75c58fb99..debb0676d 100644
--- a/global/config_center_config.go
+++ b/global/config_center_config.go
@@ -46,3 +46,30 @@ func DefaultCenterConfig() *CenterConfig {
                Params: make(map[string]string),
        }
 }
+
+// Clone a new CenterConfig
+func (c *CenterConfig) Clone() *CenterConfig {
+       if c == nil {
+               return nil
+       }
+
+       newParams := make(map[string]string, len(c.Params))
+       for k, v := range c.Params {
+               newParams[k] = v
+       }
+
+       return &CenterConfig{
+               Protocol:      c.Protocol,
+               Address:       c.Address,
+               DataId:        c.DataId,
+               Cluster:       c.Cluster,
+               Group:         c.Group,
+               Username:      c.Username,
+               Password:      c.Password,
+               Namespace:     c.Namespace,
+               AppID:         c.AppID,
+               Timeout:       c.Timeout,
+               Params:        newParams,
+               FileExtension: c.FileExtension,
+       }
+}
diff --git a/global/config_test.go b/global/config_test.go
new file mode 100644
index 000000000..6b466abb4
--- /dev/null
+++ b/global/config_test.go
@@ -0,0 +1,300 @@
+/*
+ * 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 global
+
+import (
+       "reflect"
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+// TestCloneConfig tests the Clone method of the *_config.go files
+func TestCloneConfig(t *testing.T) {
+       t.Run("ApplicationConfig", func(t *testing.T) {
+               c := DefaultApplicationConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ConfigCenterConfig", func(t *testing.T) {
+               c := DefaultCenterConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ConsumerConfig", func(t *testing.T) {
+               c := DefaultConsumerConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("CustomConfig", func(t *testing.T) {
+               c := DefaultCustomConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("LoggerConfig", func(t *testing.T) {
+               c := DefaultLoggerConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("MetadataReportConfig", func(t *testing.T) {
+               c := DefaultMetadataReportConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("MethodConfig", func(t *testing.T) {
+               c := &MethodConfig{}
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("MetricConfig", func(t *testing.T) {
+               c := DefaultMetricsConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+
+               c2 := &AggregateConfig{}
+               InitCheckCompleteInequality(t, c2)
+               clone2 := c2.Clone()
+               CheckCompleteInequality(t, c2, clone2)
+
+               c3 := &PrometheusConfig{}
+               InitCheckCompleteInequality(t, c3)
+               clone3 := c3.Clone()
+               CheckCompleteInequality(t, c3, clone3)
+
+               c4 := &Exporter{}
+               InitCheckCompleteInequality(t, c4)
+               clone4 := c4.Clone()
+               CheckCompleteInequality(t, c4, clone4)
+
+               c5 := &PushgatewayConfig{}
+               InitCheckCompleteInequality(t, c5)
+               clone5 := c5.Clone()
+               CheckCompleteInequality(t, c5, clone5)
+       })
+
+       t.Run("OtelConfig", func(t *testing.T) {
+               c := DefaultOtelConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+
+               c2 := &OtelTraceConfig{}
+               InitCheckCompleteInequality(t, c2)
+               clone2 := c2.Clone()
+               CheckCompleteInequality(t, c2, clone2)
+       })
+
+       t.Run("ProfilesConfig", func(t *testing.T) {
+               c := DefaultProfilesConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ProtocolConfig", func(t *testing.T) {
+               c := DefaultProtocolConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ProviderConfig", func(t *testing.T) {
+               c := DefaultProviderConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ReferenceConfig", func(t *testing.T) {
+               c := DefaultReferenceConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("RegistryConfig", func(t *testing.T) {
+               c := DefaultRegistryConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ServiceConfig", func(t *testing.T) {
+               c := DefaultServiceConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("ShutdownConfig", func(t *testing.T) {
+               c := DefaultShutdownConfig()
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+
+       t.Run("TLSConfig", func(t *testing.T) {
+               c := &TLSConfig{}
+               InitCheckCompleteInequality(t, c)
+               clone := c.Clone()
+               CheckCompleteInequality(t, c, clone)
+       })
+}
+
+func InitCheckCompleteInequality(t *testing.T, origin interface{}) {
+       if origin == nil {
+               t.Errorf("Invalid parameters")
+               return
+       }
+
+       originValue := reflect.ValueOf(origin).Elem()
+
+       if originValue.Kind() != reflect.Struct {
+               t.Errorf("Parameters should be struct types.")
+               return
+       }
+
+       for i := 0; i < originValue.NumField(); i++ {
+               originField := originValue.Field(i)
+
+               if !originField.CanSet() {
+                       // Field '%s' is unexported, skipping checking
+                       continue
+               }
+
+               switch originField.Kind() {
+               case reflect.String:
+                       originField.SetString("origin")
+
+               case reflect.Bool:
+                       originField.SetBool(true)
+
+               case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+                       originField.SetInt(1)
+
+               case reflect.Uint, reflect.Uint8, reflect.Uint16, 
reflect.Uint32, reflect.Uint64:
+                       originField.SetUint(1)
+
+               case reflect.Float32, reflect.Float64:
+                       originField.SetFloat(1.5)
+
+               case reflect.Map:
+                       originField.Set(reflect.MakeMap(originField.Type()))
+
+               case reflect.Slice, reflect.Ptr:
+
+               default:
+                       // Field '%s' is of unsupported type '%s', skipping 
checking
+               }
+       }
+}
+
+func CheckCompleteInequality(t *testing.T, origin interface{}, clone 
interface{}) {
+       if origin == nil || clone == nil {
+               t.Errorf("Invalid parameters")
+               return
+       }
+
+       assert.NotSame(t, origin, clone)
+
+       originValue := reflect.ValueOf(origin).Elem()
+       cloneValue := reflect.ValueOf(clone).Elem()
+
+       if originValue.Kind() != reflect.Struct || cloneValue.Kind() != 
reflect.Struct {
+               t.Errorf("Both parameters should be struct types.")
+               return
+       }
+
+       if originValue.Type() != cloneValue.Type() {
+               t.Errorf("Parameters should be of the same type.")
+               return
+       }
+
+       for i := 0; i < originValue.NumField(); i++ {
+               originField := originValue.Field(i)
+               cloneField := cloneValue.Field(i)
+
+               if !originField.CanSet() {
+                       t.Logf("Field '%s' is unexported, skipping checking", 
originValue.Type().Field(i).Name)
+                       continue
+               }
+
+               switch originField.Kind() {
+               case reflect.String:
+                       assert.Equal(t, "origin", cloneField.String())
+                       cloneField.SetString("clone")
+                       assert.Equal(t, "clone", cloneField.String())
+                       assert.Equal(t, "origin", originField.String())
+
+               case reflect.Bool:
+                       assert.Equal(t, true, cloneField.Bool())
+                       cloneField.SetBool(false)
+                       assert.Equal(t, false, cloneField.Bool())
+                       assert.Equal(t, true, originField.Bool())
+
+               case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+                       assert.EqualValues(t, 1, cloneField.Int())
+                       cloneField.SetInt(2)
+                       assert.EqualValues(t, 2, cloneField.Int())
+                       assert.EqualValues(t, 1, originField.Int())
+
+               case reflect.Uint, reflect.Uint8, reflect.Uint16, 
reflect.Uint32, reflect.Uint64:
+                       assert.EqualValues(t, 1, cloneField.Uint())
+                       cloneField.SetUint(2)
+                       assert.EqualValues(t, 2, cloneField.Uint())
+                       assert.EqualValues(t, 1, originField.Uint())
+
+               case reflect.Float32, reflect.Float64:
+                       assert.EqualValues(t, 1.5, cloneField.Float())
+                       cloneField.SetFloat(2.5)
+                       assert.EqualValues(t, 2.5, cloneField.Float())
+                       assert.EqualValues(t, 1.5, originField.Float())
+
+               case reflect.Map, reflect.Ptr:
+                       if originField.IsNil() {
+                               assert.Zero(t, cloneField.Pointer())
+                       } else {
+                               assert.NotZero(t, cloneField.Pointer())
+                               assert.NotEqual(t, originField.Pointer(), 
cloneField.Pointer())
+                       }
+
+               case reflect.Slice:
+                       assert.NotEqual(t, originField.Pointer(), 
cloneField.Pointer())
+
+               default:
+                       t.Logf("Field '%s' is of unsupported type '%s', 
skipping checking", originValue.Type().Field(i).Name, originField.Kind())
+               }
+       }
+}
diff --git a/global/consumer_config.go b/global/consumer_config.go
index ee7c21e02..7c3afaac3 100644
--- a/global/consumer_config.go
+++ b/global/consumer_config.go
@@ -40,3 +40,33 @@ func DefaultConsumerConfig() *ConsumerConfig {
                References:     make(map[string]*ReferenceConfig),
        }
 }
+
+// Clone a new ConsumerConfig
+func (c *ConsumerConfig) Clone() *ConsumerConfig {
+       if c == nil {
+               return nil
+       }
+
+       newRegistryIDs := make([]string, len(c.RegistryIDs))
+       copy(newRegistryIDs, c.RegistryIDs)
+
+       newReferences := make(map[string]*ReferenceConfig, len(c.References))
+       for k, v := range c.References {
+               newReferences[k] = v.Clone()
+       }
+
+       return &ConsumerConfig{
+               Filter:                         c.Filter,
+               RegistryIDs:                    newRegistryIDs,
+               Protocol:                       c.Protocol,
+               RequestTimeout:                 c.RequestTimeout,
+               ProxyFactory:                   c.ProxyFactory,
+               Check:                          c.Check,
+               AdaptiveService:                c.AdaptiveService,
+               References:                     newReferences,
+               TracingKey:                     c.TracingKey,
+               FilterConf:                     c.FilterConf,
+               MaxWaitTimeForServiceDiscovery: 
c.MaxWaitTimeForServiceDiscovery,
+               MeshEnabled:                    c.MeshEnabled,
+       }
+}
diff --git a/global/custom_config.go b/global/custom_config.go
index 0e418a779..107f0207c 100644
--- a/global/custom_config.go
+++ b/global/custom_config.go
@@ -28,5 +28,23 @@ type CustomConfig struct {
 }
 
 func DefaultCustomConfig() *CustomConfig {
-       return &CustomConfig{}
+       return &CustomConfig{
+               ConfigMap: make(map[string]interface{}),
+       }
+}
+
+// Clone a new CustomConfig
+func (c *CustomConfig) Clone() *CustomConfig {
+       if c == nil {
+               return nil
+       }
+
+       newConfigMap := make(map[string]interface{}, len(c.ConfigMap))
+       for k, v := range c.ConfigMap {
+               newConfigMap[k] = v
+       }
+
+       return &CustomConfig{
+               ConfigMap: newConfigMap,
+       }
 }
diff --git a/global/logger_config.go b/global/logger_config.go
index 0f94a3dcd..32315d6fd 100644
--- a/global/logger_config.go
+++ b/global/logger_config.go
@@ -63,3 +63,39 @@ func DefaultLoggerConfig() *LoggerConfig {
 
        return cfg
 }
+
+// Clone a new LoggerConfig
+func (c *LoggerConfig) Clone() *LoggerConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &LoggerConfig{
+               Driver:   c.Driver,
+               Level:    c.Level,
+               Format:   c.Format,
+               Appender: c.Appender,
+               File:     c.File.Clone(),
+       }
+}
+
+// Clone a new File
+func (f *File) Clone() *File {
+       if f == nil {
+               return nil
+       }
+
+       var newCompress *bool
+       if f.Compress != nil {
+               newCompress = new(bool)
+               *newCompress = *f.Compress
+       }
+
+       return &File{
+               Name:       f.Name,
+               MaxSize:    f.MaxSize,
+               MaxBackups: f.MaxBackups,
+               MaxAge:     f.MaxAge,
+               Compress:   f.Compress,
+       }
+}
diff --git a/global/metadata_report_config.go b/global/metadata_report_config.go
index bac446e84..bf54fc16f 100644
--- a/global/metadata_report_config.go
+++ b/global/metadata_report_config.go
@@ -35,3 +35,27 @@ func DefaultMetadataReportConfig() *MetadataReportConfig {
        // return a new config without setting any field means there is not any 
default value for initialization
        return &MetadataReportConfig{Params: map[string]string{}}
 }
+
+// Clone a new MetadataReportConfig
+func (c *MetadataReportConfig) Clone() *MetadataReportConfig {
+       if c == nil {
+               return nil
+       }
+
+       newParams := make(map[string]string, len(c.Params))
+       for k, v := range c.Params {
+               newParams[k] = v
+       }
+
+       return &MetadataReportConfig{
+               Protocol:     c.Protocol,
+               Address:      c.Address,
+               Username:     c.Username,
+               Password:     c.Password,
+               Timeout:      c.Timeout,
+               Group:        c.Group,
+               Namespace:    c.Namespace,
+               Params:       newParams,
+               metadataType: c.metadataType,
+       }
+}
diff --git a/global/method_config.go b/global/method_config.go
index 08630745a..e22ae0fbb 100644
--- a/global/method_config.go
+++ b/global/method_config.go
@@ -33,3 +33,26 @@ type MethodConfig struct {
        Sticky                      bool   `yaml:"sticky"   
json:"sticky,omitempty" property:"sticky"`
        RequestTimeout              string `yaml:"timeout"  
json:"timeout,omitempty" property:"timeout"`
 }
+
+// Clone a new MethodConfig
+func (c *MethodConfig) Clone() *MethodConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &MethodConfig{
+               InterfaceId:                 c.InterfaceId,
+               InterfaceName:               c.InterfaceName,
+               Name:                        c.Name,
+               Retries:                     c.Retries,
+               LoadBalance:                 c.LoadBalance,
+               Weight:                      c.Weight,
+               TpsLimitInterval:            c.TpsLimitInterval,
+               TpsLimitRate:                c.TpsLimitRate,
+               TpsLimitStrategy:            c.TpsLimitStrategy,
+               ExecuteLimit:                c.ExecuteLimit,
+               ExecuteLimitRejectedHandler: c.ExecuteLimitRejectedHandler,
+               Sticky:                      c.Sticky,
+               RequestTimeout:              c.RequestTimeout,
+       }
+}
diff --git a/global/metric_config.go b/global/metric_config.go
index 44f4c6b39..88f296463 100644
--- a/global/metric_config.go
+++ b/global/metric_config.go
@@ -60,10 +60,119 @@ func DefaultMetricsConfig() *MetricsConfig {
        return &MetricsConfig{Prometheus: defaultPrometheusConfig(), 
Aggregation: defaultAggregateConfig()}
 }
 
+// Clone a new MetricsConfig
+func (c *MetricsConfig) Clone() *MetricsConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newEnable *bool
+       if c.Enable != nil {
+               newEnable = new(bool)
+               *newEnable = *c.Enable
+       }
+
+       var newEnableMetadata *bool
+       if c.EnableMetadata != nil {
+               newEnableMetadata = new(bool)
+               *newEnableMetadata = *c.EnableMetadata
+       }
+
+       var newEnableRegistry *bool
+       if c.EnableRegistry != nil {
+               newEnableRegistry = new(bool)
+               *newEnableRegistry = *c.EnableRegistry
+       }
+
+       var newEnableConfigCenter *bool
+       if c.EnableConfigCenter != nil {
+               newEnableConfigCenter = new(bool)
+               *newEnableConfigCenter = *c.EnableConfigCenter
+       }
+
+       return &MetricsConfig{
+               Enable:             newEnable,
+               Port:               c.Port,
+               Path:               c.Path,
+               Protocol:           c.Protocol,
+               Prometheus:         c.Prometheus.Clone(),
+               Aggregation:        c.Aggregation.Clone(),
+               EnableMetadata:     newEnableMetadata,
+               EnableRegistry:     newEnableRegistry,
+               EnableConfigCenter: newEnableConfigCenter,
+       }
+}
+
 func defaultPrometheusConfig() *PrometheusConfig {
        return &PrometheusConfig{Exporter: &Exporter{}, Pushgateway: 
&PushgatewayConfig{}}
 }
 
+func (c *PrometheusConfig) Clone() *PrometheusConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &PrometheusConfig{
+               Exporter:    c.Exporter.Clone(),
+               Pushgateway: c.Pushgateway.Clone(),
+       }
+}
+
+func (c *Exporter) Clone() *Exporter {
+       if c == nil {
+               return nil
+       }
+
+       var newEnabled *bool
+       if c.Enabled != nil {
+               newEnabled = new(bool)
+               *newEnabled = *c.Enabled
+       }
+
+       return &Exporter{
+               Enabled: newEnabled,
+       }
+}
+
+func (c *PushgatewayConfig) Clone() *PushgatewayConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newEnabled *bool
+       if c.Enabled != nil {
+               newEnabled = new(bool)
+               *newEnabled = *c.Enabled
+       }
+
+       return &PushgatewayConfig{
+               Enabled:      newEnabled,
+               BaseUrl:      c.BaseUrl,
+               Job:          c.Job,
+               Username:     c.Username,
+               Password:     c.Password,
+               PushInterval: c.PushInterval,
+       }
+}
+
 func defaultAggregateConfig() *AggregateConfig {
        return &AggregateConfig{}
 }
+
+func (c *AggregateConfig) Clone() *AggregateConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newEnabled *bool
+       if c.Enabled != nil {
+               newEnabled = new(bool)
+               *newEnabled = *c.Enabled
+       }
+
+       return &AggregateConfig{
+               Enabled:           newEnabled,
+               BucketNum:         c.BucketNum,
+               TimeWindowSeconds: c.TimeWindowSeconds,
+       }
+}
diff --git a/global/otel_config.go b/global/otel_config.go
index f41f8e773..fd4bbf243 100644
--- a/global/otel_config.go
+++ b/global/otel_config.go
@@ -36,3 +36,36 @@ func DefaultOtelConfig() *OtelConfig {
                TracingConfig: &OtelTraceConfig{},
        }
 }
+
+// Clone a new OtelConfig
+func (c *OtelConfig) Clone() *OtelConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &OtelConfig{
+               TracingConfig: c.TracingConfig.Clone(),
+       }
+}
+
+// Clone a new OtelTraceConfig
+func (c *OtelTraceConfig) Clone() *OtelTraceConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newEnable *bool
+       if c.Enable != nil {
+               newEnable = new(bool)
+               *newEnable = *c.Enable
+       }
+
+       return &OtelTraceConfig{
+               Enable:      newEnable,
+               Exporter:    c.Exporter,
+               Endpoint:    c.Endpoint,
+               Propagator:  c.Propagator,
+               SampleMode:  c.SampleMode,
+               SampleRatio: c.SampleRatio,
+       }
+}
diff --git a/global/profiles_config.go b/global/profiles_config.go
index d829dd836..77355788c 100644
--- a/global/profiles_config.go
+++ b/global/profiles_config.go
@@ -26,6 +26,17 @@ func DefaultProfilesConfig() *ProfilesConfig {
        return &ProfilesConfig{}
 }
 
+// Clone a new ProfilesConfig
+func (c *ProfilesConfig) Clone() *ProfilesConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &ProfilesConfig{
+               Active: c.Active,
+       }
+}
+
 type ProfilesOption func(*ProfilesConfig)
 
 func WithProfiles_Active(active string) ProfilesOption {
diff --git a/global/protocol_config.go b/global/protocol_config.go
index ebfa3dd5e..e2fff4cbd 100644
--- a/global/protocol_config.go
+++ b/global/protocol_config.go
@@ -34,3 +34,19 @@ type ProtocolConfig struct {
 func DefaultProtocolConfig() *ProtocolConfig {
        return &ProtocolConfig{}
 }
+
+// Clone a new ProtocolConfig
+func (c *ProtocolConfig) Clone() *ProtocolConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &ProtocolConfig{
+               Name:                 c.Name,
+               Ip:                   c.Ip,
+               Port:                 c.Port,
+               Params:               c.Params,
+               MaxServerSendMsgSize: c.MaxServerSendMsgSize,
+               MaxServerRecvMsgSize: c.MaxServerRecvMsgSize,
+       }
+}
diff --git a/global/provider_config.go b/global/provider_config.go
index 3d7e30dd1..d5fd12985 100644
--- a/global/provider_config.go
+++ b/global/provider_config.go
@@ -47,3 +47,41 @@ func DefaultProviderConfig() *ProviderConfig {
                Services:    make(map[string]*ServiceConfig),
        }
 }
+
+// Clone a new ProviderConfig
+func (c *ProviderConfig) Clone() *ProviderConfig {
+       if c == nil {
+               return nil
+       }
+
+       newRegistryIDs := make([]string, len(c.RegistryIDs))
+       copy(newRegistryIDs, c.RegistryIDs)
+
+       newProtocolIDs := make([]string, len(c.ProtocolIDs))
+       copy(newProtocolIDs, c.ProtocolIDs)
+
+       newServices := make(map[string]*ServiceConfig, len(c.Services))
+       for k, v := range c.Services {
+               newServices[k] = v.Clone()
+       }
+
+       newConfigType := make(map[string]string, len(c.ConfigType))
+       for k, v := range c.ConfigType {
+               newConfigType[k] = v
+       }
+
+       return &ProviderConfig{
+               ServiceConfig:          *c.ServiceConfig.Clone(),
+               Filter:                 c.Filter,
+               Register:               c.Register,
+               RegistryIDs:            newRegistryIDs,
+               ProtocolIDs:            newProtocolIDs,
+               TracingKey:             c.TracingKey,
+               Services:               newServices,
+               ProxyFactory:           c.ProxyFactory,
+               FilterConf:             c.FilterConf,
+               ConfigType:             newConfigType,
+               AdaptiveService:        c.AdaptiveService,
+               AdaptiveServiceVerbose: c.AdaptiveServiceVerbose,
+       }
+}
diff --git a/global/reference_config.go b/global/reference_config.go
index 5acb6427f..1c24b61dc 100644
--- a/global/reference_config.go
+++ b/global/reference_config.go
@@ -56,6 +56,60 @@ func DefaultReferenceConfig() *ReferenceConfig {
        }
 }
 
+// Clone a new ReferenceConfig
+func (c *ReferenceConfig) Clone() *ReferenceConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newCheck *bool
+       if c.Check != nil {
+               check := *c.Check
+               newCheck = &check
+       }
+
+       newRegistryIDs := make([]string, len(c.RegistryIDs))
+       copy(newRegistryIDs, c.RegistryIDs)
+
+       var newMethods []*MethodConfig
+       if c.Methods != nil {
+               newMethods = make([]*MethodConfig, 0, len(c.Methods))
+               for _, method := range c.Methods {
+                       newMethods = append(newMethods, method.Clone())
+               }
+       }
+
+       newParams := make(map[string]string, len(c.Params))
+       for k, v := range c.Params {
+               newParams[k] = v
+       }
+
+       return &ReferenceConfig{
+               InterfaceName:    c.InterfaceName,
+               Check:            newCheck,
+               URL:              c.URL,
+               Filter:           c.Filter,
+               Protocol:         c.Protocol,
+               RegistryIDs:      newRegistryIDs,
+               Cluster:          c.Cluster,
+               Loadbalance:      c.Loadbalance,
+               Retries:          c.Retries,
+               Group:            c.Group,
+               Version:          c.Version,
+               Serialization:    c.Serialization,
+               ProvidedBy:       c.ProvidedBy,
+               Methods:          newMethods,
+               Async:            c.Async,
+               Params:           newParams,
+               Generic:          c.Generic,
+               Sticky:           c.Sticky,
+               RequestTimeout:   c.RequestTimeout,
+               ForceTag:         c.ForceTag,
+               TracingKey:       c.TracingKey,
+               MeshProviderPort: c.MeshProviderPort,
+       }
+}
+
 type ReferenceOption func(*ReferenceConfig)
 
 func WithReference_InterfaceName(name string) ReferenceOption {
diff --git a/global/registry_config.go b/global/registry_config.go
index 783dccd2b..0b6de89f1 100644
--- a/global/registry_config.go
+++ b/global/registry_config.go
@@ -44,3 +44,34 @@ type RegistryConfig struct {
 func DefaultRegistryConfig() *RegistryConfig {
        return &RegistryConfig{}
 }
+
+// Clone a new RegistryConfig
+func (c *RegistryConfig) Clone() *RegistryConfig {
+       if c == nil {
+               return nil
+       }
+
+       newParams := make(map[string]string, len(c.Params))
+       for k, v := range c.Params {
+               newParams[k] = v
+       }
+
+       return &RegistryConfig{
+               Protocol:          c.Protocol,
+               Timeout:           c.Timeout,
+               Group:             c.Group,
+               Namespace:         c.Namespace,
+               TTL:               c.TTL,
+               Address:           c.Address,
+               Username:          c.Username,
+               Password:          c.Password,
+               Simplified:        c.Simplified,
+               Preferred:         c.Preferred,
+               Zone:              c.Zone,
+               Weight:            c.Weight,
+               Params:            newParams,
+               RegistryType:      c.RegistryType,
+               UseAsMetaReport:   c.UseAsMetaReport,
+               UseAsConfigCenter: c.UseAsConfigCenter,
+       }
+}
diff --git a/global/service_config.go b/global/service_config.go
index 8fd3a3c3e..b1c097af0 100644
--- a/global/service_config.go
+++ b/global/service_config.go
@@ -62,3 +62,69 @@ func DefaultServiceConfig() *ServiceConfig {
                RCRegistriesMap: make(map[string]*RegistryConfig),
        }
 }
+
+// Clone a new ServiceConfig
+func (c *ServiceConfig) Clone() *ServiceConfig {
+       if c == nil {
+               return nil
+       }
+
+       newProtocolIDs := make([]string, len(c.ProtocolIDs))
+       copy(newProtocolIDs, c.ProtocolIDs)
+
+       newRegistryIDs := make([]string, len(c.RegistryIDs))
+       copy(newRegistryIDs, c.RegistryIDs)
+
+       newMethods := make([]*MethodConfig, len(c.Methods))
+       for i, v := range c.Methods {
+               newMethods[i] = v.Clone()
+       }
+
+       newParams := make(map[string]string, len(c.Params))
+       for k, v := range c.Params {
+               newParams[k] = v
+       }
+
+       newRCProtocolsMap := make(map[string]*ProtocolConfig, 
len(c.RCProtocolsMap))
+       for k, v := range c.RCProtocolsMap {
+               newRCProtocolsMap[k] = v.Clone()
+       }
+
+       newRCRegistriesMap := make(map[string]*RegistryConfig, 
len(c.RCRegistriesMap))
+       for k, v := range c.RCRegistriesMap {
+               newRCRegistriesMap[k] = v.Clone()
+       }
+
+       return &ServiceConfig{
+               Filter:                      c.Filter,
+               ProtocolIDs:                 newProtocolIDs,
+               Interface:                   c.Interface,
+               RegistryIDs:                 newRegistryIDs,
+               Cluster:                     c.Cluster,
+               Loadbalance:                 c.Loadbalance,
+               Group:                       c.Group,
+               Version:                     c.Version,
+               Methods:                     newMethods,
+               Warmup:                      c.Warmup,
+               Retries:                     c.Retries,
+               Serialization:               c.Serialization,
+               Params:                      newParams,
+               Token:                       c.Token,
+               AccessLog:                   c.AccessLog,
+               TpsLimiter:                  c.TpsLimiter,
+               TpsLimitInterval:            c.TpsLimitInterval,
+               TpsLimitRate:                c.TpsLimitRate,
+               TpsLimitStrategy:            c.TpsLimitStrategy,
+               TpsLimitRejectedHandler:     c.TpsLimitRejectedHandler,
+               ExecuteLimit:                c.ExecuteLimit,
+               ExecuteLimitRejectedHandler: c.ExecuteLimitRejectedHandler,
+               Auth:                        c.Auth,
+               RCProtocolsMap:              newRCProtocolsMap,
+               RCRegistriesMap:             newRCRegistriesMap,
+               ProxyFactoryKey:             c.ProxyFactoryKey,
+               NotRegister:                 c.NotRegister,
+               ParamSign:                   c.ParamSign,
+               Tag:                         c.Tag,
+               TracingKey:                  c.TracingKey,
+       }
+}
diff --git a/global/shutdown_config.go b/global/shutdown_config.go
index e55448912..18f3ecc4d 100644
--- a/global/shutdown_config.go
+++ b/global/shutdown_config.go
@@ -68,3 +68,32 @@ func DefaultShutdownConfig() *ShutdownConfig {
 
        return cfg
 }
+
+// Clone a new ShutdownConfig
+func (c *ShutdownConfig) Clone() *ShutdownConfig {
+       if c == nil {
+               return nil
+       }
+
+       var newInternalSignal *bool
+       if c.InternalSignal != nil {
+               newInternalSignal = new(bool)
+               *newInternalSignal = *c.InternalSignal
+       }
+
+       newShutdownConfig := &ShutdownConfig{
+               Timeout:                     c.Timeout,
+               StepTimeout:                 c.StepTimeout,
+               ConsumerUpdateWaitTime:      c.ConsumerUpdateWaitTime,
+               RejectRequestHandler:        c.RejectRequestHandler,
+               InternalSignal:              newInternalSignal,
+               OfflineRequestWindowTimeout: c.OfflineRequestWindowTimeout,
+       }
+
+       newShutdownConfig.RejectRequest.Store(c.RejectRequest.Load())
+       
newShutdownConfig.ConsumerActiveCount.Store(c.ConsumerActiveCount.Load())
+       
newShutdownConfig.ProviderActiveCount.Store(c.ProviderActiveCount.Load())
+       
newShutdownConfig.ProviderLastReceivedRequestTime.Store(c.ProviderLastReceivedRequestTime.Load())
+
+       return newShutdownConfig
+}
diff --git a/global/tls_config.go b/global/tls_config.go
index 3b30d199b..862d9b408 100644
--- a/global/tls_config.go
+++ b/global/tls_config.go
@@ -35,6 +35,20 @@ func DefaultTLSConfig() *TLSConfig {
        return nil
 }
 
+// Clone a new TLSConfig
+func (c *TLSConfig) Clone() *TLSConfig {
+       if c == nil {
+               return nil
+       }
+
+       return &TLSConfig{
+               CACertFile:    c.CACertFile,
+               TLSCertFile:   c.TLSCertFile,
+               TLSKeyFile:    c.TLSKeyFile,
+               TLSServerName: c.TLSServerName,
+       }
+}
+
 type TLSOption func(*TLSConfig)
 
 func WithTLS_CACertFile(file string) TLSOption {
diff --git a/options.go b/options.go
index 0e6719473..fa3996c74 100644
--- a/options.go
+++ b/options.go
@@ -167,6 +167,112 @@ func (rc *InstanceOptions) Prefix() string {
        return constant.Dubbo
 }
 
+func (rc *InstanceOptions) CloneApplication() *global.ApplicationConfig {
+       if rc.Application == nil {
+               return nil
+       }
+       return rc.Application.Clone()
+}
+
+func (rc *InstanceOptions) CloneProtocols() map[string]*global.ProtocolConfig {
+       if rc.Protocols == nil {
+               return nil
+       }
+       protocols := make(map[string]*global.ProtocolConfig, len(rc.Protocols))
+       for k, v := range rc.Protocols {
+               protocols[k] = v.Clone()
+       }
+       return protocols
+}
+
+func (rc *InstanceOptions) CloneRegistries() map[string]*global.RegistryConfig 
{
+       if rc.Registries == nil {
+               return nil
+       }
+       registries := make(map[string]*global.RegistryConfig, 
len(rc.Registries))
+       for k, v := range rc.Registries {
+               registries[k] = v.Clone()
+       }
+       return registries
+}
+
+func (rc *InstanceOptions) CloneConfigCenter() *global.CenterConfig {
+       if rc.ConfigCenter == nil {
+               return nil
+       }
+       return rc.ConfigCenter.Clone()
+}
+
+func (rc *InstanceOptions) CloneMetadataReport() *global.MetadataReportConfig {
+       if rc.MetadataReport == nil {
+               return nil
+       }
+       return rc.MetadataReport.Clone()
+}
+
+func (rc *InstanceOptions) CloneProvider() *global.ProviderConfig {
+       if rc.Provider == nil {
+               return nil
+       }
+       return rc.Provider.Clone()
+}
+
+func (rc *InstanceOptions) CloneConsumer() *global.ConsumerConfig {
+       if rc.Consumer == nil {
+               return nil
+       }
+       return rc.Consumer.Clone()
+}
+
+func (rc *InstanceOptions) CloneMetrics() *global.MetricsConfig {
+       if rc.Metrics == nil {
+               return nil
+       }
+       return rc.Metrics.Clone()
+}
+
+func (rc *InstanceOptions) CloneOtel() *global.OtelConfig {
+       if rc.Otel == nil {
+               return nil
+       }
+       return rc.Otel.Clone()
+}
+
+func (rc *InstanceOptions) CloneLogger() *global.LoggerConfig {
+       if rc.Logger == nil {
+               return nil
+       }
+       return rc.Logger.Clone()
+}
+
+func (rc *InstanceOptions) CloneShutdown() *global.ShutdownConfig {
+       if rc.Shutdown == nil {
+               return nil
+       }
+       return rc.Shutdown.Clone()
+}
+
+func (rc *InstanceOptions) CloneCustom() *global.CustomConfig {
+       if rc.Custom == nil {
+               return nil
+       }
+       return rc.Custom.Clone()
+}
+
+func (rc *InstanceOptions) CloneProfiles() *global.ProfilesConfig {
+       if rc.Profiles == nil {
+               return nil
+       }
+       return rc.Profiles.Clone()
+}
+
+func (rc *InstanceOptions) CloneTLSConfig() *global.TLSConfig {
+       if rc.TLSConfig == nil {
+               return nil
+       }
+       return rc.TLSConfig.Clone()
+}
+
 type InstanceOption func(*InstanceOptions)
 
 func WithOrganization(organization string) InstanceOption {


Reply via email to