Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solve configuration circular dependencies #2377

Open
DMwangnima opened this issue Aug 10, 2023 · 5 comments
Open

Solve configuration circular dependencies #2377

DMwangnima opened this issue Aug 10, 2023 · 5 comments

Comments

@DMwangnima
Copy link
Contributor

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模块拆解与重构计划直接挂钩。

@FoghostCn
Copy link
Contributor

FoghostCn commented Aug 17, 2023

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

@DMwangnima
Copy link
Contributor Author

DMwangnima commented Aug 17, 2023

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

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

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

@FoghostCn
Copy link
Contributor

把配置都往下传

是怎么个传法,用 url ?

@DMwangnima
Copy link
Contributor Author

DMwangnima commented Aug 17, 2023

把配置都往下传

是怎么个传法,用 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
Copy link
Contributor

FoghostCn commented Aug 17, 2023

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包拿。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants