dotnetcore中的IOptionsSnapshot<>的自动更新原理

1、首先讲讲ChangeToken.OnChange方法:

原理是给一个CancellationToken注册一个消费者委托,调用CancellationToken的Cancel的时候会调用这个CancellationToken中所有的委托 代码实现如下:

public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
{
    if (changeTokenProducer == null)
    {
        throw new ArgumentNullException("changeTokenProducer");
    }
    if (changeTokenConsumer == null)
    {
        throw new ArgumentNullException("changeTokenConsumer");
    }
    Action<object> callback = null;
    callback = delegate(object s)
    {
        IChangeToken changeToken = changeTokenProducer();
        try
        {
            changeTokenConsumer();
        }
        finally
        {
            changeToken.RegisterChangeCallback(callback, null);
        }
    };
    return changeTokenProducer().RegisterChangeCallback(callback, null);
}
2、IOptions<> 生命周期为Singleton,初始化的时候配置就已经存入缓存,并且不再更新
3、IOptionsSnapshot<> 生命周期为Scope,初始化的时候会写入缓存,内容由OptionsMonitor提供,初始化OptionsMonitor的时候会给所有的IOtionsChangeTokenSource<T>对象的ChangeToken注册一个重载配置的方法代码如下
using (IEnumerator<IOptionsChangeTokenSource<TOptions>> enumerator =  this._sources.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        IOptionsChangeTokenSource<TOptions> source = enumerator.Current;
        ChangeToken.OnChange(() => source.GetChangeToken(), delegate
        {
            this.InvokeChanged();
        });
    }
}

这里的source.GetChangeToken中的Token是从IConfigurationRoot中获取的,以下代码可以证明:

public class ConfigurationChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions>
    {
        private IConfiguration _config;

        /// <summary>
        /// Constructor taking the IConfiguration instance to watch.
        /// </summary>
        /// <param name="config">The configuration instance.</param>
        public ConfigurationChangeTokenSource(IConfiguration config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            this._config = config;
        }

        /// <summary>
        /// Returns the reloadToken from IConfiguration.
        /// </summary>
        /// <returns></returns>
        public IChangeToken GetChangeToken()
        {
            return this._config.GetReloadToken();
        }
    }

这里的_config就是调用AddOptions.Config(...)方法的时候注册进去的,而ConfigurationRoot在初始化的时候,会把自己的ChangeToken的Reload事件注册到所有的IConfigurationProvider对象的ChangeToken,代码如下

public ConfigurationRoot(IList<IConfigurationProvider> providers)
{
    if (providers == null)
    {
        throw new ArgumentNullException("providers");
    }
    this._providers = providers;
    using (IEnumerator<IConfigurationProvider> enumerator = providers.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            IConfigurationProvider p = enumerator.Current;
            p.Load();
            ChangeToken.OnChange(() => p.GetReloadToken(), delegate
            {
                this.RaiseChanged();
            });
        }
    }
}
private void RaiseChanged()
{
    //执行自身的_changeToken的OnReload事件并且重新初始化一个ConfigurationReloadToken
    Interlocked.Exchange<ConfigurationReloadToken>(ref this._changeToken, new ConfigurationReloadToken()).OnReload();
}

这样就可以保证所有的ConfigurationProvider发生Reload的时候,IConfigurationRoot中的ChangeToken也会发生Reload事件。而我们的配置发生改变的时候,我们的ConfigurationProvider需要先更新Data数据,然后再触发他的Reload事件,就可以触发IConfigurationRoot的Reload事件,OptionsMonitor初始化的时候会给IConfigurationRoot的ChangeToken注册一个更新配置缓存的事件(前面说到过),所以OptionsMonitor就会更新配置缓存,然后下一次请求的时候创建的新IOptionsSnapshot<>接口对象就可以读取到更新之后的配置信息了

原文地址:https://www.cnblogs.com/dingsblog/p/6761804.html