我是靠谱客的博主 愉快小兔子,这篇文章主要介绍Net6Configuration & Options 源码分析 Part3 IOptionsMonitor 是如何接收到配置文件变更并同步数据源的,现在分享给大家,希望可以做个参考。

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475

配置源的同步 IOptionsMonitor 使用

//以下demo演示使用IOptionsMonitor重新加载配置并当重新加载配置是执行回调函数

复制代码
1
2
3
4
5
6
7
8
9
10
var configuration = new ConfigurationBuilder().AddJsonFile(path: "profile.json", optional: false, reloadOnChange: true).Build(); new ServiceCollection().AddOptions().Configure(configuration).BuildServiceProvider().GetRequiredService>().OnChange(profile => Console.WriteLine($"data reload: {profile.Age}")); Console.Read(); public class Profile { public int Age { get; set; } }

配置源的同步 IOptionsMonitor 源码分析

当文件变更时如何向外发送通知的以及 Reload data。

以JsonConfiguration为例:
FileConfigurationProvider通过FileProvider.Watch当文件发生改变的时候会调用Load,load方法做了两件事情,1.调用子类同名虚方完成具体数据的reload data(由具体实现类:JsonConfigurationProvider)2。提供调用OnReload(由父类ConfigurationProvider实现)。完成对外发送data change的通知。OnReload内调用了_reloadToken.OnReload发送回调通知并产生一个新的ConfigurationReloadToken重新赋值给_reloadToken,通知注册到FileConfigurationProvider._reloadToken的回调,那么想接收到文件改变的消息只需要通过GetReloadToken()得到_reloadToken属性并将回调函数注册进去即可。
如下是此三个类的继承关系JsonConfiguration->FileConfigurationProvider->ConfigurationProvider
知道了这些在看下ConfigurationRoot。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public abstract class FileConfigurationProvider : ConfigurationProvider, IDisposable { public FileConfigurationProvider(FileConfigurationSource source!!) { Source = source; if (Source.ReloadOnChange && Source.FileProvider != null) { _changeTokenRegistration = ChangeToken.OnChange( () => Source.FileProvider.Watch(Source.Path!), () => { // 重新从JsonFile Load 数据并 Load(reload: true); }); } } private void Load(bool reload) { IFileInfo? file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty); using Stream stream = OpenRead(file); try { // 此处调用具体实现类的Load 方法例如JsonConfigurationProvider Load(stream); } // 发送OnReload 并重新生成ConfigurationReloadToken共下次使用。 OnReload(); } } public class JsonConfigurationProvider : FileConfigurationProvider { public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { } public override void Load(Stream stream) { Data = JsonConfigurationFileParser.Parse(stream); } } public abstract class ConfigurationProvider : IConfigurationProvider { protected void OnReload() { ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken()); previousToken.OnReload(); } public IChangeToken GetReloadToken() { return _reloadToken; } }

ConfigurationRoot会循环调用把所有的providers 并通过IConfigurationProvider.GetReloadToken()得到FileConfigurationProvider._reloadToken,然后注册上RaiseChanged作为回调函数。以文件系统为例,当文件发生改动时会调用此回调函数,此回调函数又会调用ConfigurationRoot的_changeToken.OnReload()向外发送通知。
ConfigurationChangeTokenSource:注册的时机为ConfigurationChangeTokenSource.Configure.

我们作为使用者注册的回调事件就是注册在OptionsMonitor._onChange中。当用户使用OptionsMonitor时,其在构造方法通过DI拿到使用ConfigurationChangeTokenSource作为包装类,其包装的是ConfigurationRoot._changeToken,并把自身的事件OptionsMonitor._onChange作为回调函数注册在包装类ConfigurationChangeTokenSource.包装的ConfigurationRoot._changeToken中。自此完成了整个回调链条。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// ConfigurationRoot向IConfigurationProvider注册回调函数拼接回调链条。 public class ConfigurationRoot : IConfigurationRoot, IDisposable { _providers = providers; _changeTokenRegistrations = new List(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(); } } // ConfigurationChangeTokenSource 包装类与注册 OptionsConfigurationServiceCollectionExtensions public class ConfigurationChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions> { private IConfiguration _config; public ConfigurationChangeTokenSource(IConfiguration config) : this(Options.DefaultName, config){} public IChangeToken GetChangeToken() { return _config.GetReloadToken(); } } public static class OptionsConfigurationServiceCollectionExtensions { public static IServiceCollection Configure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(this IServiceCollection services!!, string? name, IConfiguration config!!, Action? configureBinder) where TOptions : class { services.AddOptions(); services.AddSingleton>(new ConfigurationChangeTokenSource(name, config)); return services.AddSingleton>(new NamedConfigureFromConfigurationOptions(name, config, configureBinder)); } } public class OptionsMonitor<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> : IOptionsMonitor<TOptions>, IDisposable where TOptions : class { internal event Actionstring>? _onChange; public OptionsMonitor(IOptionsFactory factory, IEnumerable> sources, IOptionsMonitorCache cache) { ChangeToken.OnChange( () => source.GetChangeToken(), (name) => InvokeChanged(name), source.Name); private void InvokeChanged(string? name) { name = name ?? Options.DefaultName; _cache.TryRemove(name); TOptions options = Get(name); if (_onChange != null) { _onChange.Invoke(options, name); } } } public IDisposable OnChange(Actionstring> listener) { var disposable = new ChangeTrackerDisposable(this, listener); _onChange += disposable.OnChange; return disposable; } }

总结

整个过程回调使用了两个ConfigurationReloadToken分别是。1. FileConfigurationProvider提供了一个ConfigurationReloadToken 2.提供了一个ConfigurationRoot._changeToken 。回调链条的拼接是。1.FileConfigurationProvider构造函数中文件的Watch与FileConfigurationProvider._reloadToken同时在这里也完成了数据的reload data 2 ConfigurationRoot的构造函数中与IConfigurationProvider._reloadToken进行的回调链条拼接 。第三次拼接是把用户注册的回调函注册在OptionsMonito的event上,OptionsMonito在构造函数中通过DI容器获取到ConfigurationRoot._changeToken中包装类。并把event作为回调函数进行注册.

通过以上代码分析,当我们向创建一个具有相同通知机制的回调链条并且有多次通知 需要利用CancellationToken与 ChangeToken.OnChange 进行链接,同时要注意每次链接后向下发送消息时,要重新生成changeToken,因为changeToken的特性是只能发送一次消息。向多次必须重新生成ChangeToken例如

复制代码
1
2
3
ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); previousToken.OnReload();

最后

以上就是愉快小兔子最近收集整理的关于Net6Configuration & Options 源码分析 Part3 IOptionsMonitor 是如何接收到配置文件变更并同步数据源的的全部内容,更多相关Net6Configuration内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部