.Net Core 自定义配置源从配置中心读取配置

配置,几乎所有的应用程序都离不开它。.Net Framework时代我们使用App.config、Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再熟悉不过了。然而到了容器化、微服务的时代,这些本地文件配置有的时候就不太合适了。当你把本地部署的服务搬到docker上后,你会发现要修改一个配置文件变的非常麻烦。你不得不通过宿主机进入容器内部来修改文件,也许容器内还不带vi等编辑工具,你连看都不能看,改都不能。更别说当你启动多个容器实例来做分布式应用的时候,一个个去修改容器的配置,这简直要命了。  因为这些原因,所以“配置中心”就诞生了。配置中心是微服务的基础设施,它对配置进行集中的管理并对外暴露接口,当应用程序需要的时候通过接口读取。配置通常为Key/Value模式,然后通过http接口暴露。好了,配置中心不多说了,感觉要偏了,这次是介绍怎么自定义一个配置源从配置中心读取配置。废话不多说直接上代码吧。

模拟配置中心

我们新建一个asp.net core webapi站点来模拟配置中心服务,端口配置到5000,并添加相应的controller来模拟配置中心对外的接口。

 添加一个configscontroller,并修改Get方法,返回2个配置键值对:

    [Route("api/[controller]")][ApiController]public class ConfigsController : ControllerBase{public List<KeyValuePair<string,string>> Get(){var configs = new List<KeyValuePair<string, string>>();configs.Add(new KeyValuePair<string, string>("SecretKey","1238918290381923"));configs.Add(new KeyValuePair<string, string>("ConnectionString", "user=123;password=123;server=."));return configs;}}

访问下/api/configs看下返回是否正确:

自定义配置源

从现在开始我们真正开始来定义一个自定义的配置源然后当程序启动的时候从配置中心读取配置文件信息,并提供给后面的代码使用配置。
新建一个asp.net core mvc站点来模拟客户端程序。

MyConfigProvider

  public class MyConfigProvider : ConfigurationProvider{/// <summary>/// 尝试从远程配置中心读取配置信息/// </summary>public async override void Load(){var response = "";try{var serverAddress = "http://localhost:5000";var client = new HttpClient();client.BaseAddress = new Uri(serverAddress);response = await client.GetStringAsync("/api/configs");}catch (Exception ex){//write err log}if (string.IsNullOrEmpty(response)){throw new Exception("Can not request configs from remote config center .");}var configs = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(response);Data = new ConcurrentDictionary<string, string>();configs.ForEach(c =>{Data.Add(c);});}}

新建一个MyConfigProvider的类,这个类从ConfigurationProvider继承,并重写其中的Load方法。使用HttpClient从配置中心读取信息后,进行反序列化,并把配置转换为字典。这里注意一下,虽然Data的类型为IDictionary,但是这里实例化对象的时候使用了ConcurrentDictionary类,因为Dictionary是非线程安全的,如果进行多线程读写会出问题。

MyConfigSource

   public class MyConfigSource : IConfigurationSource{public IConfigurationProvider Build(IConfigurationBuilder builder){return new MyConfigProvider();}}

新建一个MyConfigSource的类,这个类实现IConfigurationSource接口,IConfigurationSource接口只有一个Build方法,返回值为IConfigurationProvider,我们刚才定义的MyConfigProvider因为继承自ConfigurationProvider所以已经实现了IConfigurationProvider,我们直接new一个MyConfigProvider并返回。

MyConfigBuilderExt

   public static class MyConfigBuilderExt{public static IConfigurationBuilder AddMyConfig(this IConfigurationBuilder builder){return builder.Add(new MyConfigSource());}}

给IConfigurationBuilder定义一个AddMyConfig的扩展方法,跟.Net Core自带的几个配置源使用风格保持一致。当调用AddMyConfig的时候给IConfigurationBuilder实例添加一个MyConfigSource的源。

使用配置源

在Program中添加MyConfigSource

     public class Program{public static void Main(string[] args){CreateWebHostBuilder(args).Build().Run();}public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>WebHost.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, configBuiler) =>{configBuiler.AddMyConfig();}).UseStartup<Startup>();}

在ConfigureAppConfiguration的匿名委托方法中调用AddMyConfig扩展方法,这样程序启动的时候会自动使用MyConfigSource源并从配置中心读取配置到本地应用程序。

