第三节:基于Consul做服务的配置中心

一. 简介

本节架构图:

(PS:该图仅服务于本节,完整版的微服务架构图见后最后章节)

1.什么是配置中心

 配置中心通俗的说就是在程序不重启的情况下,动态的修改程序的配置文件。

 通俗的解释:在微服务体系中,实际上就是把各个业务服务器的配置文件 eg:appsettings.json或其它配置文件中的内容copy到配置中心中,然后将业务服务器读取配置文件的流程改成 从配置中心中加载,这样就可以实现修改配置中心的内容,业务服务器自动同步哦---- 说白了这里本地的 appsetting.json 已经没用了。

PS. 我们其实完全可以使用DB或者NoSql自己来实现简单的配置中心哦.

2.用配置中心的背景

 安全性:配置跟随源代码保存在代码库中,容易造成配置泄漏。

 时效性:修改配置,需要重启服务才能生效。

 局限性:无法支持动态调整:例如日志开关、功能开关。

因此,分布式配置中心应运而生!

3.常见配置中心

 (1).Consul

  go开发

  依赖:不依赖其他组件  

  应用内/外:属于外部应用,侵入性小

  ACP原则:遵循CP原则(一致性+分离容忍) 服务注册稍慢,由于其一致性导致了在Leader挂掉时重新选举期间真个consul不可用。

  版本迭代:目前仍然进行版本迭代

  集成支持:支持SpringCloud K8S集成

  访问协议:HTTP/DNS

  雪崩保护:不支持雪崩保护

  集成:SpringCloud集成,K8S集成

  自动注销实例:不支持

  界面:英文界面,不符合国人习惯

  上手:复杂一点

(2).Apollo

  java开发 ----- 运维成本比高

  Apollo分为MySQL,Config Service,Admin Service,Portal四个模块,MySQL存储Apollo元数据和用户配置数据; Config Service提供配置的读取、推送等功能,客户端请求都是落到Config Service上;Admin Service提供配置的修改、发布等功能,Portal操作的服务就是Admin Service; Portal提供给用户配置管理界面;功能强大,社区活跃,但较为复杂,部署组件较多,运维成本比高

(3).Nacos

  依赖:mysql

  应用内/外:属于外部应用,侵入性小

  ACP原则:通知遵循CP原则(一致性+分离容忍) 和AP原则(可用性+分离容忍)

  版本迭代:目前仍然进行版本迭代,最近的提交是几天前

  集成支持:支持Dubbo 、SpringCloud、K8S集成

  访问协议:HTTP/动态DNS/UDP

  雪崩保护:支持雪崩保护

(4).Spring cloud config

  java开发 ----- Net支持比较差

  自动注销实例:支持

  界面:国产服务,中文界面,符合国人习惯

  上手:极易,中文文档,案例,社区活跃

  总结:Consul实际上是和Nacos比较相似的产品,虽然Consul目前的主要发展方向放在了Service Mesh,但是Consul最初支持的服务发现和配置管理,也是Nacos的两大功能。虽然Nacos在Consul之后以与之相似的部署架构开源,但这并不意味着Nacos在功能和架构上也模仿Consul,Nacos的架构和功能是由阿里巴巴内部十年的运行演进经验得来,所以二者的比较也一定会让大家更加了解他们的定位和演进方向是完全不一样的。

 

二. Core中的配置

   关于Core中的配置相关详见:第十节:Asp.Net Core 配置详解和选项模式

1.说明

  在core mvc中也有一套配置系统,通过修改属性reloadOnChange: true,即配置文件修改后,会被自动加载哦。

2.测试

 (1).首先将配置文件都改成 “始终复制”或者“较新则复制”

 (2).test1.json 和 test2.json,要通过代码加载进来,config.AddJsonFile("Config/test1.json", optional: true, reloadOnChange: true); 其中reloadOnChange: true,表示配置文件修改后,程序自动加载。

