.NET 5 自定义配置(动态配置,配置中心) 案例版

.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)

最后都成为了以上的结构,按照这个结构来存储(如果是配置中心可能存在数据库)

如何让配置动态修改,并能被监听到

其中有这几个类

  1. IOptions<T> (不会读取配置的更改)
  2. IOptionsMonitor<T> (会获取最新的配置的,更改后的)
  3. 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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝创精英团队

你的支持是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值