.NET,CORE,2.0小白笔记(五):配置的热更新、配置的框架设计

一、引言

在一个应用程序中,通常需要用到一些配置信息,例如数据库连接字符串、日志级别、缓存配置等等。通常情况下,这些配置信息是写在配置文件中的。在 .NET Core 中,配置是一个非常重要的模块,它提供了一个统一的方式来管理和访问应用程序的配置信息。本文将介绍 .NET Core 2.0 中的配置功能,并着重介绍了配置的热更新和配置的框架设计。

二、配置的基本用法

在 .NET Core 中,配置信息通常存储在 appsettings.json 文件中,它是以 JSON 格式记录的。下面是一个示例:

```json

{

"ConnectionStrings": {

"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0"

},

"Logging": {

"LogLevel": {

"Default": "Warning"

}

}

}

```

通过使用 IConfiguration 接口来读取和访问配置信息。在一个 ASP.NET Core 应用程序中,可以通过依赖注入注入给控制器、服务等组件使用。以下是一个示例:

```csharp

using Microsoft.Extensions.Configuration;

public class HomeController : Controller

{

private readonly IConfiguration _configuration;

public HomeController(IConfiguration configuration)

{

_configuration = configuration;

}

public IActionResult Index()

{

var connectionString = _configuration.GetConnectionString("DefaultConnection");

var logLevel = _configuration["Logging:LogLevel:Default"];

// ...

}

}

```

在上面的示例中,我们注入了一个 IConfiguration 对象,并在 Index 方法中使用了它来获取连接字符串和日志级别。

三、配置的热更新

在一个应用程序运行时,如果需要修改配置信息,例如更改日志级别或数据库连接字符串,传统的做法是停止应用程序,修改配置文件,再重新启动应用程序。这种方式虽然可行,但有一些缺点:

1. 中断服务:在修改配置文件和重新启动应用程序之间,应用程序将中断服务;

2. 风险高:修改配置文件可能会导致一些潜在的风险,例如修改错误的连接字符串可能会导致应用程序无法连接数据库;

3. 不方便:修改配置文件需要手动操作,不方便。

为了解决这些问题,.NET Core 提供了配置的热更新功能,即在应用程序运行时修改配置信息而不需要重新启动应用程序,这种方式既安全又方便。

.NET Core 中的配置热更新有两种方式:

1. 监听文件:当配置文件发生改变时,重新加载配置信息;

2. 使用 IOptionsSnapshot<>:通过注入 IOptionsSnapshot<>,在需要获取配置信息时自动获取最新的配置信息。

以下是两种方式的示例:

1.监听文件

```csharp

using System.IO;

using Microsoft.Extensions.Configuration;

public static void Main(string[] args)

{

var builder = new ConfigurationBuilder()

.SetBasePath(Directory.GetCurrentDirectory())

.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); // 监听配置文件

var configuration = builder.Build();

var connectionString = configuration.GetConnectionString("DefaultConnection");

var logLevel = configuration["Logging:LogLevel:Default"];

Console.WriteLine($"connectionString: {connectionString}");

Console.WriteLine($"logLevel: {logLevel}");

// 等待文件修改

while (true)

{

Thread.Sleep(1000); // 1秒监测一次

if (File.Exists("appsettings.json"))

{

var lastModified = File.GetLastWriteTimeUtc("appsettings.json");

if (lastModified > configurationReloadTime) // 配置文件修改

{

configurationReloadTime = lastModified;

var newConnectionString = configuration.GetConnectionString("DefaultConnection");

var newLogLevel = configuration["Logging:LogLevel:Default"];

Console.WriteLine($"New configuration loaded. connectionString: {newConnectionString}, logLevel: {newLogLevel}");

}

}

}

}

```

在上面的示例中,我们使用了 SetBasePath 和 AddJsonFile 方法来加载 appsettings.json 文件,并设置 reloadOnChange 参数为 true,表示需要监听该文件的变化。当文件变化时,配置信息将会重新加载。

2.使用 IOptionsSnapshot<>

```csharp

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Options;

public class HomeController : Controller

{

private readonly ConnectionStringsConfiguration _connectionStringsConfiguration;

private readonly LoggingConfiguration _loggingConfiguration;

public HomeController(IOptionsSnapshot connectionStringsConfiguration,

IOptionsSnapshot loggingConfiguration)

{

_connectionStringsConfiguration = connectionStringsConfiguration.Value; // 自动获取配置信息

_loggingConfiguration = loggingConfiguration.Value;

}

public IActionResult Index()

{

var connectionString = _connectionStringsConfiguration.DefaultConnection;

var logLevel = _loggingConfiguration.LogLevel;

// ...

}

}

public class ConnectionStringsConfiguration

{

public string DefaultConnection { get; set; }

}

public class LoggingConfiguration

{

public string LogLevel { get; set; }

}

public void ConfigureServices(IServiceCollection services)

{

services.Configure(_configuration.GetSection("ConnectionStrings"));

services.Configure(_configuration.GetSection("Logging"));

// ...

}

```

在上面的示例中,我们将 Configuration 对象中的 ConnectionStrings 和 Logging 配置节点绑定到 ConnectionStringsConfiguration 和 LoggingConfiguration 类中,并注册到 DI 中。然后,在 HomeController 中注入 IOptionsSnapshot 和 IOptionsSnapshot 对象,通过 Value 属性自动获取最新的配置信息。

四、配置的框架设计

在 .NET Core 中,配置模块的实现可分为三层:

1. Configuration:表示配置模块中的核心接口,提供了访问配置信息的方法;

2. Configuration.Providers:表示配置的提供者,各种不同的配置信息都是由不同的提供者提供的;