代码如下:

  public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                //配置相关(这里是用来测试Core本身配置系统的,与Consul配置中心没有任何关系哦)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                   //1. 设置当前目录为基础目录(后面则可以用相对路径)
                    config.SetBasePath(Directory.GetCurrentDirectory());
                    //2. 加载json文件 (配置后面两个参数为true,当配置文件发生变化的时候,会自动更新加载,而不必重启整个项目)
                   config.AddJsonFile("Config/test1.json", optional: true, reloadOnChange: true);
                    config.AddJsonFile("Config/test2.json", optional: true, reloadOnChange: true);
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

 (3).注释掉上面从consul加载配置的代码

 (4).通过指令启动项目 【dotnet ConfigCenterTest.dll --urls="http://*:7060" --ip="127.0.0.1" --port=7060】

 (5).用PostMan测试,发送Get请求:http://127.0.0.1:7060/api/Home/GetMsg2 获取原配置中的内容;

     修改test1.json中的内容(一定要去运行的bin文件中修改,不是在代码中修改哦),重新用postman获取,获取的是修改后的内容,这期间项目并没有重启,至此,大功告成。

原运行结果:

修改后的运行结果:

 

三. Consul配置中心的应用

补充:

  既然前面的Core已经可以实现动态加载,为什么还要有配置中心呢?

  Reason:微服务会有很多个,分别部署在不同服务器上,去每个服务器修改配置还是很麻烦,这里Consul提供了一个统一的入口,维护更方便。

1.准备条件

 (1).新建WebApi项目ConfigCenterTest,配置支持命令行启动.

    【dotnet ConfigCenterTest.dll --urls="http://*:7060" --ip="127.0.0.1" --port=7060】

 (2).通过Nuget安装【Winton.Extensions.Configuration.Consul 3.1.0】

 PS. 这里用ConfigCenterTest项目来单纯的演示配置中心功能,在整个微服务体系中需要再各个业务服务器中加上“读取配置中心”的代码。

 (3).通过指令【consul.exe agent -dev】启动Consul

 PS. 这里用的Consul是以开发模式来演示,不能持久化数据,真实环境中要用生产模式,来持久化数据。

2.三种配置模式

(1).单服务单配置

 a.访问 http://127.0.0.1:8500/ 客户端, 然后在"Key/Value"选项卡中,以appsettings.json为键,将其内容copy到里面作为值进行保存。

 b.在Program中ConfigureAppConfiguration,通过 config.AddConsul("appsettings.json",xxx),表示从Consul中读取配置文件。

            public static IHostBuilder CreateHostBuilder(string[] args) =>
                 Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    //1.添加命令行支持(写在上面的Main里也可以)
                    var config = new ConfigurationBuilder().AddCommandLine(args).Build();

                    //2.从Consul中读取配置文件的3种模式
                    //2.1 单服务单配置模式                   webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        // 加载默认配置信息到Configuration
                        hostingContext.Configuration = config.Build();
                        config.AddConsul("appsettings.json", options =>
                        {
                            options.ConsulConfigurationOptions = cco => { cco.Address = new Uri("http://127.0.0.1:8500"); }; // 1、consul地址
                            options.Optional = true; // 2、配置选项
                            options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                            options.OnLoadException = exceptionContext => { exceptionContext.Ignore = true; }; // 4、忽略异常
                        });
                        hostingContext.Configuration = config.Build(); // 5、consul中加载的配置信息加载到Configuration对象,然后通过Configuration 对象加载项目中
                    });
                    webBuilder.UseStartup<Startup>();
                });

 c.用PostMan测试,发送Get请求:http://127.0.0.1:7060/api/Home/GetMsg 获取原配置中的内容; 此时,在客户端中改一下,重新用postman获取,获取的是修改后的内容,至此,大功告成。

[HttpGet]
public string GetMsg()
{
   var DbStr = Configuration["DbStr"];
   var userName = Configuration["userName"];
   var pwd = Configuration["pwd"];
   return $"DbStr={DbStr},userName={userName},pwd={pwd}";
}

原运行结果:

在Consul客户端中修改后的运行结果:

