说明:下面的实例是通过阅读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;
}
}
请求结果