配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置;第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置。要了解配置同步机制的实现原理,先得从认识一个名为ConfigurationReloadToken的类型开始。 [ 本文已经同步到《get='_blank'>ASP.NET Core框架揭秘》之中]
目录
当ConfigurationReloadToken的OnReload方法被执行的时候,这被封装的CancellationTokenSource对象的Cancel方法随之被调用。我们知道一旦这个Cancel方法被调用之后,CancellationTokenSource的IsCancellationRequested会马上变成True,意味着ConfigurationReloadToken的HasChanged属性也立即变成True。由于调用RegisterChangeCallback方法注册的回调最是注册到CancellationTokenSource创建的CancellationToken上的,所以该回调会在OnLoad方法被调用之后自动执行。
二、Configuration对象与配置文件的同步
在《聊聊默认支持的各种配置源》和《深入了解三种针对文件(JSON、
1: public abstract class FileConfigurationSource : IConfigurationSource
2: {
3: ...
4: public bool ReloadOnChange { get; set; }
5: }
我们知道 FileConfigurationProvdier总是利用一个FileProvider对象来读取对应的配置文件,除了读取文件内容之外,FileProvider的Watch方法自身就提供了文件监控的能力。FileConfigurationProvdier利用FileProvider监控配置文件,并在配置文件发生改变时自动加载配置的操作实现在如下所示的代码片段中。
1: public abstract class FileConfigurationProvider : ConfigurationProvider
2: {
3: ...
4: public FileConfigurationProvider(FileConfigurationSource source)
5: {
6: this.Source = source;
7: if (source.ReloadOnChange && (this.Source.FileProvider != null))
8: {
9: ChangeToken.OnChange(() => source.FileProvider.Watch(source.Path), this.Load);
10: }
11: }
12: }
程序应用重新配置的回调是注册到Configuration对象的GetReloadToken方法返回的ChangeToken对象上,而Configuration对象的重新加载最终是通过调用所有ConfigurationProvider的Load方法来实现的,所以两者之间必然存在着某种联系。说的具体一点,应用程序可以通过这个ChangeToken感知到配置系统针对ConfigurationProvider的Load方法的调用。要了解两者之间的联系,我们必须先弄清楚Configuration的 GetReloadToken方法返回的是怎样一个ChangeToken对象。
一个Configuration对象代表配置树的某个节点,对于组成同一棵配置树的所有Configuration对象来说,它们的GetReloadToken方法返回的ChangeToken都来源于代表根节点的ConfigurationRoot对象。说的更加具体一点,当我们调用它们的GetReloadToken的时候,返回的其实是调用ConfigurationRoot的同名方法的返回值,那么我们有必要了解一下ConfigurationRoot的GetReloadToken方法的逻辑。
1: public class ConfigurationRoot : IConfigurationRoot
2: {
3: private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();
4: private IList<IConfigurationProvider> _providers;
5:
6: public ConfigurationRoot(IList<IConfigurationProvider> providers)
7: {
8: _providers = providers;
9:
10: foreach (var provider in providers)
11: {
12: provider.Load();
13: ChangeToken.OnChange(() => provider.GetReloadToken(), this.RaiseChanged);
14: }
15: }
16:
17: public IChangeToken GetReloadToken()
18: {
19: return _changeToken;
20: }
21:
22: private void RaiseChanged()
23: {
24: Interlocked.Exchange<ConfigurationReloadToken>(ref _changeToken, new ConfigurationReloadToken()).OnReload();
25: }
26:
27: public void Reload()
28: {
29: foreach (var provider in _providers)
30: {
31: provider.Load();
32: }
33: this.RaiseChanged();
34: }
35: }
如上面的代码片段所示,ConfigurationRoot的GetReloadToken方法返回的是通过字段_changeToken表示的一个ConfigurationReloadToken对象。私有方法RaiseChanged通过调用ConfigurationReloadToken对象的OnReload向订阅者发送配置重新被加载的通知,由于ChangeToken只能使用一次,所以该方法总是为_changeToken字段附上一个新的ConfigurationReloadToken对象。
针对这个RaiseChanged方法的调用发生在两个地方,第一个地方发生在ConfigurationRoot的Reload方法上,也就是说当我们调用该方法以手工的方式重新加载配置的时候,注册到Configuration对象提供的ChangeToken上的回调也会自动执行。
针对RaiseChanged方法的调用还出现在ConfigurationRoot构造函数中。如上面的代码片段所示,ConfigurationRoot会调用每个ConfigurationProvdier的GetReloadToken方法,并将针对RaiseChanged方法的调用作为回调注册到返回的ChangeToken上,也就是说注册到Configuration对象提供的ChangeToken上的回调实际上注册到ConfigurationProvider提供的ChangeToken上。既然如此,如果 ConfigurationProvider提供的这个ChangeToken能够反映针对Load方法的调用,那么上面提到的关于Configuration提供的ChangeToken与ConfigurationProvider的Load方法之间的联系就建立起来了。那么ConfigurationProvider的Load方法与ChangeToken方法返回的ChangeToken究竟有没有关系呢?
1: public abstract class ConfigurationProvider : IConfigurationProvider
2: {
3: private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
4:
5: public IChangeToken GetReloadToken()
6: {
7: return_reloadToken;
8: }
9:
10: protected void OnReload()
11: {
12: Interlocked.Exchange<ConfigurationReloadToken>(ref_reloadToken, new ConfigurationReloadToken()).OnReload();
13: }
14: }
如上面的代码片段所示,抽象类ConfigurationProvider的GetRealoadToken方法返回的是一个通过字段_reloadToken表示的ConfigurationReloadToken对象。该类型还定义了一个受保护的OnReload方法,该方法具有与上面介绍的RaiseChanged方法一样的逻辑,意味着ConfigurationProvider实际上是调用这个方法对外发送配置被重新加载的通知。针对这个OnLoad方法的调用发生在FileConfigurationProvider的Load方法中。所以上面提到的让ConfigurationProvider提供的ChangeToken能够反映针对Load方法的调用最终实现在FileConfigurationProvider中。
1: public abstract class FileConfigurationProvider : ConfigurationProvider
2: {
3: ...
4: public override void Load()
5: {
6: ...
7: base.OnReload();
8: }
9: }
原标题:.NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?
关键词:.NET