基于选项模式实现.NET Core的配置热更新( 三 )


基于选项模式实现.NET Core的配置热更新文章插图
IChnageToken现在 , 让我们回到本文的主题 , 博主你不是要说配置热更新这个话题吗?截至到目前为止 , 我们修改配置文件的时候 , ASP.NET Core 应用明明就会更新配置啊 , 所以 , 博主你到底想说什么?其实 , 博主想说的是 , 的确我们的目的已经达到了 , 但我们不能永远停留在“知其然”的水平 , 如果不试图去了解内在的机制 , 当我们去尝试实现一个自定义配置源的时候 , 就会遇到一些你没有办法想明白的事情 。 所以 , 接下来要讲的 IChnageToken 这个接口可以说是非常重要 。
首先 , 我们把目光聚焦到 CreateDefaultBuilder这个方法 , 它通常在入口文件Program.cs 中被调用 , 主要作用是构造一个 IWebHostBuilder 实例并返回 , 下面是这个方法的内部实现 , 博主这里对其进行了精简:
public static IWebHostBuilder CreateDefaultBuilder(string[] args){ //以下简化后的代码片段 builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != ) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables; if (args != ) { config.AddCommandLine(args); } })}可以注意到 , 通过 ConfigureAppConfiguration方法 , 框架主要做了下面的工作:

  • 从 appsettings.json 和appsettings.${env.EnvironmentName}.json 两个配置文件中加载配置
  • 从机密管理器中加载配载
  • 从环境变量中加载配置
  • 从命令行参数中加载配置
实际上 , .NET Core 可以从配置文件、环境变量、Azure Key Vault、Azure 应用程序配置、命令行参数、已安装或已创建的自定义提供程序、目录文件、内存中的 .NET 对象等各种各样的来源中加载配置 , 这里的 appsettings.json 使用的是JsonConfigurationProvider 类 , 位于Microsoft.Extensions.Configuration.Json这个命名空间 , 可以注意到 , 它继承自FileConfigurationProvider类 , 并重写了Load 方法 , 通过这些关系 , 我们最终可以找到这样一段代码:
public FileConfigurationProvider(FileConfigurationSource source){ if (source == ) { throw new ArgumentException(nameof(source)); } Source = source; if (Source.ReloadOnChange Load(reload: true); }); }}所以 , 真相就是,所有基于文件的配置提供者 , 都依赖于 FileConfigurationSource , 而通过FileConfigurationSource 暴露出来的FileProvider 都具备监视文件变化的能力 , 更本质上的代码其实应该是下面这样:
//ChangeToken + IFileProvider 实现对文件的监听var filePath = @"C:\Users\admin\Downloads\孔乙己.txt";var directory = System.IO.Path.GetDirectoryName(filePath);var fileProvider = new PhysicalFileProvider(directory);ChangeToken.OnChange(=> fileProvider.Watch("孔乙己.txt"),=> { _logger.LogInformation("孔乙己 , 你一定又偷人家书了吧!"); });所以 , 真相只有一个 , 真正帮助我们实现配置热更新的 , 其实是 IChangeToken 这个接口 , 我们只需要把这样一个实例传入到ChangeToken.OnChange 方法中 , 就可以在特定的时机触发这个回调函数 , 而显然 , 对于大多数的IConfigurationProvider 接口而言 , 这个回调函数其实就是Load 方法 , 关于微软提供的ChangeToken 静态类的实现 , 大家如果有兴趣去了解的话 , 可以参考这里: 。 话说回来 , 我们说IOptionsSnapshot和IOptionsMonitor是响应式的 , 当配置发生改变的时候 , 它们对应的值会跟着改变 , 从某种意义上来说 , 是因为IChangeToken提供了这样一个可以监听变化的的能力 , 试想一下 , 我们只需要给每一个IConfigurationProvider对应的IChangeToken注册相同的回调函数 , 那么 , 当某一个IConfigurationProvider 需要重新加载的时候 , 我们就可以针对这个IConfigurationProvider里对应的键值对进行处理 。 事实上 , 微软官方在实现IConfigurationRoot 的时候 , 的确就是这样做的: