IChangeToken监控配置文件修改

Configuration是支持监控配置文件修改的,如果设置了ReloadOnChange=true,则当配置文件发生改变时,会自动重新加载,这是通过IChangeToken实现的。

文件类型的配置文件提供程序都继承自虚基类FileConfigurationProvider,配置源继承自FileConfigurationSource。FileConfigurationSource中的ReloadOnChange属性,配置是否监控文件的修改。当该属性置为true时,在Provider中会通过ChangeToken.OnChange()方法订阅配置文件更改事件,当发生修改时重新加载文件。代码如下:

if (Source.ReloadOnChange && Source.FileProvider != null)
{
    _changeTokenRegistration = ChangeToken.OnChange(
        () => Source.FileProvider.Watch(Source.Path),
        () =>
        {
            Thread.Sleep(Source.ReloadDelay);
            Load(reload: true);
        });
}

先看下ChangeToken.OnChange()方法,该方法提供了生产者-消费者模式。生产者为创建IChangeToken的工厂,IChangeToken有一个RegisterChangeCallback方法注册回调函数。在ChangeTokenRegistration的构造函数中,会调用IChangeToken.RegisterChangeCallback()将消费者注册为回调函数,这样即实现了生产-消费模式。

public static IDisposable OnChange<TState>(Func<IChangeToken?> changeTokenProducer, 
    Action<TState> changeTokenConsumer, TState state)
{
    if (changeTokenProducer == null)
        throw new ArgumentNullException(nameof(changeTokenProducer));
    if (changeTokenConsumer == null)
        throw new ArgumentNullException(nameof(changeTokenConsumer));

    return new ChangeTokenRegistration<TState>(changeTokenProducer, changeTokenConsumer, state);
}
public interface IChangeToken
{
    bool HasChanged { get; }
    bool ActiveChangeCallbacks { get; }
    IDisposable RegisterChangeCallback(Action<object?> callback, object? state);
}

回到文件监控上,FileProvider.Watch()方法返回一个CancellationChangeToken。当CancellationTokenSource调用Cancel()方法时(使用了FileSystemWatcher,监听其Changed事件,当事件触发时调用Cancel()方法),就会触发回调,Load()方法便会重新加载配置文件,从而实现配置文件的修改监控。

Load方法还会继续向上传递文件修改信息。ConfigurationProvider、ConfigurationRoot、ConfigurationManager均有一个ConfigurationReloadToken字段,用来监控修改。ConfigurationReloadToken与CancellationChangeToken一样,都是使用CancellationTokenSource实现的消费回调。当调用Load方法时,也会触发ConfigurationProvider的回调。

public class ConfigurationReloadToken : IChangeToken
{
    private CancellationTokenSource _cts = new CancellationTokenSource();

    public bool ActiveChangeCallbacks => true;
    public bool HasChanged => _cts.IsCancellationRequested;

    public IDisposable RegisterChangeCallback(Action<object?> callback, object? state) =>
        _cts.Token.Register(callback, state);

    public void OnReload() => _cts.Cancel();
}
// FileConfigurationProvider
private void Load(bool reload)
{
    // ...
    OnReload();
}

// ConfigurationProvider
protected void OnReload()
{
    ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _reloadToken, 
        new ConfigurationReloadToken());
    previousToken.OnReload();
}

ConfigurationProvider的IChangeToken又会触发ConfigurationRoot、ConfigurationManager的回调,从而实现向上传递。

// ConfigurationRoot构造函数
public ConfigurationRoot(IList<IConfigurationProvider> providers)
{
    if (providers == null)
        throw new ArgumentNullException(nameof(providers));
    
    _providers = providers;
    _changeTokenRegistrations = new List<IDisposable>(providers.Count);
    foreach (IConfigurationProvider p in providers)
    {
        p.Load();
        _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged()));
    }
}

private void RaiseChanged()
{
    ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _changeToken, 
        new ConfigurationReloadToken());
    previousToken.OnReload();
}

程序中可以通过ChangeToken.OnChange()方法绑定IConfiguration的IChangeToken,订阅配置文件的修改事件。

posted @ 2023-12-02 01:24  louzi  阅读(27)  评论(0编辑  收藏  举报