修改HomeController

   public class HomeController : Controller{IConfiguration _configuration;public HomeController(IConfiguration configuration){_configuration = configuration;}public IActionResult Index(){var secretKey = _configuration["SecretKey"];var connectionString = _configuration["ConnectionString"];ViewBag.SecretKey = secretKey;ViewBag.ConnectionString = connectionString;return View();}}

修改homecontroller,把IConfiguration通过构造函数注入进去,在Index Action方法中读取配置,并赋值给ViewBag

修改Index视图

 @{ViewData["Title"] = "Test my config";
}
<h3>SecretKey: @ViewBag.SecretKey
</h3>
<h3>ConnectionString: @ViewBag.ConnectionString
</h3>

修改Index视图的代码,把配置信息从ViewBag中读取出来并在网页上展示。

运行一下

先运行配置中心站点再运行一下网站,首页出现了我们在配置中心定义的SecretKey跟ConnectionString信息,表示我们的程序成功的从配置中心读取了配置信息。我们的自定义配置源已经能够成功运行了。

改进

以上配置源虽然能够成功运行,但是仔细看的话显然它有2个比较大的问题。

  • 配置中心的服务地址是写死在类里的。我们的配置中心很有可能会修改ip或者域名,写死在代码里显然不是高明之举,所以我们还是需要保留本地配置文件,把配置中心的服务地址写到本地配置文件中。

  • 配置中心作为微服务的基础设施一旦故障会引发非常严重的后果,新启动或者重启的客户端会无法正常启动。如果我们在配置中心正常的时候冗余一份配置在本地,当配置中心故障的时候从本地读取配置,至少可以保证一部分客户端程序能够正常运行。

{"Logging": {"LogLevel": {"Default": "Warning"}},"AllowedHosts": "*","myconfigServer": "http://localhost:5000"
}

修改本地appsettings.json文件,添加myconfigServer的配置信息。

 public class MyConfigProvider : ConfigurationProvider{private string _serverAddress;public MyConfigProvider(){var jsonConfig = new JsonConfigurationSource();jsonConfig.FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());jsonConfig.Path = "appsettings.json";var jsonProvider = new JsonConfigurationProvider(jsonConfig);jsonProvider.Load();jsonProvider.TryGet("myconfigServer", out string serverAddress);if (string.IsNullOrEmpty(serverAddress)){throw new Exception("Can not find myconfigServer's address from appsettings.json");}_serverAddress = serverAddress;}/// <summary>/// 尝试从远程配置中心读取配置信息,当成功从配置中心读取信息的时候把配置写到本地的myconfig.json文件中,当配置中心无法访问的时候尝试从本地文件恢复配置。/// </summary>public async override void Load(){var response = "";try{var client = new HttpClient();client.BaseAddress = new Uri(_serverAddress);response = await client.GetStringAsync("/api/configs");WriteToLocal(response);}catch (Exception ex){//write err logresponse = ReadFromLocal();}if (string.IsNullOrEmpty(response)){throw new Exception("Can not request configs from remote config center .");}var configs = JsonConvert.DeserializeObject<List<KeyValuePair<string, string>>>(response);Data = new ConcurrentDictionary<string, string>();configs.ForEach(c =>{Data.Add(c);});}private void WriteToLocal(string resp){var file = Directory.GetCurrentDirectory() + "/myconfig.json";File.WriteAllText(file,resp);}private string ReadFromLocal(){var file = Directory.GetCurrentDirectory() + "/myconfig.json";return File.ReadAllText(file);}}

修改MyConfigProvider,修改构造函数,通过JsonConfigurationProvider从本地读取appsettings.json中的myconfigServer配置信息。新增WriteToLocal方法把配置中心返回的json数据写到本地文件中。新增ReadFromLocal方法,从本地文件读取json信息。

再次运行

先运行配置中心站点,再运行客户端网站,可以看到配置信息展示到首页界面上。关闭配置中心客跟客户端网站,并且重启客户端网站依然能够展示配置信息,说明自定义配置源当配置中心故障的时候成功从本地文件恢复了配置。图跟上面的图是一致的,就不贴了。

总结

