dubbo-go icon indicating copy to clipboard operation
dubbo-go copied to clipboard

Solve configuration circular dependencies

Open DMwangnima opened this issue 1 year ago • 5 comments

Why is this needed: 这个issue由之前的提案(#2375)引出,拆分出client模块后,为了让NewClient可以完全与config.Load等价,还需要注入config.RootConfig和config.ConsumerConfig,以完成ReferenceConfig.Init流程。但config模块也会依赖client模块,这样就造成了循环依赖。根本原因在于config模块中的大部分配置都会直接感知上层配置,以client侧的Root->Consumer->Reference配置结构为例,ReferenceConfig.Init(rc *RootConfig)中会直接引用RootConfig和ConsumerConfig。

What would you like to be added: 为了达到上层模块依赖下层模块,下层模块感知不到上层模块的效果,考虑将ReferenceConfig.Init(rc *config.RootConfig)改成ReferenceConfig.Init(opts ...ReferenceOption),ConsumerConfig.Init在调用client.ReferenceConfig.Init时,将所需的配置都以client.ReferenOption数组的形式传入。以下是代码转变:

// old code
func (cc *ConsumerConfig) Init(rc *RootConfig) error {
        // 省略
	for key, referenceConfig := range cc.References {
		if err := referenceConfig.Init(rc); err != nil {
			return err
		}
	}
	// 省略
}
// new code
func (cc *ConsumerConfig) Init(rc *RootConfig) error {
        // 省略
	cc.refOpts = []client.ReferenceOption{
		client.WithFilter(cc.Filter),
		client.WithRegistryIDs(cc.RegistryIDs),
		client.WithProtocol(cc.Protocol),
		client.WithTracingKey(cc.TracingKey),
		client.WithCheck(cc.Check),
		client.WithMeshEnabled(cc.MeshEnabled),
		client.WithAdaptiveService(cc.AdaptiveService),
		client.WithProxyFactory(cc.ProxyFactory),
	}

	for key, referenceConfig := range cc.References {
		if err := referenceConfig.Init(cc.refOpts...); err != nil {
			return err
		}
	}
	// 省略
}

Extended 将ReferenceConfig移动到client模块,config模块依赖client模块可以引申到其他配置,将这些配置移动到各自的模块,例如将RegistryConfig移动到registry模块中,config模块依赖registry模块。这部分的工作将和#2343 dubbo模块拆解与重构计划直接挂钩。

DMwangnima avatar Aug 10 '23 15:08 DMwangnima

依赖确实有点容易循环,有些模块会把一模一样的配置再拷贝一个 struct 出来,只是为了避免循环依赖,例如 https://github.com/apache/dubbo-go/blob/bf5f2ddf40eaf9779355a692bab7842e290b54b2/config/metric_config.go#L45C55-L45C55, 是不是可以考虑做彻底点,把 config 只作为被依赖方,不再依赖其他模块

FoghostCn avatar Aug 17 '23 02:08 FoghostCn

依赖确实有点容易循环,有些模块会把一模一样的配置再拷贝一个 struct 出来,只是为了避免循环依赖,例如 https://github.com/apache/dubbo-go/blob/bf5f2ddf40eaf9779355a692bab7842e290b54b2/config/metric_config.go#L45C55-L45C55, 是不是可以考虑做彻底点,把 config 只作为被依赖方,不再依赖其他模块

config包之前做为项目入口一直是顶层模块,如果不依赖其他模块,config.Load这一套启动流程没法保证。我的想法是config包做为顶层模块,把配置都往下传,层次较低的模块只看配置,不感知config包。

而且除了config包,extension包也有循环依赖的问题,我们应该梳理一下整体的依赖情况,尽量保证自顶向下依赖的逻辑是通顺的。

DMwangnima avatar Aug 17 '23 03:08 DMwangnima

把配置都往下传

是怎么个传法,用 url ?

FoghostCn avatar Aug 17 '23 06:08 FoghostCn

把配置都往下传

是怎么个传法,用 url ?

  1. 直接依赖,使用Init(opts ...option)方式把参数注入进去 比如ConsumerConfig依赖ReferenceConfig,之前是Init(rc *RootConfig),现在是Init(opts ...ReferenceOption)。这个需要我们对dubbo-go做好分层。
  2. invoker链串联 使用URL传静态配置(config.Load时完成的所有配置),使用Invocation传动态配置(invoker链执行时查看)。invocation目前已经可以传复杂配置,URL也需要支持类似map[string]interface{}的复杂配置。每个invoker内部只管传进来的配置,不去config包拿。

DMwangnima avatar Aug 17 '23 11:08 DMwangnima

sounds good

把配置都往下传

是怎么个传法,用 url ?

  1. 直接依赖,使用Init(opts ...option)方式把参数注入进去 比如ConsumerConfig依赖ReferenceConfig,之前是Init(rc *RootConfig),现在是Init(opts ...ReferenceOption)。这个需要我们对dubbo-go做好分层。
  2. invoker链串联 使用URL传静态配置(config.Load时完成的所有配置),使用Invocation传动态配置(invoker链执行时查看)。invocation目前已经可以传复杂配置,URL也需要支持类似map[string]interface{}的复杂配置。每个invoker内部只管传进来的配置,不去config包拿。

FoghostCn avatar Aug 17 '23 11:08 FoghostCn