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

Net6+ 调整IList<IConfigurationSource>的顺序之后Apollo的热更新不工作了. #203

Closed
czd890 opened this issue Jul 28, 2022 · 5 comments

Comments

@czd890
Copy link
Contributor

czd890 commented Jul 28, 2022

Net6+ 通过WebApplicationBuilder配置application, configuration系统换成了ConfigurationManager的实现.
通过ConfigurationManager.Sources.[Insert,Remove,Add] 调整source的顺序的时候, 会导致 dispose ApolloConfigurationProvider对象, 进而导致丢掉对changeLinstener的调用.

是不是考虑吧ConfigurationProvider和IConfigurationSource 的实现从ApolloConfigurationProvider独立开来.

@zhaobotao
Copy link

zhaobotao commented Oct 27, 2022

试了下,先调用builder.Sources.clear(),然后按照自己想要的顺序添加source,就不会丢掉changeLinstener。在builder.Sources.clear()之前,可以先把builder.Sources的所有Sources按照类型存在不同的List临时变量里,后面再用。

我这样写可以:

       var apolloConfig = builder.Build().GetSection("apollo");// AddPlaceholderResolver();
        var apolloOptions = apolloConfig.Get<ApolloOptions>();
        var preoverlocal = apolloConfig.GetValue<bool>("preoverlocal", false);

        IList<IConfigurationSource> fileSources = new List<IConfigurationSource>();
        IList<IConfigurationSource> environmentVariablesSources = new List<IConfigurationSource>();
        IList<IConfigurationSource> commandLineSources = new List<IConfigurationSource>();
        IList<IConfigurationSource> otherSources = new List<IConfigurationSource>(); // 包括MemoryConfigurationSource,ChainedConfigurationSource,优先级最低,但是要先添加

        foreach (var configSource in builder.Sources)
        {
            // 开始暂存
            if (configSource is FileConfigurationSource)
            {
                fileSources.Add(configSource);
            }
            else if (configSource is EnvironmentVariablesConfigurationSource)
            {
                environmentVariablesSources.Add(configSource);
            }
            else if (configSource is CommandLineConfigurationSource)
            {
                commandLineSources.Add(configSource);
            }
            else
            {
                otherSources.Add(configSource);    
            }
        }
        
        builder.Sources.Clear(); // 必须使用这个方法,不能调用remove,会引发dispose导致动态更新失效。
        // 开始重新装载
        IApolloConfigurationBuilder apolloBuilder = null;
        // 先添加的优先级低
        foreach (var otherSource in otherSources)
        {
            builder.Sources.Add(otherSource);    
        }
        
        if (preoverlocal == false) // 默认apollo优先级低于本地文件
        {
            // 不能调用remove,会引发dispose导致动态更新失效
            
            // foreach (var fileSource in fileSources)
            // {
            //     builder.Sources.Remove(fileSource);
            // }

            apolloBuilder = builder.AddApollo(apolloOptions);
            foreach (var fileSource in fileSources)
            {
                builder.Sources.Add(fileSource);
            }
        }
        else  // apollo优先于本地文件,但是不优先于环境变量
        {
            foreach (var fileSource in fileSources)
            {
                // builder.Sources.Remove(fileSource);
                builder.Sources.Add(fileSource);
            }
            apolloBuilder = builder.AddApollo(apolloOptions);
        }
        
        foreach (var environmentVariablesSource in environmentVariablesSources)
        {
            // builder.Sources.Remove(environmentVariablesSource);
            builder.Sources.Add(environmentVariablesSource);
        }

        foreach (var commandLineSource in commandLineSources)
        {
            // builder.Sources.Remove(commandLineSource);
            builder.Sources.Add(commandLineSource);
        }

        return apolloBuilder;

@czd890
Copy link
Contributor Author

czd890 commented Nov 3, 2022

@zhaobotao 是的, 保存之前的sources, 在根据自己的规则调整add顺序, builder.Sources.Clear()的时候也是会dispose所有的provider的, 您上面的代码没问题是因为apollo的source没有在此之前加入.
比如说json文件的source可以在clear或者insert,remove之后在重新add也是能工作的. 因为他们的实现是把source和provider独立分开的.
比如说基于文件的provider, 包括xml,json等. dispose是释放对文件的监控. 在add的时候会根据source重新生成provider.

所以这个issue是建议要不要考虑把ConfigurationProvider和IConfigurationSource 的实现从ApolloConfigurationProvider独立开来.
类似伪代码: 这是伪代码哈, 手动敲的. 还有很多细节没有. 只是为了方便说明.

class ApolloConfigurationSource :IConfigurationSource {
    IConfigRepository ConfigRepository;
    IConfigurationProvider Build=>new ApolloConfigurationProvider(this.ConfigRepository);
}

class ApolloConfigurationProvider:IConfigurationProvider {
    ApolloConfigurationProvider(IConfigRepository  configRepository){
        configRepository.AddChangeListener(this)
    }
    void Dispose()
    {
        configRepository.RemoveChangeListener(this);
    }
}

@czd890
Copy link
Contributor Author

czd890 commented Nov 3, 2022

ConfigurationSources 的Insert,Remove,Clear 都会调用ReloadSources(). 然后_providerManager.ReplaceProviders(list);, 然后在ReplaceProviders里面就吧之前的全部dispose.

	private sealed class ConfigurationSources : IList<IConfigurationSource>, ICollection<IConfigurationSource>, IEnumerable<IConfigurationSource>, IEnumerable
	{
		public void Add(IConfigurationSource source)
		{
			_sources.Add(source);
			_config.AddSource(source);
		}

		public void Clear()
		{
			_sources.Clear();
			_config.ReloadSources();
		}

		public void Insert(int index, IConfigurationSource source)
		{
			_sources.Insert(index, source);
			_config.ReloadSources();
		}

		public bool Remove(IConfigurationSource source)
		{
			bool result = _sources.Remove(source);
			_config.ReloadSources();
			return result;
		}
public void ReplaceProviders(List<IConfigurationProvider> providers)
{
	ReferenceCountedProviders refCountedProviders = _refCountedProviders;
	lock (_replaceProvidersLock)
	{
		if (_disposed)
		{
			throw new ObjectDisposedException("ConfigurationManager");
		}
		_refCountedProviders = ReferenceCountedProviders.Create(providers);
	}
	refCountedProviders.Dispose();
}

@czd890
Copy link
Contributor Author

czd890 commented Nov 3, 2022

这里的情况也差不多 @zhaobotao

builder.Sources.Remove(previous);
builder.Sources.Add(previous);

@czd890
Copy link
Contributor Author

czd890 commented Nov 2, 2023

Closed by #236

@czd890 czd890 closed this as completed Nov 2, 2023
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