抽丝剥茧读源码——Microsoft.Extensions.Configuration(2)
繼續抽絲剝繭
? 我們知道在使用json、xml或ini等文件類配置源時,如果更改了配置文件中的內容,程序是能夠感知文件變化的。這里以json配置源為例,查看AddJsonFile這個方法的定義,我們看到在添加json配置源的時候,有這么兩個參數:
AddJsonFile(this IConfigurationBuilder builder,
string path, bool optional, bool reloadOnChange);
optional:配置文件可選
reloadOnChange:配置文件修改時進行重新加載
? OK,動手操練操練。
test.json文件:
{
"SectionA": "ValueA",
"SectionB": "ValueB"
}
測試代碼:
var builder = new ConfigurationBuilder().AddJsonFile("test.json",false,true);
IConfigurationRoot configurationRoot = builder.Build();
Assert.Equal("ValueA", configurationRoot["SectionA"]);
Assert.Equal("ValueB", configurationRoot["SectionB"]);
Thread.Sleep(3000); //這時將文件中SectionA的值更改為ValueA+
Assert.Equal("ValueA+", configurationRoot["SectionA"]);
? 完美通過測試。那么,配置信息是如何監視文件變化的呢?
CancellationTokenSource的使用
? 為了弄清楚配置信息是如何監視文件變化之前,我們先看下CancellationTokenSource的簡單應用。
var source = new CancellationTokenSource();
source.Token.Register(() => Console.WriteLine("This is a callback"));
if(!source.IsCancellationRequested)
{
source.Cancel();
}
// 控制臺輸出 "This is a callback"
? CancellationTokenSource多用在取消線程操作中,這里使用了CancellationToken注冊回調的特性,使用Cancel()方法時觸發回調函數。配置文件的重加載就是通過這個原理實現的,所以在接下來IChangeToken接口的實現類中,我們就能發現CancellationTokenSource的身影。
引入FileExtension
? 目前我們引入了文件配置源,并使用json文件作為了測試文件,那我們再把之前的圖再補充一下。
ConfigurationRoot
? 追溯一下我們在加載配置源時的代碼。
//private readonly IList<IDisposable> _changeTokenRegistrations;
foreach (var p in providers)
{
p.Load();
_changeTokenRegistrations.Add(ChangeToken.OnChange(() =>p.GetReloadToken(),
() => RaiseChanged()));
}
? 我們看到了_changeTokenRegistrations這個對象調用了ChangeToken.OnChange()靜態方法,那么這個靜態方法做了什么呢?
public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
{
if (changeTokenProducer == null)
{
throw new ArgumentNullException(nameof(changeTokenProducer));
}
if (changeTokenConsumer == null)
{
throw new ArgumentNullException(nameof(changeTokenConsumer));
}
return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
}
? 這里引入了ChangeTokenRegistration這個類,通過類的構造函數參數(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state),我們可以看出
它就是將token的生產者changeTokenProducer和消費者changeTokenConsumer做一個綁定操作,綁定的消費函數就是ConfigurationRoot中的RaiseChanged()函數。
private void RaiseChanged()
{
var previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken());
previousToken.OnReload();
}
? 通過這個函數就看到previousToken執行了OnReload()函數,這里我們再跟蹤到此函數的定義時,我們就發現了這段代碼。
public void OnReload() => _cts.Cancel();
? 是不是有點清晰了,但是通過CancellationTokenSource的使用我們知道,token是需要綁定callback函數的,那么這個注冊是在哪里進行的呢?我們再回到ChangeTokenRegistration這個類中。
private void OnChangeTokenFired()
{
// The order here is important. We need to take the token and then apply our changes BEFORE
// registering. This prevents us from possible having two change updates to process concurrently.
// If the token changes after we take the token, then we'll process the update immediately upon
// registering the callback.
var token = _changeTokenProducer();
try
{
_changeTokenConsumer(_state);
}
finally
{
// We always want to ensure the callback is registered
RegisterChangeTokenCallback(token);
}
}
? 在ChangeTokenRegistration的構造函數中,我們知道token注冊了OnChangeTokenFired()這個函數回調,這個函數主要做了三件事情:
token生產者生產一個新的token
消費token,觸發消費函數
將新的token重新注冊一個到此回調函數
? 這樣在token失效后又被重新注冊了。
? 但是,但是,但是,重要的事情說三遍。這里還不是監視文件變化部分的原理哦,這里只是多配置源用來監視配置文件重載的,那么監視文件變化其實也是這個原理,這里理順了,文件變化只要找到使用ChangeToken.OnChange()這個靜態方法的地方就可以了,文件變化也就理解了。
FileConfigurationProvider
? 在FileConfigurationProvider類的構造函數中,我們就找到了監視文件變化的源頭了。這里的消費者函數,就是Load()函數,一旦監視到文件變化就調用Load對文件進行重新加載。
if (Source.ReloadOnChange && Source.FileProvider != null)
{
_changeTokenRegistration = ChangeToken.OnChange(
() => Source.FileProvider.Watch(Source.Path),
() => {
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
}
? 這里的Watch函數用到的原理是FileSystemWatcher這個類,這里不過多闡述,大家可自行查看,它主要有以下幾個事件.
public event FileSystemEventHandler Deleted
public event FileSystemEventHandler Created
public event FileSystemEventHandler Changed
public event RenamedEventHandler Renamed
public event ErrorEventHandler Error;
? 配置文件方面的原理也就逐漸清晰了,不過原理易懂,設計思想難懂,希望大家多借鑒其中的設計理念,用好別人的代碼,也寫好自己的代碼。
個人博客:www.corecoder.cn
總結
以上是生活随笔為你收集整理的抽丝剥茧读源码——Microsoft.Extensions.Configuration(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [oracle]分区表学习
- 下一篇: java生成首字母拼音简码的总结