設定是我們必不可少的功能,我們在開發中,經常會遇到需要獲取設定資訊的需求,那麼如何才能優雅的獲取設定資訊?
我們希望新的設定:
根據使用場景我們將設定分為本地設定以及遠端設定,下面我們就來看一下本地設定與遠端設定是如何來使用的?
Assignment.MasaConfiguration
,並安裝Masa.Contrib.Configuration
dotnet new web -o Assignment.MasaConfiguration
cd Assignment.MasaConfiguration
dotnet add package Masa.Contrib.Configuration --version 0.6.0-preview.7
AppConfig
、ConnectionStrings
,用於儲存資料庫設定/// <summary>
/// 應用設定類
/// </summary>
public class AppConfig : LocalMasaConfigurationOptions
{
public ConnectionStrings ConnectionStrings { get; set; }
}
public class ConnectionStrings
{
public string DefaultConnection { get; set; }
}
appsettings.json
{
"AppConfig": {
"ConnectionStrings": {
"DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity"
}
}
}
MasaConfiguration
,修改類Program
builder.AddMasaConfiguration();
Program
app.MapGet("/AppConfig", (IOptions<AppConfig> appConfig)
{
return appConfig.Value.ConnectionStrings.DefaultConnection);
});
如果希望監聽設定變更事件,則可使用IOptionsMonitor
的OnChange方法
目前我們遠端設定的能力僅實現了Dcc, 下面就讓我們看看如何來使用它
Assignment.MasaConfiguration
,並安裝Masa.Contrib.Configuration.ConfigurationApi.Dcc
dotnet add package Masa.Contrib.Configuration.ConfigurationApi.Dcc --version 0.6.0-preview.7
appsettings.json
{
//Dcc設定,擴充套件Configuration能力,支援遠端設定
"DccOptions": {
"ManageServiceAddress ": "http://localhost:8890",
"RedisOptions": {
"Servers": [
{
"Host": "localhost",
"Port": 8889
}
],
"DefaultDatabase": 0,
"Password": ""
}
}
}
RedisOptions
, 用於設定業務專案中使用的快取地址public class RedisOptions : ConfigurationApiMasaConfigurationOptions
{
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
public int DefaultDatabase { get; set; }
}
Program
var app = builder.AddMasaConfiguration(configurationBuilder =>
{
configurationBuilder.UseDcc();
}).Build();
// 推薦使用,通過IOptions<TOptions>獲取設定,支援強型別
app.MapGet("/AppConfig", (IOptions<RedisOptions> options)
{
return options.Value.Host;
});
到目前為止,我們已經學會了如何使用Masa提供的設定,但只有瞭解原理,我們才敢在專案中大膽的用起來,出現問題後才能快速的定位並解決問題,下面我們就來深入瞭解下
根據使用場景我們將設定劃分為:
在使用MasaConfiguration後,IConfiguration的檔案結構變更為:
IConfiguration
├── Local 本地節點(固定)
│ ├── Platforms 自定義設定
│ ├── ├── Name 引數
├── ConfigurationAPI 遠端節點(固定)
│ ├── AppId 替換為你的AppId
│ ├── AppId ├── Platforms 自定義節點
│ ├── AppId ├── Platforms ├── Name 引數
除了一下設定源以及設定的提供者提供的設定除外,其餘的設定會遷移到Local
節點下
MasaConfiguration中提供了全域性設定的功能,並預設支援AppId
、Environment
、Cluster
獲取引數值的優先順序為:
自定義全域性設定 > 從IConfiguration中獲取(支援命令、環境變數、組態檔) > 約定設定
service.Configure<MasaAppConfigureOptions>(options =>
{
options.AppId = "Replace-With-Your-AppId";
options.Environment = "Replace-With-Your-Environment";
options.Cluster = "Replace-With-Your-Cluster";
options.TryAdd("Replace-With-Your-ConfigKey", "Replace-With-Your-ConfigValue");// 自定義全域性設定鍵、值
})
當未指定設定的值時,將會從設定中獲取得到設定的值,預設設定與Key的關係為:
AppId
: AppId
Environment
: ASPNETCORE_ENVIRONMENT
Cluster
: Cluster
當命令列與環境變數獲取引數失敗後,則會嘗試從組態檔根據設定的Key獲取對應的值
當未自定義設定,且無法從IConfiguration中獲取到相對應引數的設定後,我們將根據約定好的規則生成對應的值
AppId
: 啟動程式名.Replace(".", "-")Environment
: Production
Cluster
: Default
在快速入門的例子中,看似很簡單就可以通過IOptions<TOptions>
獲取到AppConfig
的設定資訊以及Dcc
中設定的Redis
資訊,這一切是如何做到的呢?
在MasaConfiguration中提供了兩種對映方式,用來對映設定與類的對應關係,分別是:自動對映、手動對映。
分為本地設定以及遠端設定的自動對映
Masa.Contrib.Configuration
提供Masa.Contrib.Configuration.ConfigurationApi.Dcc
提供1.1 當設定儲存在本地時,則將對應的設定類繼承LocalMasaConfigurationOptions
// <summary>
/// 應用設定類
/// </summary>
public class AppConfig : LocalMasaConfigurationOptions
{
// /// <summary>
// /// 如果當前設定掛載在根節點(一級節點)時,則無需過載,如果掛載在二級節點時,則需要過載ParentSection並賦值為一級節點名
// /// 根節點名:預設為一級節點,可不寫,格式:一級節點:二級節點:三級節點……
// /// </summary>
// [JsonIgnore]
// public override string? ParentSection => null;
// /// <summary>
// /// 如果類名與節點名保持一致,則可忽略不寫,否則重寫`Section`並賦值為節點名
// /// </summary>
// [JsonIgnore]
// public override string? Section => "RabbitMq";
public ConnectionStrings ConnectionStrings { get; set; }
}
public class ConnectionStrings
{
public string DefaultConnection { get; set; }
}
當設定中的引數直接平鋪掛載根節點下,而不是掛載到跟節點下的某個指定節點時,
ParentSection
無需過載,Section
需要過載並賦值為空字串
1.2 當設定儲存在Dcc,則將對應的設定類繼承ConfigurationApiMasaConfigurationOptions
public class RedisOptions : ConfigurationApiMasaConfigurationOptions
{
/// <summary>
/// 設定所屬的AppId,當AppId與預設AppId一致時,可忽略
/// </summary>
// public virtual string AppId { get; }
/// <summary>
/// Dcc的設定物件名稱,當設定物件名稱與類名一致時,可忽略
/// </summary>
// public virtual string? ObjectName { get; }
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
public int DefaultDatabase { get; set; }
}
雖然自動對映的方式很簡單,也很方便,但總是有一些場景使得我們無法通過自動對映來做,那如何手動指定對映關係呢?
為了方便大家理解,手動對映仍然使用AppConfig
以及Redis
來舉例
builder.AddMasaConfiguration(configurationBuilder =>
{
configurationBuilder.UseDcc();//使用Dcc 擴充套件Configuration能力,支援遠端設定
configurationBuilder.UseMasaOptions(options =>
{
options.MappingLocal<AppConfig>("AppConfig");//其中引數"AppConfig"可不寫(當類與節點名稱一致時可忽略)
options.MappingConfigurationApi<RedisOptions>("{替換為Dcc中設定所屬的AppId}", "{設定物件名稱}");//其中設定物件名稱可不寫(當設定物件名與類名一致時可忽略)
});
});
完整的Dcc設定如下:
{
"DccOptions": {
"ManageServiceAddress ": "http://localhost:8890",
"RedisOptions": {
"Servers": [
{
"Host": "localhost",
"Port": 8889
}
],
"DefaultDatabase": 0,
"Password": ""
},
"AppId": "Replace-With-Your-AppId",
"Environment": "Development",
"ConfigObjects": [ "Platforms" ],
"Secret": "",
"Cluster": "Default",
"ExpandSections" : [
{
"AppId": "Replace-With-Your-AppId",
"Environment": "Development",
"ConfigObjects": [ "Platforms" ],
"Secret": "",
"Cluster": "Default",
}
],
"PublicId": "Replace-With-Your-Public-AppId",
"PublicSecret": "Replace-With-Your-Public-AppId-Secret"
}
}
上面提到了目前的遠端設定能力僅支援Dcc,那如果我希望接入自己開發的設定中心或者其它更優秀的設定中心需要接入如何做?
以Apollo
為例:
新建類庫Masa.Contrib.Configuration.ConfigurationApi.Apollo
新建ApolloConfigurationRepository
並實現類AbstractConfigurationRepository
internal class ApolloConfigurationRepository : AbstractConfigurationRepository
{
private readonly IConfigurationApiClient _client;
public override SectionTypes SectionType => SectionTypes.ConfigurationAPI;
public DccConfigurationRepository(
IConfigurationApiClient client,
ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_client = client;
//todo: 藉助 IConfigurationApiClient 獲取需要掛載到遠端節點的設定資訊並監聽設定變化
// 當設定變更時觸發FireRepositoryChange(SectionType, Load());
}
public override Properties Load()
{
//todo: 返回當前掛載到遠端節點的設定資訊
}
}
ConfigurationApiClient
,為ConfigurationApi
提供獲取基礎設定的能力public class ConfigurationApiClient : IConfigurationApiClient
{
public Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string configObject, Action<string>? valueChanged = null)
{
throw new NotImplementedException();
}
public Task<(string Raw, ConfigurationTypes ConfigurationType)> GetRawAsync(string environment, string cluster, string appId, string configObject, Action<string>? valueChanged = null)
{
throw new NotImplementedException();
}
public Task<T> GetAsync<T>(string configObject, Action<T>? valueChanged = null);
{
throw new NotImplementedException();
}
public Task<T> GetAsync<T>(string environment, string cluster, string appId, string configObject, Action<T>? valueChanged = null);
{
throw new NotImplementedException();
}
public Task<dynamic> GetDynamicAsync(string environment, string cluster, string appId, string configObject, Action<dynamic> valueChanged)
{
throw new NotImplementedException();
}
public Task<dynamic> GetDynamicAsync(string key)
{
throw new NotImplementedException();
}
}
ConfigurationApiManage
,為ConfigurationApi
提供管理設定的能力public class ConfigurationApiManage : IConfigurationApiManage
{
// 通過管理端初始化AppId下的遠端設定
public Task InitializeAsync(string environment, string cluster, string appId, Dictionary<string, string> configObjects)
{
throw new NotImplementedException();
}
// 通過管理端更新指定設定的資訊
public Task UpdateAsync(string environment, string cluster, string appId, string configObject, object value)
{
throw new NotImplementedException();
}
}
ConfigurationApiMasaConfigurationOptions
類,並繼承MasaConfigurationOptions
我們希望其它自定義設定也能根據約定實現自動對映,我們也清楚不同的設定中心中儲存設定的名稱是不一樣的,例如在Apollo
中設定物件名稱叫做名稱空間,因此為了方便開發人員可以使用起來更方便,我們建議不同的設定中心可以有自己專屬的屬性,比如Apollo
的Namespace
,以此來降低開發人員的學習成本
public abstract class ConfigurationApiMasaConfigurationOptions : MasaConfigurationOptions
{
/// <summary>
/// The name of the parent section, if it is empty, it will be mounted under SectionType, otherwise it will be mounted to the specified section under SectionType
/// </summary>
[JsonIgnore]
public sealed override string? ParentSection => AppId;
//
public virtual string AppId => StaticConfig.AppId;
/// <summary>
/// The section null means same as the class name, else load from the specify section
/// </summary>
[JsonIgnore]
public sealed override string? Section => Namespace;
/// <summary>
///
/// </summary>
public virtual string? Namespace { get; }
/// <summary>
/// Configuration object name
/// </summary>
[JsonIgnore]
public sealed override SectionTypes SectionType => SectionTypes.ConfigurationApi;
}
Masa.Contrib.BasicAbility.Apollo
,並新建IMasaConfigurationBuilder的擴充套件方法UseApollo
public static class MasaConfigurationExtensions
{
public static IMasaConfigurationBuilder UseApollo(this IMasaConfigurationBuilder builder)
{
//todo:將IConfigurationApiClient、IConfigurationApiManage註冊到到服務集合中,並通過builder.AddRepository()新增ApolloConfigurationRepository
return builder;
}
}
如何使用MasaConfiguration?
builder.AddMasaConfiguration()
為何通過IOptions
IConfigurationApiClient
與IConfiguration
之間有什麼關係?
IConfigurationApiClient
、IConfigurationApiManage
分別是管理遠端Api的使用者端以及管理端,與IConfiguration
相比,IConfigurationApiClient
的資訊更全,每次獲取設定需要像設定中心請求獲取資料,而IConfiguration
是通過呼叫IConfigurationApiClient
將需要使用的設定物件獲取並新增到IConfiguration
中,後續使用者獲取設定時無需向設定中心請求資料遠端設定物件更新後,IConfiguration
中的資訊會更新嗎?為什麼?
IConfiguration
重新重新整理資料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,歡迎聯絡我們
本文來自部落格園,作者:磊_磊,轉載請註明原文連結:https://www.cnblogs.com/zhenlei520/p/16643261.html
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利