通过以上我们定义了一个比较简单的自定义配置源,它能够通过http从配置中心读取配置,并且提供了同传统json配置文件一致的使用风格,最大程度的复用旧代码,减少因为引入配置中心而大规模改动代码。我们从上面的代码可以更清楚的知道.Net Core的配置源是如何工作的。ConfigurationSource只是ConfigurationProvider的建造器。真正完成配置加载、查找工作的是ConfigurationProvider。
以上代码还是演示级别的代码,还有很多改进的空间,比如http访问失败的重试,我们可以使用polly重构;比如支持定时从配置中心刷新配置等,有兴趣可以自己去实践一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308534.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Redis6]Bitmaps与set对比

Bitmaps与set对比 但Bitmaps并不是万金油&#xff0c; 假如该网站每天的独立访问用户很少&#xff0c; 例如只有10万&#xff08;大量的僵尸用户&#xff09; &#xff0c; 那么两者的对比如下表所示&#xff0c; 很显然&#xff0c; 这时候使用Bitmaps就不太合适了&#xff0c…

MySQL分布式ID_分布式唯一ID系列(3)——数据库自增ID机制适合做分布式ID吗

数据库自增ID机制原理介绍在分布式里面&#xff0c;数据库的自增ID机制的主要原理是&#xff1a;数据库自增ID和mysql数据库的replace_into()函数实现的。这里的replace数据库自增ID和mysql数据库的replace_into()函数实现的。这里的replace into跟insert功能类似&#xff0c;不…

7-15 QQ帐户的申请与登陆 (25 分)(map做法+思路分析)

一&#xff1a;题目 实现QQ新帐户申请和老帐户登陆的简化版功能。最大挑战是&#xff1a;据说现在的QQ号码已经有10位数了。 输入格式: 输入首先给出一个正整数N&#xff08;≤10 ​5 ​​ &#xff09;&#xff0c;随后给出N行指令。每行指令的格式为&#xff1a;“命令符&a…

[Redis6]新数据类型_HyperLogLog

HyperLogLog 简介 在工作当中&#xff0c;我们经常会遇到与统计相关的功能需求&#xff0c;比如统计网站PV&#xff08;PageView页面访问量&#xff09;,可以使用Redis的incr、incrby轻松实现。 但像UV&#xff08;UniqueVisitor&#xff0c;独立访客&#xff09;、独立IP数…

每天都在支付,你真的了解信息流和资金流?

作为一个财务类的产品经理&#xff0c;除了每天被财务“虐待”千百遍&#xff0c;还需要对整个资金流向很清楚&#xff1a;钱给谁&#xff0c;怎么给&#xff0c;怎么做逆向流程&#xff0c;谁参与容错等。财务很在意资金的流转安全&#xff0c;但又极不愿意花时间关注它。诸如…

mysql改表字段类型导致数据丢失_故障分析 | 记一次 MySQL 主从双写导致的数据丢失问题【转】...

一、问题起源不久前用户反馈部门的 MySQL 数据库发生了数据更新丢失。为了解决这个问题&#xff0c;当时对用户使用的场景进行了分析。发现可能是因为用户在两台互为主从的机器上都进行了写入导致的数据丢失。如图所示&#xff0c;是正常和异常情况下应用写入数据库的示例。随后…

[Redis6]新数据类型_Geospatial

Geospatial 简介 Redis 3.2 中增加了对GEO类型的支持。GEO&#xff0c;Geographic&#xff0c;地理信息的缩写。该类型&#xff0c;就是元素的2维坐标&#xff0c;在地图上就是经纬度。redis基于该类型&#xff0c;提供了经纬度设置&#xff0c;查询&#xff0c;范围查询&…

《Apache SkyWalking实战》送书活动结果公布

截至2020.07.31 本次送书活动 这么多Apache顶级项目&#xff0c;SkyWalking为何一枝独秀&#xff1f;&#xff0c;本次很多同学在看到活动的书&#xff0c;自行就到网上购买了书。下面把Top 8的留言截图给大家回顾一下。以下8位同学将获赠书籍一本&#xff1a;landon、田晓青、…

7-16 一元多项式求导 (20 分)(详解+题目分析)

7-16 一元多项式求导 (20 分) 1&#xff1a;题目 设计函数求一元多项式的导数。 输入格式: 以指数递降方式输入多项式非零项系数和指数&#xff08;绝对值均为不超过1000的整数&#xff09;。数字间以空格分隔。 输出格式: 以与输入相同的格式输出导数多项式非零项的系数和…

