8. 更优雅的获取配置信息
前言
配置是我们必不可少的功能,我们在开发中,经常会遇到需要获取配置信息的需求,那么如何才能优雅的获取配置信息?
我们希望新的配置:
- 支持强类型
 - 配置变更后通知
 - 学习难度低
 
快速入门
根据使用场景我们将配置分为本地配置以及远程配置,下面我们就来看一下本地配置与远程配置是如何来使用的?
- 安装.Net 6.0
 
本地配置
- 新建ASP.NET Core 空项目
Assignment.MasaConfiguration,并安装Masa.Contrib.Configuration 
1  | dotnet new web -o Assignment.MasaConfiguration  | 
- 新建类
AppConfig、ConnectionStrings,用于存储数据库配置 
1  | /// <summary>  | 
- 修改文件
appsettings.json 
1  | {  | 
- 注册
MasaConfiguration,修改类Program 
1  | builder.AddMasaConfiguration();  | 
- 如何使用?修改类
Program 
1  | app.MapGet("/AppConfig", (IOptions<AppConfig> appConfig)  | 
如果希望监听配置变更事件,则可使用IOptionsMonitor
的OnChange方法 
远程配置
目前我们远程配置的能力仅实现了Dcc, 下面就让我们看看如何来使用它
- 选中
Assignment.MasaConfiguration,并安装Masa.Contrib.Configuration.ConfigurationApi.Dcc 
1  | dotnet add package Masa.Contrib.Configuration.ConfigurationApi.Dcc --version 0.6.0-preview.7  | 
- 修改
appsettings.json 
1  | {  | 
- 新建类
RedisOptions, 用于配置业务项目中使用的缓存地址 
1  | public class RedisOptions : ConfigurationApiMasaConfigurationOptions  | 
- 修改类
Program 
1  | var app = builder.AddMasaConfiguration(configurationBuilder =>  | 
- 如何使用?
 
1  | // 推荐使用,通过IOptions<TOptions>获取配置,支持强类型  | 
进阶
到目前为止,我们已经学会了如何使用Masa提供的配置,但只有了解原理,我们才敢在项目中大胆的用起来,出现问题后才能快速的定位并解决问题,下面我们就来深入了解下
分类
根据使用场景我们将配置划分为:
- 本地配置(配置存储在本地配置文件中,后期配置变更不变)
 - 远程配置(配置在远程配置中心、例如Dcc、Apollo、其它配置中心)
 
IConfiguration结构
在使用MasaConfiguration后,IConfiguration的文件结构变更为:
1  | IConfiguration  | 
除了以下配置源以及配置的提供者提供的配置除外,其余的配置会迁移到Local节点下
- 配置源
 - 配置提供者
 
全局配置
MasaConfiguration中提供了全局配置的功能,并默认支持AppId、Environment、Cluster
- 优先级
 
获取参数值的优先级为:
1  | 自定义全局配置 > 从IConfiguration中获取(支持命令、环境变量、配置文件) > 约定配置  | 
- 自定义全局配置
 
1  | service.Configure<MasaAppConfigureOptions>(options =>  | 
- IConfiguration中获取
 
当未指定配置的值时,将会从配置中获取得到配置的值,默认配置与Key的关系为:
AppId:AppIdEnvironment:ASPNETCORE_ENVIRONMENTCluster:Cluster
当命令行与环境变量获取参数失败后,则会尝试从配置文件根据配置的Key获取对应的值
- 约定默认值
 
当未自定义配置,且无法从IConfiguration中获取到相对应参数的配置后,我们将根据约定好的规则生成对应的值
AppId: 启动程序名.Replace(“.”, “-“)Environment:ProductionCluster:Default
配置映射
在快速入门的例子中,看似很简单就可以通过IOptions<TOptions>获取到AppConfig的配置信息以及Dcc中配置的Redis信息,这一切是如何做到的呢?
在MasaConfiguration中提供了两种映射方式,用来映射配置与类的对应关系,分别是:自动映射、手动映射。
- 自动映射
 
分为本地配置以及远程配置的自动映射
- 本地配置: 由
Masa.Contrib.Configuration提供 - 远程配置
- Dcc: 由
Masa.Contrib.Configuration.ConfigurationApi.Dcc提供 
 - Dcc: 由
 
1.1 当配置存储在本地时,则将对应的配置类继承LocalMasaConfigurationOptions
1  | // <summary>  | 
当配置中的参数直接平铺挂载根节点下,而不是挂载到跟节点下的某个指定节点时,
ParentSection无需重载,Section需要重载并赋值为空字符串
1.2 当配置存储在Dcc,则将对应的配置类继承ConfigurationApiMasaConfigurationOptions
1  | public class RedisOptions : ConfigurationApiMasaConfigurationOptions  | 
- 手动映射
 
虽然自动映射的方式很简单,也很方便,但总是有一些场景使得我们无法通过自动映射来做,那如何手动指定映射关系呢?
为了方便大家理解,手动映射仍然使用AppConfig以及Redis来举例
1  | builder.AddMasaConfiguration(configurationBuilder =>  | 
Dcc配置
完整的Dcc配置如下:
1  | {  | 
- ManageServiceAddress: 用于更新远程配置使用,非必填
 - RedisOptions:Dcc会在Redis中存储配置的副本,此处是存储Dcc配置的的Redis地址(*)
 - AppId:项目中需要获取配置的AppId,也被称为Dcc的默认AppId,当未赋值时从全局配置中获取
 - Environment:项目中需要获取配置的环境信息,当未赋值时从全局配置中获取
 - ConfigObjects:项目中需要使用的配置对象名称,未赋值时默认获取当前环境、当前集群、当前AppId下的全部配置对象
 - Secret:秘钥,用于更新远程配置,每个AppId有一个秘钥,非必填(不可使用更新远程配置的能力)
 - Cluster:需要加载配置的集群,后面我们简称为Dcc的默认集群,未赋值时从全局配置中获取
 - PublicId:Dcc中公共配置的AppId,默认:public-$Config,非必填
 - PublicSecret:Dcc中公共配置的AppId的秘钥,非必填
 - ExpandSections:扩展配置的集合,适用于当前应用需要获取多个AppId下的配置时使用,其中AppId为必填项、Environment、Cluster为非必填项,当不存在时将与Dcc默认环境、集群一致,非必填
 
扩展其它的配置中心
上面提到了目前的远程配置能力仅支持Dcc,那如果我希望接入自己开发的配置中心或者其它更优秀的配置中心需要接入如何做?
以Apollo为例:
新建类库
Masa.Contrib.Configuration.ConfigurationApi.Apollo新建
ApolloConfigurationRepository并实现类AbstractConfigurationRepository
1  | internal class ApolloConfigurationRepository : AbstractConfigurationRepository  | 
- 新建类
ConfigurationApiClient,为ConfigurationApi提供获取基础配置的能力 
1  | public class ConfigurationApiClient : IConfigurationApiClient  | 
- 新建类
ConfigurationApiManage,为ConfigurationApi提供管理配置的能力 
1  | public class ConfigurationApiManage : IConfigurationApiManage  | 
- 新建
ConfigurationApiMasaConfigurationOptions类,并继承MasaConfigurationOptions 
我们希望其它自定义配置也能根据约定实现自动映射,我们也清楚不同的配置中心中存储配置的名称是不一样的,例如在Apollo中配置对象名称叫做命名空间,因此为了方便开发人员可以使用起来更方便,我们建议不同的配置中心可以有自己专属的属性,比如Apollo的Namespace,以此来降低开发人员的学习成本
1  | public abstract class ConfigurationApiMasaConfigurationOptions : MasaConfigurationOptions  | 
- 选中类库
Masa.Contrib.BasicAbility.Apollo,并新建IMasaConfigurationBuilder的扩展方法UseApollo 
1  | public static class MasaConfigurationExtensions  | 
总结
如何使用MasaConfiguration?
- 新增:
builder.AddMasaConfiguration() 
- 新增:
 为何通过IOptions
获取到的配置为空,但通过IConfiguration或者IMasaConfiguration根据节点可以获取到? - 检查下是否没有绑定节点关系,如何绑定节点关系请查看问题2
 - 检查节点绑定是否错误
 
IConfigurationApiClient与IConfiguration之间有什么关系?IConfigurationApiClient、IConfigurationApiManage分别是管理远程Api的客户端以及管理端,与IConfiguration相比,IConfigurationApiClient的信息更全,每次获取配置需要像配置中心请求获取数据,而IConfiguration是通过调用IConfigurationApiClient将需要使用的配置对象获取并添加到IConfiguration中,后续用户获取配置时无需向配置中心请求数据
远程配置对象更新后,
IConfiguration中的信息会更新吗?为什么?- 会更新、远程配置更新后会通过valueChanged通知远程配置的提供者,然后远程配置的提供者会刷新本地的远程配置并通知
IConfiguration重新刷新数据 
- 会更新、远程配置更新后会通过valueChanged通知远程配置的提供者,然后远程配置的提供者会刷新本地的远程配置并通知
 
Dcc: Distributed Configuration Center 是一个以DDD为指导思想、使用.Net6.0开发的分布式配置中心
本章源码
Assignment08
https://github.com/zhenlei520/MasaFramework.Practice
开源地址
MASA.Framework:https://github.com/masastack/MASA.Framework
MASA.EShop:https://github.com/masalabs/MASA.EShop
MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor
如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们




