.NET 5 自定义配置(动态配置,配置中心) 案例版
为啥要自定义配置
有些功能可能自己用着简单,不需要那么复杂。想着简单粗暴的解决问题,可控的实现代码控制,就可以用这样的方式实现。
另外,如果对配置中心的原理,以及对.NET 底层的配置深入理解,就得动手实现一下,才能更深入的了解此功能,对吧。
.Net Core 配置的简单用法
统一按照依赖注入的方法获取对象
构造注入 以下对象
IConfiguration configuration
注: 否则可以自己构造对象
configuration = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
配置如何动态更改还能被读到呢
假设配置如下:
{
"serverUrl": "http://192.168.1.4:7890",
"AliOSSConfig": {
"Endpoint": "123",
"AccessKeyId": "456",
"AccessKeySecret": "789",
"BucketName": "6666"
},
"ossUrl": "publish",
"servers": [ "RouteHost", "TestHost" ]
}
读取相应的值(大致有以下几个用法)
//直接读取相关配置信息
var ossUrl = configuration.GetValue<string>("ossUrl");
var serverUrl = configuration["serverUrl"];
var Endpoint = configuration.GetValue<string>("AliOSSConfig:Endpoint");
var AccessKeyId = configuration.GetSection("AliOSSConfig").GetSection("AccessKeyId").Value;
var servers0 = configuration.GetValue<string>("servers:0");
var servers1 = configuration.GetValue<string>("servers:1");
var AliOSSConfigOjb = new AliOSSConfig();
configuration.GetSection("AliOSSConfig").Bind(AliOSSConfigOjb);
配置节结构
首先不论你是啥文件方式,INI,JSON,YAML,XML等。
对于 .NET 的 配置对象 IConfiguration 来讲,都统一转换成了一个统一的结构,
其实,从上边的读取用例来看,我们其实已经知道结构是啥了
key (单独key)
key:key (获取子key)
key:key:0 (获取集合第0个)
最后都成为了以上的结构,按照这个结构来存储(如果是配置中心可能存在数据库)
如何让配置动态修改,并能被监听到
其中有这几个类
- IOptions<T> (不会读取配置的更改)
- IOptionsMonitor<T> (会获取最新的配置的,更改后的)
- IOptionsSnapshot<T> (下一次读取的是最新的更改,更改后的)
所以,我们选择 IOptionsMonitor 作为 依赖注入的对象即可
大致如下 :
IOptionsMonitor<AliOSSConfig> optionsMonitor;
public HomeController(ILogger<HomeController> logger, IOptionsMonitor<AliOSSConfig> optionsMonitor)
{
_logger = logger;
this.optionsMonitor = optionsMonitor;
this.optionsMonitor.OnChange(_ => Console.WriteLine(_.BucketName));
}
public IActionResult Index()
{
return Json(optionsMonitor.CurrentValue);
}
用着还是很方便,直接获取的就是最新的配置信息了。
自定义的配置项目如下:
基本就是如下的结构了
现在,我们来 看看每个方法到底实现了啥子
appsettings.json
先看具体的配置
{
"serverUrl": "http://192.168.1.4:7890",
"AliOSSConfig": {
"Endpoint": "123",
"AccessKeyId": "456",
"AccessKeySecret": "789",
"BucketName": "6666"
},
"ossUrl": "publish",
"servers": [ "RouteHost", "TestHost" ]
}
跟上边的示例一样,没啥大的变化。
AliOSSConfig.cs
对应的实体对象
public class AliOSSConfig
{
public string Endpoint { get; set; }
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string BucketName { get; set; }
}
ConfigSource (配置源)
/// <summary>
/// 配置源
/// </summary>
public class ConfigSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new ConfigProvider();
}
}
ConfigProvider (配置提供者)
ConfigProvider 是我们配置的核心类,也是实现动态配置以及配置中心的核心类,可以认真看一下。
其中 Load() 方法就是 动态更新配置信息的主要入口点。
可以通过 各种方式来实现 配置的动态更新。
/// <summary>
/// 配置提供者
/// </summary>
public class ConfigProvider : ConfigurationProvider, IDisposable
{
private bool isDisposed = false;
public ConfigProvider()
{
ThreadPool.QueueUserWorkItem(obj =>
{
while (!isDisposed)
{
Load();
Thread.Sleep(3 * 1000);
}
});
}
/// <summary>
/// 读写锁
/// </summary>
private ReaderWriterLockSlim lockObj = new ReaderWriterLockSlim();
public override IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
{
lockObj.EnterReadLock();
try
{
return base.GetChildKeys(earlierKeys, parentPath);
}
finally
{
lockObj.ExitReadLock();
}
}
public override bool TryGet(string key, out string value)
{
lockObj.EnterReadLock();
try
{
return base.TryGet(key, out value);
}
finally
{
lockObj.ExitReadLock();
}
}
/// <summary>
/// 更新数据
/// 内部数据结构: Name
/// Name:asdfd:asdf:0
/// Name:asdfd:asdf:1
/// </summary>
public override void Load()
{
try
{
lockObj.EnterWriteLock();
//模拟动态更改指定配置,后期可以用配置中心 数据库 信息读取等实现
Data["AliOSSConfig:BucketName"] = DateTime.Now.ToString();
}
finally
{
lockObj.ExitWriteLock();
}
OnReload();
}
public void Dispose()
{
isDisposed = true;
}
}
ConfigExtensions(配置扩展)
这个方法主要是为了方便实现功能的使用
如下 (ASP.NET Core 项目内)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration((context, builder) =>
{
//增加我们自己写的自定义配置
builder.AddCustomConfig();
});
具体的代码
/// <summary>
/// 增加配置扩展
/// </summary>
public static class ConfigExtensions
{
/// <summary>
/// 自定义扩展,增加配置
/// </summary>
public static IConfigurationBuilder AddCustomConfig(this IConfigurationBuilder builder, IConfiguration configuration = null)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.Add(new ConfigSource());
}
}
简单的测试方法 ( main )
IConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
var configuration = configBuilder.Build();
//直接读取相关配置信息
var ossUrl = configuration.GetValue<string>("ossUrl");
var serverUrl = configuration["serverUrl"];
var Endpoint = configuration.GetValue<string>("AliOSSConfig:Endpoint");
var AccessKeyId = configuration.GetSection("AliOSSConfig").GetSection("AccessKeyId").Value;
var servers0 = configuration.GetValue<string>("servers:0");
var servers1 = configuration.GetValue<string>("servers:1");
var AliOSSConfigOjb = new AliOSSConfig();
configuration.GetSection("AliOSSConfig").Bind(AliOSSConfigOjb);
configBuilder.AddCustomConfig();
configuration = configBuilder.Build();
var serviceCollection = new ServiceCollection();
serviceCollection.Configure<AliOSSConfig>(configuration.GetSection("AliOSSConfig"));
var serviceProvider = serviceCollection.BuildServiceProvider();
var _options = serviceProvider.GetRequiredService<IOptionsMonitor<AliOSSConfig>>();
_options.OnChange(_ => Console.WriteLine(_.BucketName));
// Task.Run(() => {
//=
// while (true)
// {
// Thread.Sleep(2000);
// Console.WriteLine(_options.CurrentValue.Name);
// }
// });
Console.WriteLine("获取到配置信息!");
Console.ReadLine();
源码结束
以上就是整体的一个自定义动态配置的实现。
应用到ASP.NET Core MVC 项目中
只需要引用此项目,然后,在以下位置增加代码即可使用
主要是以下三个地方
Program
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration((context, builder) =>
{
//增加我们自己写的自定义配置
builder.AddCustomConfig();
});
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.Configure<AliOSSConfig>(Configuration.GetSection("AliOSSConfig"));//配置需要绑定的配置节点
}
HomeController.cs
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
IOptionsMonitor<AliOSSConfig> optionsMonitor;
public HomeController(ILogger<HomeController> logger, IOptionsMonitor<AliOSSConfig> optionsMonitor)
{
_logger = logger;
this.optionsMonitor = optionsMonitor;
this.optionsMonitor.OnChange(_ => Console.WriteLine(_.BucketName));
}
public IActionResult Index()
{
return Json(optionsMonitor.CurrentValue);
}
}
结果如下
可以看到 BucketName 在以我们让它3秒更新一次配置的信息在更新。
完结
至此,整个自定义配置就完结了。可以根据自己的需要去扩展。
代码位置
https://github.com/kesshei/ConfigDemo.git