3. Configuration.Binder:表示配置的绑定器,用于将配置信息绑定到 .NET 类型中。

总体框架如下图所示:

![image](https://user-images.githubusercontent.com/47147484/136487054-56b615a9-cebd-49c4-96a0-83097d265018.png)

1. IConfiguration

IConfiguration 是配置模块中的核心接口,它定义了读取配置信息的方法。在使用 IConfiguration 接口时,可以使用以下方法:

```csharp

IConfiguration configuration;

// 获取连接字符串

var connectionString = configuration.GetConnectionString("DefaultConnection");

// 直接读取配置值

var logLevel = configuration["Logging:LogLevel:Default"];

```

2. IConfigurationBuilder

IConfigurationBuilder 是用于构建 IConfiguration 对象的工厂方法,每当需要创建一个 IConfiguration 对象时,都需要使用 IConfigurationBuilder。在使用 IConfigurationBuilder 构建 IConfiguration 对象时,需要设置以下参数:

1. SetBasePath:设置配置文件的基础路劲,用于防止相对路径问题;

2. AddJsonFile:添加一个 JSON 配置文件;

3. AddInMemoryCollection:添加一个内存配置;

4. AddEnvironmentVariables:添加环境变量配置;

5. AddCommandLine:添加命令行参数配置。

以下是一个使用 IConfigurationBuilder 的例子:

```csharp

var builder = new ConfigurationBuilder()

.SetBasePath(Directory.GetCurrentDirectory())

.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)

.AddEnvironmentVariables();

var configuration = builder.Build();

```

在上面的示例中,我们通过 SetBasePath 方法设置 appsettings.json 文件的基础路径,然后通过 AddJsonFile 方法添加了两个 JSON 配置文件,后者根据不同的环境自动选择不同的配置文件。最后,通过 AddEnvironmentVariables 方法添加了环境变量配置。

3. IConfigurationProvider

IConfigurationProvider 是配置的提供者接口,它负责提供配置信息。每当需要读取配置信息时,都需要通过 IConfigurationProvider 来读取配置信息。在使用 IConfigurationProvider 时,需要实现以下方法:

1. Load:用于加载配置信息;

2. GetReloadToken:用于获取监控配置变更的 ReloadToken 对象;

3. TryGet:用于从内存中获取配置值;

4. Set:用于将配置值存储到内存中。

以下是一个 IConfigurationProvider 接口的示例:

```csharp

public interface IConfigurationProvider

{

void Load();

IChangeToken GetReloadToken();

IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath);

bool TryGet(string key, out string value);

void Set(string key, string value);

}

```

4. IConfigurationRoot

IConfigurationRoot 是 IConfiguration 接口的默认实现,它实现了访问配置信息的方法。在 IConfigurationRoot 内部,维护了一个 IConfigurationProvider 的列表,在需要读取配置信息时,会遍历全部的 IConfigurationProvider,直到找到正确的配置值。以下是一个 IConfigurationRoot 的示例:

```csharp

public class ConfigurationRoot : IConfigurationRoot

{

private readonly IEnumerable _providers;

public ConfigurationRoot(IEnumerable providers)

{

_providers = providers;

}

public IEnumerable Providers => _providers;

public string this[string key]

{

get

{

foreach (var provider in Providers.Reverse())

{

if (provider.TryGet(key, out var value))

{

return value;

}

}

return null;

}

set

{

foreach (var provider in Providers)

{

if (provider.TryGet(key, out var _))

{

provider.Set(key, value);

break;

}

}

}

}

public IConfigurationSection GetSection(string key)

{

foreach (var provider in Providers.Reverse())

{

var configurationSection = provider.GetSection(key);

if (configurationSection.Exists())

{

return configurationSection;

}

}

return new ConfigurationSection(this, key);

}

public IEnumerable GetChildren()

{

return GetChildrenImplementation(null);

}

public IChangeToken GetReloadToken()

{

var changeTokenProviders = Providers.OfType();

if (changeTokenProviders.Any())

{

return new CompositeChangeToken(changeTokenProviders.Select(p => p.GetReloadToken()));

}

return NullChangeToken.Singleton;

}

}

```

在上面的代码中,我们实现了 IConfigurationRoot 接口,并且维护了一个 IConfigurationProvider 的列表。在获取配置信息时,会根据先后顺序依次调用 IConfigurationProvider 的 TryGet 方法。在获取子级配置时,会调用 IConfigurationProvider 的 GetSection 方法。在监视配置变更时,会调用 IReloadTokenProvider 接口的 GetReloadToken 方法。因此,当配置信息发生变更时,IConfigurationRoot 将会调用 IReloadTokenProvider 的 GetReloadToken 方法,并通过 ChangeToken 触发配置变更事件。

总的来说,.NET Core 的配置模块提供了一种集中化的方式来管理配置信息,并通过 IConfigurationProvider 和 IConfigurationRoot 实现了配置信息的读取、存储和监视等功能,而通过 IConfiguration 接口和 IConfigurationBuilder 工厂方法可以方便地使用和构建 IConfiguration 对象。配置的热更新功能则进一步提升了应用程序的可用性和灵活性。


点赞(22) 打赏
如果你喜欢我们的文章,欢迎您分享或收藏为众码农的文章! 我们网站的目标是帮助每一个对编程和网站建设以及各类acg,galgame,SLG游戏感兴趣的人,无论他们的水平和经验如何。我们相信,只要有热情和毅力,任何人都可以成为一个优秀的程序员。欢迎你加入我们,开始你的美妙旅程!www.weizhongchou.cn

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部