如何实现配置与源文件的同步
配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置;第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置。接下来我们利用一个简单的.NET Core控制台应用来演示针对文件的配置会涉及到数据同步的问题,我们希望应用能够对原始配置文件实施监控,并在文件内容发生改变的时候从新加载并应用新的配置。针对JSON文件的配置源通过JsonConfigurationSource类型来表示,该类型定义在“Microsoft.Extensions.Configuration.Json”这个NuGet包中,所以我们需要在project.json文件中按照如下的形式添加针对这个NuGet包的依赖。[ 本文已经同步到《ASP.NET Core框架揭秘》之中]
1: {
2: ...
3: "dependencies": {
4: ...
5: "Microsoft.Extensions.Configuration.Json": "1.0.0"
6: }
7: }
假设我们需要通过配置来当前应用使用的线程池的容量,这样的设置需要根据当前的负载进行调整,所以需要很高的时效性,我们希望一旦修改了JSON文件的配置,应用程序中针对线程池的相关设置可以立即生效。简单起见,我们仅仅定义MinThreads 和MaxThreads这两个分别决定线程池容量区间的配置项,如下所示的ThreadPoolOptions是对应的Options类型。
1: public class ThreadPoolOptions
2: {
3: public int MinThreads { get; set; }
4: public int MaxThreads { get; set; }
5:
6: public override string ToString()
7: {
8: return $"Thread pool size: [{MinThreads}, {MaxThreads}]";
9: }
10: }
我们在项目中添加一个名为threadPool.json的文件来定义线程池的配置。除此之外,我们需要通过修改project.json与编译相关的配置让该文件在编译的时候自动拷贝到输出目录下(默认为bin目录)。具体来说,我们只需要按照如下的方式将该文件的路径设置为“builtOptions/copyToOutput”配置选购的值就可以了。
1: {
2: ...
3: "buildOptions": {
4: ...
5: "copyToOutput": "threadPool.json"
6: }
7: }
接下来我们编写了如下一段程序来演示应用中使用的配置如何与配置文件的内容保持同步。我们首先创建了一个ConfigurationBuilder对象,并在它上面注册了一个JsonConfigurationSource。在创建这个JsonConfigurationSource对象的时候,除了指定配置文件(“threadPool.json”)的路径之外,我们还将它的ReloadOnChange属性设置为True。顾名思义,这个ReloadOnChange属性的含义就是当原始配置文件的内容发生改变的时候是否需要重新加载配置。
1: IConfiguration config = new ConfigurationBuilder()
2: .AddJsonFile(new JsonConfigurationSource {Path = "threadPool.json", ReloadOnChange = true })
3: .Build();
4:
5: Action changeCallBack = () => {
6: ThreadPoolOptions options = new ServiceCollection()
7: .AddOptions()
8: .Configure<ThreadPoolOptions>(config)
9: .BuildServiceProvider()
10: .GetService<IOptions<ThreadPoolOptions>>()
11: .Value;
12: Console.WriteLine(options);
13: };
14:
15: ChangeToken.OnChange(()=>config.GetReloadToken(), changeCallBack);
16:
17: Random random = new Random();
18: while (true)
19: {
20: ThreadPoolOptions options = new ThreadPoolOptions
21: {
22: MinThreads = random.Next(10, 20),
23: MaxThreads = random.Next(40, 50)
24: };
25: File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "threadPool.json"), JsonConvert.SerializeObject(options));
26: Task.Delay(5000).Wait();
27: }
在利用ConfigurationBuilder得到Configuration对象之后,我们调用它的GetReloadToken方法得到一个ChangeToken对象,后者会帮助我们判断配置是否被重新加载。我们调用ChangeToken类型的静态方法OnChange为这个ChangeToken对象注册了一个回调,该回调会在配置被重新加载时自动执行。至于这个注册的回调,我们仅仅是采用Options模式得到配置绑定生成的ThreadPoolOptions对象,并将它的相关信息打印在控制台上。
在这段程序的最后,我们在一个无限循环中以5秒钟的间隔对threadPool.json文件进行更新。按照这段程序的意图,当我们每次完成了针对threadPool.json的更新之后,我们创建的Configuration对象会自动重新加载。Configuration一旦重新加载,之前调用它的GetReloadToken方法得到ChangeToken对象的HasChanged属性将变成True,注册在它上面的回调将被执行。所以最终的结果就是重新设置的配置会实时出现在控制台上,如下所示的输出结果证实了这一点。(S04)