.NET Core 3.0之創(chuàng)建基于Consul的Configuration擴(kuò)展組件
經(jīng)過(guò)前面三篇關(guān)于.NET Core Configuration的文章之后,本篇文章主要討論如何擴(kuò)展一個(gè)Configuration組件出來(lái)。
了解了Configuration的源碼后,再去擴(kuò)展一個(gè)組件就會(huì)比較簡(jiǎn)單,接下來(lái)我們將在.NET Core 3.0-preview5的基礎(chǔ)上創(chuàng)建一個(gè)基于Consul的配置組件。
相信大家對(duì)Consul已經(jīng)比較了解了,很多項(xiàng)目都會(huì)使用Consul作為配置中心,此處也不做其他闡述了,主要是講一下,創(chuàng)建Consul配置擴(kuò)展的一些思路。使用Consul配置功能時(shí),我們可以將信息轉(zhuǎn)成JSON格式后再存儲(chǔ),那么我們?cè)谧x取的時(shí)候,在體驗(yàn)上就像是從讀取JSON文件中讀取一樣。
開(kāi)發(fā)前的準(zhǔn)備初始化Consul
假設(shè)你已經(jīng)安裝并啟動(dòng)了Consul,我們打開(kāi)Key/Value功能界面,創(chuàng)建兩組配置選項(xiàng)出來(lái),分別是commonservice和userservice,如下圖所示

配置值采用JSON格式

實(shí)現(xiàn)思路
我們知道在Configuration整個(gè)的設(shè)計(jì)框架里,比較重要的類ConfigurationRoot,內(nèi)部又有一個(gè)IConfigurationProvider集合屬性,也就是說(shuō)我們追加IConfigurationProvider實(shí)例最終也會(huì)被放到到該集合中,如下圖所示

該項(xiàng)目中,我使用到了一個(gè)已經(jīng)封裝好的Consul(V0.7.2.6)類庫(kù),同時(shí)基于.NET Core關(guān)于Configuration的設(shè)計(jì)風(fēng)格,做如下的框架設(shè)計(jì)