[MyBatisPlus]通用枚举

通用枚举 数据库表添加字段sex 创建通用枚举类型 package com.xxxx.mybatisplus.enums;import com.baomidou.mybatisplus.annotation.EnumValue; import lombok.Getter;Getter public enum SexEnum {MALE(1,"男"),FEMALE(2,"女");EnumValue // 将注解所标…

信创产业发展应不忘初心牢记使命

受国际大环境影响&#xff0c;信创市场国产化替代步伐已然加速。由于其中存在巨大的商机&#xff0c;信创市场中已然出现一些乱象。有的厂商拿洋技术标榜和宣传自主&#xff1b;有的厂商全国各地跑马圈地建产业园&#xff0c;进而向当地政府要政策要市场&#xff1b;有的厂商恶…

mysql采用 级触发_Mysql高级之触发器(trigger)

触发器是一类特殊的事务 ,可以监视某种数据操作(insert/update/delete),并触发相关操作(insert/update/delete)。看以下事件&#xff1a;完成下单与减少库存的逻辑Insert into o (gid,num) values (2,3); // 插入语句Update g set goods_num goods_num - 3 where id 2;// 更新…

[MyBatisPlus]代码生成器

代码生成器 引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.freemarker</groupId…

飞机游戏项目完整代码(详解JAVA300集)

一&#xff1a;简介 这是一个可以通过键盘移动飞机位置 然后可以躲避 炮弹的打击 &#xff0c;游戏结束为 当炮弹与飞机发生碰撞 则 最后显示出飞机存活的时长 代表游戏结束 二&#xff1a;上码 1&#xff1a;运行游戏的类 package src.src.cn.wyj.飞机;import java.awt.Co…

[MyBatisPlus]模拟多数据源环境及测试

模拟多数据源环境 多数据源 适用于多种场景&#xff1a;纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景&#xff0c;其他场景类似 场景说明&#xff1a; 我们创建两个库&#xff0c;分别为&#xff1a;mybatis_plus&#xff08;以前…

生活有哪些残忍的真相?

1.很多人拼尽全力&#xff0c;只是为了艰难的生存。2.大多数人一点也不喜欢读书&#xff0c;只是喜欢被别人贴上“爱读书”的标签罢了。3.你越没能力&#xff0c;社交圈质量就越低&#xff0c;碰到的傻逼就越多。4.职场绝不能追求快乐&#xff0c;只能追求痛苦的成长。5.抬起头…

7-17 汉诺塔的非递归实现 (25 分)(思路分析)

一&#xff1a;题目 借助堆栈以非递归&#xff08;循环&#xff09;方式求解汉诺塔的问题&#xff08;n, a, b, c&#xff09;&#xff0c;即将N个盘子从起始柱&#xff08;标记为“a”&#xff09;通过借助柱&#xff08;标记为“b”&#xff09;移动到目标柱&#xff08;标记…

[MyBatisPlus]MyBatisX插件

MyBatisX插件 MyBatis-Plus为我们提供了强大的mapper和service模板&#xff0c;能够大大的提高开发效率 但是在真正开发过程中&#xff0c;MyBatis-Plus并不能为我们解决所有问题&#xff0c;例如一些复杂的SQL&#xff0c;多表联查&#xff0c;我们就需要自己去编写代码和SQ…

7-19 求链式线性表的倒数第K项 (20 分)(思路分析+极简代码+超容易理解)

一&#xff1a;题目描述&#xff1a; 给定一系列正整数&#xff0c;请设计一个尽可能高效的算法&#xff0c;查找倒数第K个位置上的数字。 输入格式: 输入首先给出一个正整数K&#xff0c;随后是若干非负整数&#xff0c;最后以一个负整数表示结尾&#xff08;该负数不算在序…

很认真地聊一下 “选择比努力更重要”

这是头哥侃码的第211篇原创两周前&#xff0c;蚂蚁集团官宣上市计划&#xff0c;在科创板和港交所寻求同步上市的消息&#xff0c;刷爆了朋友圈。消息爆出的第二天&#xff0c;我就看到在某个微信群里的一位 “热心” 小伙伴给他在蚂蚁金服的朋友算了一笔账&#xff0c;说是能套…