說明:下面的實例是通過閱讀Nacos的SDK源碼,提取出來的關鍵實現。
Asp.NET core添加一個自定義配置,只要通過IConfigurationBuilder的Add方法,傳遞一個實現了IConfigurationSource接口實例即可。
先定義一個NiuBConfigSource,實現接口IConfigurationSource
public class NiuBConfigSource : NiuBOption, IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
throw new NotImplementedException();
}
}
這時候,Build方法需要返回一個IConfigurationProvider的實例,那么再來定義一個IConfigurationProvider的實現類,直接繼承.Net框架定義好的類ConfigurationProvider,該類實現了IConfigurationProvider接口。覆寫一下Load方法,為后續的配置更新刷新內存配置做準備,重載一個Load方法,傳一個鍵值對進來,調用原來的Load方法,實現對內存的刷新。
public class NiuBConfigProvider : ConfigurationProvider
{
private Dictionary<string, string> innerCnfiguration = new Dictionary<string, string>();
public override void Load()
{
foreach (string key in innerCnfiguration.Keys)
{
base.Data[key] = innerCnfiguration[key];
}
base.Load();
}
internal void Load(Dictionary<string, string> configuration)
{
this.innerCnfiguration = configuration;
this.Load();
}
}
這時候NiuBConfigSource就可以返回一個IConfigurationProvider實例了
public class NiuBConfigSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new NiuBConfigProvider(service);
}
}
為了方便使用,定義一個WebApplicationBuilder的擴展方法UseNiuBConfigure
public static class NiuBConfigProviderExtension
{
public static IConfigurationBuilder UseNiuBConfigure(this WebApplicationBuilder builder)
{
NiuBConfigSource source = new NiuBConfigSource();
IConfigurationBuilder configBuilder = builder.Configuration;
return configBuilder.Add(source);
}
}
那么來看一下怎么使用?
var builder = WebApplication.CreateBuilder(args);
builder.UseNiuBConfigure();
嗯,看起來不錯了,那么問題來了,如果配置中心更新了配置,怎么刷新內存配置數據呢?
沒錯NiuBConfigProvider有個Load的方法。
我們先改造一下UseNiuBConfigure方法,把配置中心的請求服務地址信息傳遞進去
public static IConfigurationBuilder UseNiuBConfigure(this WebApplicationBuilder builder, Action<NiuBOption> configure)
{
NiuBOption option= new NiuBOption();
configure(option);
NiuBConfigSource source = new NiuBConfigSource();
IConfigurationBuilder configBuilder = builder.Configuration;
return configBuilder.Add(source);
}
NiuBOption的定義
public class NiuBOption
{
public string ConfigSectionName { get; set; }
public string Host { get; set; }
public string Port { get; set; }
}
定義個監聽器
public class Listener
{
NiuBConfigProvider provider;
public Listener(NiuBConfigProvider provider)
{
this.provider = provider;
}
public void ConfigurationChanged(Dictionary<string, string> configuration)
{
provider.Load(configuration);
}
}
定義一個監聽服務
public class NiuBService : INiuBService
{
// 啟動遠程監聽,比如
public NiuBService()
{
Start();
}
// 配置中心的代碼沒有實現,通過該方法進行測試
private void grpcAccept(string host, string port, string configSectionName)
{
new ConfigureCenter().ConfigureChanged += configuration=> Configure(configuration);
}
// 該方法在UseNiuBConfigure方法中服務注入的時候會調用
// 根據配置信息,啟動遠程監聽,比如用GRPC的雙工通信,socket通信
public void Start()
{
Console.WriteLine("begin listen to xxx監聽配置中心");
// 如果有新的更新,那么調用Configure方法
grpcAccept(this.Option?.Host, this.Option?.Port, this.Option?.ConfigSectionName);
}
public void Restart(Dictionary<string, string> options)
{
this.Option.Host = options["Host"];
this.Option.Host = options["Port"];
this.Start();
}
public NiuBOption Option { get; set; }
public List<Listener> Listeners { get; set; } = new List<Listener>();
public void Configure(Dictionary<string, string> configuration)
{
foreach (var listener in Listeners)
{
listener.ConfigurationChanged(configuration);
}
}
}
現在UseNiuBConfigure方法的實現變成了這樣
public static IConfigurationBuilder UseNiuBConfigure(this WebApplicationBuilder builder, Action<NiuBOption> configure)
{
NiuBOption option= new NiuBOption();
configure(option);
var service= new NiuBService();
NiuBConfigSource source = new NiuBConfigSource(service);
service.Option= option;
builder.Services.addNiuBConfig(service);
IConfigurationBuilder configBuilder = builder.Configuration;
return configBuilder.Add(source);
}
NiuBConfigProvider的實現修改為
public class NiuBConfigProvider : ConfigurationProvider
{
private INiuBService service { get; set; }
public NiuBConfigProvider(INiuBService service) {
this.service = service;
service.Listeners.Add(new Listener(this));
}
private Dictionary<string, string> innerCnfiguration = new Dictionary<string, string>();
public override void Load()
{
foreach (string key in innerCnfiguration.Keys)
{
base.Data[key] = innerCnfiguration[key];
if (key == "ConfigCenterOption") {//熱重啟配置監聽服務
service.Restart(innerCnfiguration);
}
}
base.Load();
}
internal void Load(Dictionary<string, string> configuration)
{
this.innerCnfiguration = configuration;
this.Load();
}
}
NiuBConfigSource把Service傳遞進去
public class NiuBConfigSource : NiuBOption, IConfigurationSource
{
private INiuBService service;
public NiuBConfigSource(INiuBService service)
{
this.service = service;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new NiuBConfigProvider(service);
}
}
事情搞完了,下面來測試一下,由于注冊中心沒有實現,下面來模擬一下
// 配置中心的代碼沒有實現,通過該方法模擬監聽
private void grpcAccept(string host, string port, string configSectionName)
{
ConfigureCenter.ConfigureChanged += configuration=> Configure(configuration);
}
ConfigureCenter的實現
public class ConfigureCenter
{
public ConfigureCenter() { }
public event Action<Dictionary<string, string>> ConfigureChanged;
public void PublishConfig(Dictionary<string, string> config)
{
if (this.ConfigureChanged != null)
{
ConfigureChanged(config);
}
}
}
模擬配置中心發布了新的配置
var service= app.Services.GetService<INiuBService>();
service.ConfigureCenter.PublishConfig(new Dictionary<string, string>() {
{ "OrderService","{"Host":"http://niubi.com/api/order","descript":"訂單服務地址"}"}
});
通過API查看是否能獲取到配置中心發布的配置
[Route("api/[Controller]")]
public class OrderController : ControllerBase
{
public IConfiguration Configuration { get; set; }
public OrderController(IConfiguration configuration )
{
this.Configuration = configuration;
}
[HttpGet]
public string Get()
{
var value= Configuration["OrderService"];
return value == null ? "Empty": value;
}
}
請求結果