This is an automated email from the ASF dual-hosted git repository.
AlexStocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
The following commit(s) were added to refs/heads/develop by this push:
new 25d2f0c7f expose (#3481)
25d2f0c7f is described below
commit 25d2f0c7f5367b7f7bf6e22754126578209c378d
Author: Xuetao Li <[email protected]>
AuthorDate: Sat Jul 11 07:50:40 2026 +0000
expose (#3481)
---
dubbo.go | 36 +++++++++++++++++++-
dubbo_test.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++
global/config_test.go | 2 ++
global/tracing_config.go | 20 ++++++++++++
loader.go | 16 +++++----
options.go | 59 +++++++++++++++++++++++++++------
6 files changed, 201 insertions(+), 17 deletions(-)
diff --git a/dubbo.go b/dubbo.go
index 4fedea24b..70751aa91 100644
--- a/dubbo.go
+++ b/dubbo.go
@@ -29,6 +29,7 @@ import (
import (
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common"
+ "dubbo.apache.org/dubbo-go/v3/global"
"dubbo.apache.org/dubbo-go/v3/server"
)
@@ -40,7 +41,7 @@ var (
startOnce sync.Once
)
-// Instance is the highest layer conception that user could touch. It is
mapped from RootConfig.
+// Instance is the highest layer conception that user could touch.
// When users want to inject global configurations and configure common
modules for client layer
// and server layer, user-side code would be like this:
//
@@ -61,6 +62,39 @@ func NewInstance(opts ...InstanceOption) (*Instance, error) {
return &Instance{insOpts: newInsOpts}, nil
}
+// GetOptionsSnapshot returns a detached snapshot of this instance's options.
+func (ins *Instance) GetInstanceOptionsSnapshot() *InstanceOptions {
+ if ins == nil || ins.insOpts == nil {
+ return nil
+ }
+ return ins.insOpts.Clone()
+}
+
+// GetCustomConfig returns a detached snapshot of this instance's custom
config.
+func (ins *Instance) GetCustomConfigSnapshot() *global.CustomConfig {
+ if ins == nil || ins.insOpts == nil {
+ return nil
+ }
+ return ins.insOpts.CloneCustom()
+}
+
+// GetInstanceOptionsSnapshot returns a detached snapshot of the options
loaded by Load.
+func GetInstanceOptionsSnapshot() *InstanceOptions {
+ instanceOptionsMutex.Lock()
+ defer instanceOptionsMutex.Unlock()
+ return instanceOptions.Clone()
+}
+
+// GetCustomConfig returns a detached snapshot of the custom config loaded by
Load.
+func GetCustomConfigSnapshot() *global.CustomConfig {
+ instanceOptionsMutex.Lock()
+ defer instanceOptionsMutex.Unlock()
+ if instanceOptions == nil {
+ return nil
+ }
+ return instanceOptions.CloneCustom()
+}
+
// NewClient is like client.NewClient, but inject configurations from
RootConfig and
// ConsumerConfig
func (ins *Instance) NewClient(opts ...client.ClientOption) (*client.Client,
error) {
diff --git a/dubbo_test.go b/dubbo_test.go
index 3c8e123b0..0a6e7e069 100644
--- a/dubbo_test.go
+++ b/dubbo_test.go
@@ -119,6 +119,91 @@ func TestIndependentConfig(t *testing.T) {
}
}
+func TestInstanceOptionsSnapshotIsDetached(t *testing.T) {
+ useAgent := false
+ ins, err := NewInstance(func(opts *InstanceOptions) {
+ opts.Application.Name = "snapshot-app"
+ opts.Registries = map[string]*global.RegistryConfig{
+ "zk": {
+ Protocol: constant.ZookeeperKey,
+ Address: "127.0.0.1:2181",
+ UseAsMetaReport: "false",
+ },
+ }
+ opts.Tracing = map[string]*global.TracingConfig{
+ "jaeger": {
+ Name: "jaeger",
+ Address: "127.0.0.1:6831",
+ UseAgent: &useAgent,
+ },
+ }
+ opts.Custom = &global.CustomConfig{
+ ConfigMap: map[string]any{"k": "v"},
+ }
+ })
+ require.NoError(t, err)
+
+ snapshot := ins.GetInstanceOptionsSnapshot()
+ require.NotNil(t, snapshot)
+ require.NotNil(t, snapshot.Application)
+ require.NotNil(t, snapshot.Registries["zk"])
+ require.NotNil(t, snapshot.Tracing["jaeger"])
+ require.NotNil(t, snapshot.Tracing["jaeger"].UseAgent)
+ require.NotNil(t, snapshot.Custom)
+
+ snapshot.Application.Name = "changed-app"
+ snapshot.Registries["zk"].Address = "127.0.0.1:2182"
+ *snapshot.Tracing["jaeger"].UseAgent = true
+ snapshot.Custom.ConfigMap["k"] = "changed"
+
+ assert.Equal(t, "snapshot-app", ins.insOpts.Application.Name)
+ assert.Equal(t, "127.0.0.1:2181", ins.insOpts.Registries["zk"].Address)
+ assert.False(t, *ins.insOpts.Tracing["jaeger"].UseAgent)
+ assert.Equal(t, "v", ins.insOpts.Custom.ConfigMap["k"])
+}
+
+func TestWithCustom(t *testing.T) {
+ configMap := map[string]any{"k": "v"}
+ ins, err := NewInstance(WithCustom(configMap))
+ require.NoError(t, err)
+
+ configMap["k"] = "changed"
+
+ custom := ins.GetCustomConfigSnapshot()
+ require.NotNil(t, custom)
+ assert.Equal(t, "v", custom.ConfigMap["k"])
+
+ custom.ConfigMap["k"] = "changed-again"
+ assert.Equal(t, "v", ins.insOpts.Custom.ConfigMap["k"])
+}
+
+func TestGlobalInstanceOptionsSnapshotIsDetached(t *testing.T) {
+ prevIns := instanceOptions
+ defer func() { instanceOptions = prevIns }()
+
+ instanceOptions = defaultInstanceOptions()
+ instanceOptions.Application.Name = "global-snapshot-app"
+ instanceOptions.Custom = &global.CustomConfig{
+ ConfigMap: map[string]any{"k": "v"},
+ }
+
+ snapshot := GetInstanceOptionsSnapshot()
+ require.NotNil(t, snapshot)
+ require.NotNil(t, snapshot.Application)
+ require.NotNil(t, snapshot.Custom)
+
+ snapshot.Application.Name = "changed-app"
+ snapshot.Custom.ConfigMap["k"] = "changed"
+
+ assert.Equal(t, "global-snapshot-app", instanceOptions.Application.Name)
+ assert.Equal(t, "v", instanceOptions.Custom.ConfigMap["k"])
+
+ custom := GetCustomConfigSnapshot()
+ require.NotNil(t, custom)
+ custom.ConfigMap["k"] = "changed-again"
+ assert.Equal(t, "v", instanceOptions.Custom.ConfigMap["k"])
+}
+
func TestInstanceInitKeepsGlobalOnlyConfigWithConfigCenter(t *testing.T) {
resetDynamicConfiguration(t)
diff --git a/global/config_test.go b/global/config_test.go
index 449cd3f96..90d6abd29 100644
--- a/global/config_test.go
+++ b/global/config_test.go
@@ -1751,8 +1751,10 @@ func TestDefaultCustomConfig(t *testing.T) {
t.Run("default_custom_config", func(t *testing.T) {
custom := DefaultCustomConfig()
assert.NotNil(t, custom)
+ assert.NotNil(t, custom.ConfigMap)
})
}
+
func TestMethodConfigClone(t *testing.T) {
t.Run("clone_full_method_config", func(t *testing.T) {
method := &MethodConfig{
diff --git a/global/tracing_config.go b/global/tracing_config.go
index d4c9b423f..a7228b69e 100644
--- a/global/tracing_config.go
+++ b/global/tracing_config.go
@@ -25,3 +25,23 @@ type TracingConfig struct {
Address string `yaml:"address" json:"address,omitempty"
property:"address"`
UseAgent *bool `default:"false" yaml:"use-agent"
json:"use-agent,omitempty" property:"use-agent"`
}
+
+// Clone a new TracingConfig
+func (c *TracingConfig) Clone() *TracingConfig {
+ if c == nil {
+ return nil
+ }
+
+ var useAgent *bool
+ if c.UseAgent != nil {
+ v := *c.UseAgent
+ useAgent = &v
+ }
+
+ return &TracingConfig{
+ Name: c.Name,
+ ServiceName: c.ServiceName,
+ Address: c.Address,
+ UseAgent: useAgent,
+ }
+}
diff --git a/loader.go b/loader.go
index 06dc4c838..d855f7bfd 100644
--- a/loader.go
+++ b/loader.go
@@ -68,22 +68,26 @@ var watcher = &fileWatcher{
func Load(opts ...LoaderConfOption) error {
conf := NewLoaderConf(opts...)
+ newOpts := conf.opts
if conf.opts == nil {
+ newOpts = defaultInstanceOptions()
koan := GetConfigResolver(conf)
koan = conf.MergeConfig(koan)
- if err := koan.UnmarshalWithConf(instanceOptions.Prefix(),
- instanceOptions, koanf.UnmarshalConf{Tag: "yaml"}); err
!= nil {
+ if err := koan.UnmarshalWithConf(newOpts.Prefix(),
+ newOpts, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
return err
}
- } else {
- instanceOptions = conf.opts
}
- if err := instanceOptions.init(); err != nil {
+ if err := newOpts.init(); err != nil {
return err
}
- instance := &Instance{insOpts: instanceOptions}
+ instanceOptionsMutex.Lock()
+ instanceOptions = newOpts
+ instanceOptionsMutex.Unlock()
+
+ instance := &Instance{insOpts: newOpts}
// start the file watcher
once.Do(func() {
watcher.watcherWg.Add(1)
diff --git a/options.go b/options.go
index 349f0055a..1e2b5157f 100644
--- a/options.go
+++ b/options.go
@@ -18,6 +18,7 @@
package dubbo
import (
+ "maps"
"strconv"
"time"
)
@@ -218,6 +219,17 @@ func (rc *InstanceOptions) CloneMetrics()
*global.MetricsConfig {
return rc.Metrics.Clone()
}
+func (rc *InstanceOptions) CloneTracing() map[string]*global.TracingConfig {
+ if rc.Tracing == nil {
+ return nil
+ }
+ tracing := make(map[string]*global.TracingConfig, len(rc.Tracing))
+ for k, v := range rc.Tracing {
+ tracing[k] = v.Clone()
+ }
+ return tracing
+}
+
func (rc *InstanceOptions) CloneOtel() *global.OtelConfig {
if rc.Otel == nil {
return nil
@@ -271,6 +283,36 @@ func (rc *InstanceOptions) CloneTLSConfig()
*global.TLSConfig {
return rc.TLSConfig.Clone()
}
+// Clone returns a snapshot of InstanceOptions. The returned value is detached
+// from the running instance, so callers can inspect or modify it without
+// changing live framework configuration.
+func (rc *InstanceOptions) Clone() *InstanceOptions {
+ if rc == nil {
+ return nil
+ }
+
+ return &InstanceOptions{
+ Application: rc.CloneApplication(),
+ Protocols: rc.CloneProtocols(),
+ Registries: rc.CloneRegistries(),
+ ConfigCenter: rc.CloneConfigCenter(),
+ MetadataReport: rc.CloneMetadataReport(),
+ Provider: rc.CloneProvider(),
+ Consumer: rc.CloneConsumer(),
+ Metrics: rc.CloneMetrics(),
+ Tracing: rc.CloneTracing(),
+ Otel: rc.CloneOtel(),
+ Logger: rc.CloneLogger(),
+ Shutdown: rc.CloneShutdown(),
+ Router: rc.CloneRouter(),
+ EventDispatcherType: rc.EventDispatcherType,
+ CacheFile: rc.CacheFile,
+ Custom: rc.CloneCustom(),
+ Profiles: rc.CloneProfiles(),
+ TLSConfig: rc.CloneTLSConfig(),
+ }
+}
+
type InstanceOption func(*InstanceOptions)
func WithOrganization(organization string) InstanceOption {
@@ -453,16 +495,13 @@ func WithRouter(opts ...router.Option) InstanceOption {
// }
//}
-//func WithCustom(opts ...global.CustomOption) InstanceOption {
-// cusCfg := new(global.CustomConfig)
-// for _, opt := range opts {
-// opt(cusCfg)
-// }
-//
-// return func(cfg *InstanceOptions) {
-// cfg.Custom = cusCfg
-// }
-//}
+func WithCustom(configMap map[string]any) InstanceOption {
+ return func(cfg *InstanceOptions) {
+ custom := global.DefaultCustomConfig()
+ maps.Copy(custom.ConfigMap, configMap)
+ cfg.Custom = custom
+ }
+}
//func WithProfiles(opts ...global.ProfilesOption) InstanceOption {
// proCfg := new(global.ProfilesConfig)