考慮到我會(huì)在該組件內(nèi)部創(chuàng)建ConsulClient實(shí)例,所以對(duì)ConsulClient構(gòu)造函數(shù)的一部分參數(shù)做了抽象提取,并添加到了IConsulConfigurationSource中,以增強(qiáng)該組件的靈活性。
之前說(shuō)過(guò),Consul中的配置信息是以JSON格式存儲(chǔ)的,所以此處使用到了Microsoft.Extensions.Configuration.Json.JsonConfigurationFileParser,用以將JSON格式的信息轉(zhuǎn)換為Configuration的通用格式Key/Value。
核心代碼 IConsulConfigurationSource
/// <summary>
/// ConsulConfigurationSource
/// </summary>
public interface IConsulConfigurationSource : IConfigurationSource
{
/// <summary>
/// CancellationToken
/// </summary>
CancellationToken CancellationToken { get; }
/// <summary>
/// Consul構(gòu)造函數(shù)實(shí)例,可自定義傳入
/// </summary>
Action<ConsulClientConfiguration> ConsulClientConfiguration { get; set; }
/// <summary>
/// Consul構(gòu)造函數(shù)實(shí)例,可自定義傳入
/// </summary>
Action<HttpClient> ConsulHttpClient { get; set; }
/// <summary>
/// Consul構(gòu)造函數(shù)實(shí)例,可自定義傳入
/// </summary>
Action<HttpClientHandler> ConsulHttpClientHandler { get; set; }
/// <summary>
/// 服務(wù)名稱
/// </summary>
string ServiceKey { get; }
/// <summary>
/// 可選項(xiàng)
/// </summary>
bool Optional { get; set; }
/// <summary>
/// Consul查詢選項(xiàng)
/// </summary>
QueryOptions QueryOptions { get; set; }
/// <summary>
/// 重新加載延遲時(shí)間,單位是毫秒
/// </summary>
int ReloadDelay { get; set; }
/// <summary>
/// 是否在配置改變的時(shí)候重新加載
/// </summary>
bool ReloadOnChange { get; set; }
}
ConsulConfigurationSource
該類提供了一個(gè)構(gòu)造函數(shù),用于接收ServiceKey和CancellationToken實(shí)例
public ConsulConfigurationSource(string serviceKey, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(serviceKey))
{
throw new ArgumentNullException(nameof(serviceKey));
}
this.ServiceKey = serviceKey;
this.CancellationToken = cancellationToken;
}
其build()方法也比較簡(jiǎn)單,主要是初始化ConsulConfigurationParser實(shí)例
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
ConsulConfigurationParser consulParser = new ConsulConfigurationParser(this);
return new ConsulConfigurationProvider(this, consulParser);
}
ConsulConfigurationParser
該類比較復(fù)雜,主要實(shí)現(xiàn)Consul配置的獲取、監(jiān)控以及容錯(cuò)處理,公共方法源碼如下
/// <summary>
/// 獲取并轉(zhuǎn)換Consul配置信息
/// </summary>
/// <param name="reloading"></param>
/// <param name="source"></param>
/// <returns></returns>
public async Task<IDictionary<string, string>> GetConfig(bool reloading, IConsulConfigurationSource source)
{
try
{
QueryResult<KVPair> kvPair = await this.GetKvPairs(source.ServiceKey, source.QueryOptions, source.CancellationToken).ConfigureAwait(false);
if ((kvPair?.Response == null) && !source.Optional)
{
if (!reloading)
{
throw new FormatException(Resources.Error_InvalidService(source.ServiceKey));
}
return new Dictionary<string, string>();
}
if (kvPair?.Response == null)
{
throw new FormatException(Resources.Error_ValueNotExist(source.ServiceKey));
}
this.UpdateLastIndex(kvPair);
return JsonConfigurationFileParser.Parse(source.ServiceKey, new MemoryStream(kvPair.Response.Value));
}
catch (Exception exception)
{
throw exception;
}
}
/// <summary>
/// Consul配置信息監(jiān)控
/// </summary>
/// <param name="key"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public IChangeToken Watch(string key, CancellationToken cancellationToken)
{
Task.Run(() => this.RefreshForChanges(key, cancellationToken), cancellationToken);
return this.reloadToken;
}
另外,關(guān)于Consul的監(jiān)控主要利用了QueryResult.LastIndex屬性,該類緩存了該屬性的值,并與實(shí)獲取的值進(jìn)行比較,以判斷是否需要重新加載內(nèi)存中的緩存配置
ConsulConfigurationProvider
該類除了實(shí)現(xiàn)Load方法外,還會(huì)根據(jù)ReloadOnChange屬性,在構(gòu)造函數(shù)中注冊(cè)O(shè)nChange事件,用于重新加載配置信息,源碼如下:
public sealed class ConsulConfigurationProvider : ConfigurationProvider
{
private readonly ConsulConfigurationParser configurationParser;
private readonly IConsulConfigurationSource source;
public ConsulConfigurationProvider(IConsulConfigurationSource source, ConsulConfigurationParser configurationParser)
{
this.configurationParser = configurationParser;
this.source = source;
if (source.ReloadOnChange)
{
ChangeToken.OnChange(
() => this.configurationParser.Watch(this.source.ServiceKey, this.source.CancellationToken),
async () =>
{
await this.configurationParser.GetConfig(true, source).ConfigureAwait(false);
Thread.Sleep(source.ReloadDelay);
this.OnReload();
});
}
}
public override void Load()
{
try
{
this.Data = this.configurationParser.GetConfig(false, this.source).ConfigureAwait(false).GetAwaiter().GetResult();
}
catch (AggregateException aggregateException)
{
throw aggregateException.InnerException;
}
}
}
調(diào)用及運(yùn)行結(jié)果
此處調(diào)用在Program中實(shí)現(xiàn)
public class Program
{
public static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
WebHost.CreateDefaultBuilder(args).ConfigureAppConfiguration(
(hostingContext, builder) =>
{
builder.AddConsul("userservice", cancellationTokenSource.Token, source =>
{
source.ConsulClientConfiguration = cco => cco.Address = new Uri("http://localhost:8500");
source.Optional = true;
source.ReloadOnChange = true;
source.ReloadDelay = 300;
source.QueryOptions = new QueryOptions
{
WaitIndex = 0
};
});
builder.AddConsul("commonservice", cancellationTokenSource.Token, source =>
{
source.ConsulClientConfiguration = cco => cco.Address = new Uri("http://localhost:8500");
source.Optional = true;
source.ReloadOnChange = true;
source.ReloadDelay = 300;
source.QueryOptions = new QueryOptions
{
WaitIndex = 0
};
});
}).UseStartup<Startup>().Build().Run();
}
}
以上就是本次介紹的全部知識(shí)點(diǎn)內(nèi)容,感謝大家對(duì)腳本之家的支持。
- ASP.NET?Core中的Configuration配置二
- ASP.NET?Core中的Configuration配置一
- 淺析.netcore中的Configuration具體使用
- ASP.NET Core Web API 教程Project Configuration
- .Net Core配置Configuration具體實(shí)現(xiàn)
- 如何在ASP.NET Core 的任意類中注入Configuration
- .Net Core3.0 配置Configuration的實(shí)現(xiàn)
- Net Core全局配置讀取管理方法ConfigurationManager
- 詳解ASP.NET Core實(shí)現(xiàn)強(qiáng)類型Configuration讀取配置數(shù)據(jù)
相關(guān)文章
無(wú)法將類型為“System.Web.UI.WebControls.HiddenField”的對(duì)象強(qiáng)制轉(zhuǎn)換為類型的錯(cuò)誤的解
無(wú)法將類型為“System.Web.UI.WebControls.HiddenField”的對(duì)象強(qiáng)制轉(zhuǎn)換為類型的錯(cuò)誤的解決方法2011-12-12
asp.net ASPxTextBox等控件實(shí)現(xiàn)"回車模擬Tab"的 常用代碼整理
今天我要實(shí)現(xiàn)一些編輯框如ASPxTextBox、ASPxComboBox等控件回車模擬Tab的功能。這沒(méi)辦法,用戶用慣了回車,討厭按Tab來(lái)移動(dòng)焦點(diǎn)(鼠標(biāo)點(diǎn)擊更麻煩)。2010-03-03
asp.net窗體的打開(kāi)和關(guān)閉(輸出js)
asp.net窗體的打開(kāi)和關(guān)閉(輸出js),需要的朋友可以參考下。2011-06-06
Asp.net 動(dòng)態(tài)加載用戶自定義控件,并轉(zhuǎn)換成HTML代碼
Ajax現(xiàn)在已經(jīng)是相當(dāng)流行的技術(shù)了,Ajax不僅是想服務(wù)器端發(fā)送消息,更重要的是無(wú)刷新的重載頁(yè)面。2010-03-03