(2).多服务单配置

 背景:上述①中的配置模式,如果是多个服务,每个服务都会对应一个appsettings.json,首先在Consul客户端上同一目录下是没法创建的.

 解决方案:利用文件夹分层的方式,在Consul客户端上一个项目对应一个文件夹,然后在各自文件夹下放各自的appsettings.json, 如:ConfigCenterTest/appsettings.json、GoodsService/appsettings.json然后在Program加载配置文件的时候,动态获取项目名称,进行路径的组装即可.

 var evn = hostingContext.HostingEnvironment;

 config.AddConsul($"{evn.ApplicationName}/appsettings.json", options =>{})

 

 代码分享:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    //1.添加命令行支持(写在上面的Main里也可以)
                    var config = new ConfigurationBuilder().AddCommandLine(args).Build();
                    //2.从Consul中读取配置文件的3种模式
                    //2.2 多服务单配置模式
                    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        // 加载默认配置信息到Configuration
                        hostingContext.Configuration = config.Build();
                        //动态获取项目名称
                        var evn = hostingContext.HostingEnvironment;
                        config.AddConsul($"{evn.ApplicationName}/appsettings.json", options =>
                        {
                            options.ConsulConfigurationOptions = cco => { cco.Address = new Uri("http://127.0.0.1:8500"); }; // 1、consul地址
                            options.Optional = true; // 2、配置选项
                            options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                            options.OnLoadException = exceptionContext => { exceptionContext.Ignore = true; }; // 4、忽略异常
                        });
                        hostingContext.Configuration = config.Build(); // 5、consul中加载的配置信息加载到Configuration对象,然后通过Configuration 对象加载项目中
                    });
                    webBuilder.UseStartup<Startup>();
                });

(3).多服务多配置

 背景:如果有多个服务,每个服务还需要多个配置文件,上①②都解决不了.

 解决方案:首先要把所需的所有配置文件都手动copy到指定项目文件夹的consul中,然后在②的基础上多加几个config.AddConsul($"{evn.ApplicationName}/test1.json", options =>{})等等,即可。

 测试:

  用PostMan测试,发送Get请求:http://127.0.0.1:7060/api/Home/GetMsg2 获取原配置中的内容;修改test1.json中的内容,重新用postman获取,获取的是修改后的内容,至此,大功告成.

 注:这里不需要做配置文件的本地加载,因为已经改为从Consul中读取了,不需要config.AddJsonFile(xxx)!!!

 PS:该方案也适用于单服务多配置哦.

 

代码分享:

  public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    //1.添加命令行支持(写在上面的Main里也可以)
                    var config = new ConfigurationBuilder().AddCommandLine(args).Build();
                    //2.从Consul中读取配置文件的3种模式
                    //2.3 多服务多配置模式
                    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        // 加载默认配置信息到Configuration
                        hostingContext.Configuration = config.Build();
                        //动态获取项目名称
                        var evn = hostingContext.HostingEnvironment;
                        config.AddConsul($"{evn.ApplicationName}/appsettings.json", options =>
                        {
                            options.ConsulConfigurationOptions = cco => { cco.Address = new Uri("http://127.0.0.1:8500"); }; // 1、consul地址
                            options.Optional = true; // 2、配置选项
                            options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                            options.OnLoadException = exceptionContext => { exceptionContext.Ignore = true; }; // 4、忽略异常
                        });
                        config.AddConsul($"{evn.ApplicationName}/test1.json", options =>
                        {
                            options.ConsulConfigurationOptions = cco => { cco.Address = new Uri("http://127.0.0.1:8500"); }; // 1、consul地址
                            options.Optional = true; // 2、配置选项
                            options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                            options.OnLoadException = exceptionContext => { exceptionContext.Ignore = true; }; // 4、忽略异常
                        });
                        config.AddConsul($"{evn.ApplicationName}/test2.json", options =>
                        {
                            options.ConsulConfigurationOptions = cco => { cco.Address = new Uri("http://127.0.0.1:8500"); }; // 1、consul地址
                            options.Optional = true; // 2、配置选项
                            options.ReloadOnChange = true; // 3、配置文件更新后重新加载
                            options.OnLoadException = exceptionContext => { exceptionContext.Ignore = true; }; // 4、忽略异常
                        });
                        hostingContext.Configuration = config.Build(); // 5、consul中加载的配置信息加载到Configuration对象,然后通过Configuration 对象加载项目中
                    });

                    webBuilder.UseStartup<Startup>();
                });

3. 应用

 (1).动态切换数据库链接

  在配置中心中修改数据库连接字符串,代码中配合:

  xxxContext.Database.GetDbConnection().ConnectionString = Configuration["DbStr"];    即可实现动态切换数据库.

 (2).切换数据源,充当一个开关.

  比如:flag=true,从缓存中读数据,flag=false,从db中读数据

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

 

posted @ 2020-05-19 14:45  Yaopengfei  阅读(5363)  评论(2编辑  收藏  举报