.NET6+自定义Configuration Provider实现重载/刷新方案问询
自定义配置提供器适配IOptionsMonitor与IOptionsSnapshot实现指南
核心机制说明
GetReloadToken:返回一个IChangeToken,配置系统(如ConfigurationRoot)和IOptionsMonitor<T>会监听这个token。当配置变更时,旧token失效,新token生成,触发监听者的刷新逻辑。OnReload():由配置提供器主动调用,用于触发配置变更通知。调用后,内部会更新变更token,告知所有依赖方配置已更新。IOptionsMonitor<T>:单例组件,通过监听IChangeToken实时刷新配置缓存,适合需要实时获取最新配置的场景。IOptionsSnapshot<T>:作用域组件,每次创建作用域时会重新从配置源读取最新数据绑定实例,适合每个请求需要最新配置的场景。
轮询模式实现步骤
由于你的配置源不支持推送,轮询是最优方案,具体实现如下:
- 添加后台轮询任务:在提供器构造时启动定时任务,定期拉取远程配置。
- 异步化Load逻辑:替换同步阻塞的
GetAwaiter().GetResult(),改用异步加载避免阻塞主线程。 - 增量更新判断:每次拉取后对比新旧配置数据,仅当实际变化时才更新
Data并触发OnReload(),减少不必要的通知。
关键方法与生命周期解析
GetReloadToken调用方:ConfigurationRoot会主动调用此方法获取变更token,IOptionsMonitor<T>通过ConfigurationRoot监听token变化。OnReload()调用时机:仅当配置数据确实发生变化时调用,确保通知的有效性。- 配置提供器生命周期:默认以单例形式存在于DI容器中,轮询任务会伴随应用整个生命周期,需实现
IDisposable确保资源释放。
修改后的完整代码示例
internal sealed class MyLittleConfigurationProvider : ConfigurationProvider, IDisposable { private readonly ConfigFetcher _configFetcher; private readonly PeriodicTimer _pollTimer; private readonly CancellationTokenSource _cts = new(); public MyLittleConfigurationProvider(IDataClient client, TimeSpan pollInterval) { _configFetcher = new SecretsFetcher(client); _pollTimer = new PeriodicTimer(pollInterval); // 启动后台轮询任务,无需等待完成 _ = RunPollingLoopAsync(); } // 重写同步Load方法,内部调用异步实现 public override void Load() { LoadAsync().GetAwaiter().GetResult(); } private async Task LoadAsync() { var remoteConfig = await _configFetcher.GetConfig(); var newConfigData = // 将远程配置转换为键值对字典 // 对比新旧数据,仅当内容不同时更新 if (!IsDataEqual(Data, newConfigData)) { Data = newConfigData; OnReload(); // 触发配置变更通知 } } private async Task RunPollingLoopAsync() { try { while (await _pollTimer.WaitForNextTickAsync(_cts.Token)) { await LoadAsync(); } } catch (OperationCanceledException) { // 任务正常取消,无需处理 } } // 辅助方法:对比两个配置字典是否相等 private bool IsDataEqual(IDictionary<string, string?> oldData, IDictionary<string, string?> newData) { if (oldData.Count != newData.Count) return false; foreach (var (key, value) in oldData) { if (!newData.TryGetValue(key, out var newValue) || value != newValue) { return false; } } return true; } public void Dispose() { _cts.Cancel(); _pollTimer.Dispose(); _cts.Dispose(); } }
配置重载完整流程
- 轮询任务定期触发,调用
LoadAsync()拉取远程最新配置。 - 对比新配置与当前
Data,确认存在差异。 - 更新
Data为新的键值对集合。 - 调用
OnReload(),内部生成新的变更token,标记旧token失效。 ConfigurationRoot检测到token变更,通知所有注册的监听者。IOptionsMonitor<T>收到通知后,重新绑定配置到T实例并更新缓存;IOptionsSnapshot<T>在下次作用域创建时,读取最新Data生成新的配置实例。
内容的提问来源于stack exchange,提问作者Zdeněk
相关产品推荐
相关产品推荐

