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<>接口对象就可以读取到更新之后的配置信息了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步