上一篇介绍了.net core的配置原理已经系统提供的一些常用的配置,但有时我们的配置是存放在Zookeeper,DB,Redis中的,这就需要我们自己去实现集成了。

  这里再介绍几个我们用的多的配置集成方案,可以加强我们对.net core配置机制的理解。

  

  Zookeeper

  .net core集成使用Zookeeper的做法在之前的博客中已经介绍过了,祥看:Zookeeper基础教程(六):.net core使用Zookeeper

  通用配置

  对于配置,数量一般不会很多,放在内存中一般时候是可以接受的,这样一方面方便使用,另一方面避免了频繁访问数据库或者Redis等第三方设置带来的性能消耗。

  其实,对于数据在数据库或者redis等其他存储设备上的集成,官方给我们提供了一个InMemory解决方案,也就是上一篇说到的从内存集合中获取配置的方案。在使用时,我们只需要在程序启动时从数据库或者Redis等位置将数据读取出来,然后已InMemory的方式集成进去就行了。但是这样做有个不足,就是当配置被修改时,我们需要重新启动应用服务才能生效,这样就是放弃了.net core配置重新加载机制,因此我们需要自己去实现这个集成。

  通过上述,我们可以认为数据库和Redis是同一类东西,但是又不能使用InMemory解决方案,因此,为满足以后的其它第三方配置需求,我们希望有一种通用的配置提供者,它需要满足两点:  

    1、提供配置所需的数据
2、能触发配置的重新加载更新

  为满足这两点,我们可以提供一个接口,如:  

    public interface IDataProvider
{
/// <summary>
/// 获取配置数据
/// </summary>
/// <returns></returns>
IDictionary<string, string> Process();
/// <summary>
/// 开启监听,决定什么时候触发重新加载配置
/// </summary>
/// <param name="trigger"></param>
void Watch(Action trigger);
}

  在上一篇我们已经讲到,集成配置需要我们自己实现两个接口:IConfigurationSource 和 IConfigurationProvider 。另外还提到,如果我们的配置来自其它文件,推荐分别继承 FileConfigurationSource 和 FileConfigurationProvider 两个抽象类,否则推荐自己实现 IConfigurationSource 接口,但是 IConfigurationProvider 接口的实现类继承 ConfigurationProvider 抽象类,显然数据库和Redis之类的都不是文件,于是我们可以有这样两个实现类:  

  

    public class CommonConfigurationSource : IConfigurationSource
{
public Type DataProviderType { get; set; }
/// <summary>
/// 是否监控源数据变化
/// </summary>
public bool ReloadOnChange { get; set; } = true;
/// <summary>
/// 加载延迟
/// </summary>
public int ReloadDelay { get; set; } = 250; public IConfigurationProvider Build(IConfigurationBuilder builder)
{
if (!typeof(IDataProvider).IsAssignableFrom(DataProviderType))
{
throw new ArgumentException("Data Provider Type must implement IDataProvider");
} return new CommonConfigurationProvider(this);
}
}

CommonConfigurationSource

  

    public class CommonConfigurationProvider : ConfigurationProvider, IDisposable
{
ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
CommonConfigurationSource commonConfigurationSource;
IDisposable _changeTokenRegistration;
IDataProvider dataProvider; public CommonConfigurationProvider(CommonConfigurationSource commonConfigurationSource)
{
this.commonConfigurationSource = commonConfigurationSource;
this.dataProvider = Activator.CreateInstance(commonConfigurationSource.DataProviderType) as IDataProvider; if (commonConfigurationSource.ReloadOnChange)
{
dataProvider.Watch(() =>
{
OnReload();
});
_changeTokenRegistration = ChangeToken.OnChange(
() => GetReloadToken(),
() =>
{
Thread.Sleep(commonConfigurationSource.ReloadDelay);
Load();
});
}
} /// <summary>
/// 加载配置
/// </summary>
public override void Load()
{
Data = dataProvider.Process();
}
/// <summary>
/// 释放
/// </summary>
public void Dispose()
{
_changeTokenRegistration?.Dispose();
}
}

CommonConfigurationProvider

  还没有完,同上一篇介绍的.net core自带的配置一样,我们还需要提供拓展方法去集成:  

  

    public static class CommonConfigurationExtensions
{
/// <summary>
/// 集成通用配置
/// </summary>
/// <param name="builder"></param>
/// <param name="dataProviderType"></param>
/// <param name="reloadOnChange"></param>
/// <param name="reloadDelay"></param>
/// <returns></returns>
public static IConfigurationBuilder AddCommonConfiguration(this IConfigurationBuilder builder, Type dataProviderType, bool reloadOnChange = false, int reloadDelay = 250)
{
return builder.Add<CommonConfigurationSource>(source =>
{
source.DataProviderType = dataProviderType;
source.ReloadDelay = reloadDelay;
source.ReloadOnChange = reloadOnChange;
});
}
/// <summary>
/// 集成通用配置
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="builder"></param>
/// <param name="reloadOnChange"></param>
/// <param name="reloadDelay"></param>
/// <returns></returns>
public static IConfigurationBuilder AddCommonConfiguration<T>(this IConfigurationBuilder builder, bool reloadOnChange = false, int reloadDelay = 250) where T : IDataProvider, new()
{
return builder.AddCommonConfiguration(typeof(T), reloadOnChange, reloadDelay);
}
}

CommonConfigurationExtensions

  接下来我们看看怎么使用。

  数据库(MySql)

  数据库我们以mysql为例,接下来我们只需要实现IDataProvider接口,用来实现配置数据的提供和配置的重新加载,比如我的数据中有数据:

  

  而接下来我们的IDataProvider接口实现类是:  

    /// <summary>
/// 要求存在空构造函数
/// </summary>
public class MysqlDataProvider : IDataProvider
{
private IDbConnection GetDbConnection()
{
string connectionString = @"Server=192.168.209.128;Port=3306;Database=test;Uid=root;Pwd=123456"; return new MySqlConnector.MySqlConnection(connectionString);
} /// <summary>
/// 获取配置数据
/// </summary>
/// <returns></returns>
public IDictionary<string, string> Process()
{
using (var con = GetDbConnection())
{
if (con.State != ConnectionState.Open)
{
con.Open();
} using (var cmd = con.CreateCommand())
{
cmd.CommandText = "SELECT `Key`,`Value` FROM Configurations";
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
IDictionary<string, string> dict = new Dictionary<string, string>();
while (reader.Read())
{
var key = reader.GetString(0);
var value = reader.GetString(1);
dict[key] = value;
}
return dict;
}
}
}
/// <summary>
/// 开启监听,决定什么时候触发重新加载配置
/// </summary>
/// <param name="trigger"></param>
public void Watch(Action trigger)
{
//表示什么时候出发一次配置重新加载
//比如定时加载
var timer = new System.Timers.Timer();
timer.Interval = 1000 * 60;//一分钟加载一次
timer.Elapsed += new System.Timers.ElapsedEventHandler((sender, e) =>
{
timer.Enabled = false;
trigger.Invoke();
timer.Enabled = true;
});
timer.Start();
}
}

  其中,在实现Watch方法时,我们采用了一个定时间,定时从数据库获取配置,当然,最佳做法是使用一条消息总线来实现,比如采用消息队列等,这里只是简单的说明介绍一下。

  接下来看看怎么使用:  

    static void Main(string[] args)
{
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddCommonConfiguration<MysqlDataProvider>(true);
var configuration = builder.Build();
do
{
Console.Write("请输入指令:");
var line = Console.ReadLine();
if (line == "reload" || line == "r")
{
configuration.Reload();
}
else if (line == "print" || line == "p")
{
var collections = configuration.AsEnumerable();
foreach (var item in collections)
{
Console.WriteLine("{0}={1}", item.Key, item.Value);
}
}
}
while (true);
}

  如果是.net core webapi或者mvc,只需要:  

  

    public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>(); webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddCommonConfiguration<MysqlDataProvider>(true); });
}); }

Program

  运行后输入p:

  

  接下来可以修改数据库中的配置:

  

  接下来只需要等1分钟(MysqlDataProvider 中Watch方法中的定时器1分钟刷新),或者直接输入r,使用IConfigurationRoot的Reload方法强制刷新。

  刷新之后,再输入p,查看到配置已自动更新了。

  

  可以看到,数据库配置以被重新加载。

  

  Redis

  接下来说说Redis,假如我们的Redis有如下结构的配置,其中Config表示的是前缀,表明这个前缀下的RedisKey都是配置:

  

  在集成之前,同样的,我们需要先实现IDataProvider接口:  

    public class RedisDataProvider : IDataProvider
{
StackExchange.Redis.ConnectionMultiplexer connectionMultiplexer;
public StackExchange.Redis.IConnectionMultiplexer GetConnectionMultiplexer()
{
if (connectionMultiplexer == null)
{
var configurationOptions = new StackExchange.Redis.ConfigurationOptions();
configurationOptions.DefaultDatabase = 0;
configurationOptions.EndPoints.Add("192.168.209.128:6379");
connectionMultiplexer = StackExchange.Redis.ConnectionMultiplexer.Connect(configurationOptions);
}
return connectionMultiplexer;
} public IDictionary<string, string> Process()
{
var connectionMultiplexer = GetConnectionMultiplexer();
int database = 0;
var server = connectionMultiplexer.GetServer(connectionMultiplexer.GetEndPoints().First());
var db = connectionMultiplexer.GetDatabase(database);
IDictionary<string, string> dict = new Dictionary<string, string>();
int pageSize = 10;
int pageOffset = 0;
string prefix = "Config";//加载配置节点的前缀
while (true)
{
var keys = server.Keys(
database: database,
pattern: new StackExchange.Redis.RedisValue(prefix + "*"),
pageSize: pageSize,
pageOffset: pageOffset
);
if (keys.Count() == 0) break; foreach (var key in keys)
{
try
{
var value = db.StringGet(key);
dict[key.ToString().Substring(prefix.Length).TrimStart(':')] = value.ToString();
}
catch
{
continue;
}
} pageOffset += pageSize;
}
return dict;
} public void Watch(Action trigger)
{
//采用Redis的发布订阅模式进行监听
var connectionMultiplexer = GetConnectionMultiplexer();
var subscriber = connectionMultiplexer.GetSubscriber();
subscriber.Subscribe(new StackExchange.Redis.RedisChannel("Watch_RedisChannel", StackExchange.Redis.RedisChannel.PatternMode.Auto), (channel, message) =>
{
trigger?.Invoke();
});
}
}

  在介绍数据库的使用时,提到在Watch中推荐使用消息总线来实现重新加载,于是乎,我们利用Redis的订阅与发布来实现这个功能,真是一举两得

  另外,在开发过程中,我们应该讲Redis中配置与缓存等数据分开,比如使用不同的database来存放,这样允许我们在读取配置的时候避免读取到大量的缓存数据而影响到性能。

  接下来看看使用:  

    static void Main(string[] args)
{
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddCommonConfiguration<RedisDataProvider>(true);
var configuration = builder.Build();
do
{
Console.Write("请输入指令:");
var line = Console.ReadLine();
if (line == "reload" || line == "r")
{
configuration.Reload();
}
else if (line == "publish")
{
var configurationOptions = new StackExchange.Redis.ConfigurationOptions();
configurationOptions.DefaultDatabase = 0;
configurationOptions.EndPoints.Add("192.168.209.128:6379");
var connectionMultiplexer = new RedisDataProvider().GetConnectionMultiplexer();
connectionMultiplexer.GetSubscriber().Publish("Watch_RedisChannel", "Reload");
}
else if (line == "print" || line == "p")
{
var collections = configuration.AsEnumerable();
foreach (var item in collections)
{
Console.WriteLine("{0}={1}", item.Key, item.Value);
}
}
}
while (true);
}

  如果是.net core webapi或者mvc,只需要:  

  

    public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>(); webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddCommonConfiguration<RedisDataProvider>(true);
});
}); }

Program

  运行后输入p:

  

  接下来,我们修改Redis中的配置,比如使用RedisManager工具修改,比如修改一个配置:

  

  保存后,在控制台输入publish发布重新加载的订阅消息,或者输入r,使用IConfigurationRoot的Reload方法强制刷新。

  刷新之后,再输入p,查看到配置已自动更新了。

  

  可见集成完成!

  总结

  通过这里的例子,应该能对.net core提供的配合有个更完整的了解。这里虽然给出了一种通用集成配置的方案,比如数据库和Redis的集成,但是还需要读者提供一个IDataProvider接口的实现类,不过这也算是一种练习吧。

  

.net core的配置介绍(二):自定义配置(Zookeeper,数据库,Redis)的更多相关文章

  1. AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(二):配置WinClient分布式运行环境

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  2. VS2012 常用web.config配置解析之自定义配置节点

    在web.config文件中拥有一个用户自定义配置节点configSections,这个节点可以方便用户在web.config中随意的添加配置节点,让程序更加灵活(主要用于第三方插件的配置使用) 自定 ...

  3. Ubuntu配置OpenStack 二:配置时间同步NTP和安装数据库Maridb以及问题总结

    继上一节Ubuntu配置OpenStack 一:配置主机环境,下面继续为安装时间同步,以及配置openstack的安装包源和安装数据库Maridb.(全文截图都是由自己徒手搭建完成并且截图) 一.安装 ...

  4. nginx介绍(二) - 默认配置

    前言 前面, 在浏览器中, 输入linux 的ip, 出现了以下页面: 那这个页面在哪里呢? 一. 工具 notepad++ 在进入主题之前, 先来介绍下, 一会使用到的工具. 在notepad++里 ...

  5. flume安装及配置介绍(二)

    注: 环境: skylin-linux Flume的下载方式: wget http://www.apache.org/dyn/closer.lua/flume/1.6.0/apache-flume-1 ...

  6. ODI中通过配置表和自定义逆向工程获取数据库信息

    自定义逆向工程RKM 从配置表meta_db, meta_table, meta_column, meta_key中获取生产库的元数据信息.

  7. MongoDB副本集配置系列二:配置MongoDB副本集

    接上一篇博客: http://www.cnblogs.com/xiaoit/p/4479066.html 1:首先创建3台虚拟机作为配置环境 IP1:192.168.91.128 IP2:192.16 ...

  8. 接口--全局异常配置--异常处理handle自定义配置

    在重写了异常处理的handle类之后需要配置配置文件中handle的路径:

  9. [ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  10. [ASP.NET Core 3框架揭秘] 配置[4]:将配置绑定为对象

    虽然应用程序可以直接利用通过IConfigurationBuilder对象创建的IConfiguration对象来提取配置数据,但是我们更倾向于将其转换成一个POCO对象,以面向对象的方式来使用配置, ...

随机推荐

  1. 利用Lombok编写优雅的spring依赖注入代码,去掉繁人的@Autowired

    大家平时使用spring依赖注入,都是怎么写的? @Servicepublic class OrderService {@Autowiredprivate UserService userServic ...

  2. 搭建内网Yum源

    搭建内网yum源 阅读(2,238) 一:因内网服务器 众多,当统一安装一些比较大的rpm的时候全部从外网下载就比较慢,而且还占用了一定的出口流量,因此在内网部署了一台yum服务器,将阿里云的epel ...

  3. linux系统下安装dubbo-admin

    1.在安装dubbo-admin之前确保你得linux服务器上已经成功安装了jdk,tomcat, 若还没安装jdk以及tomcat则参考我的上一篇文章"linux环境下安装jdk,tomc ...

  4. 安装xampp开发环境更改默认项目路径

    xampp开发环境中默认的项目路径在xampp下的htdocs文件下 如果想修改默认项目的位置步骤如下: 1)D:\xampp\apache\conf 找到httpd.conf打开 2)找到 Docu ...

  5. [BSidesCF 2019]Runit(细心)

    这道题目并不难,甚至很简单,通过这个文章提醒自己一定要细心 例行检查我就不放了 首先程序开启了nx这个保护,但是首先buf却鲜卑mmap映射了 然后程序又调用了buf 所以这道题直接上传shellco ...

  6. [BUUCTF]REVERSE——firmware

    firmware 附件 步骤: 检查文件没有看出什么,ida载入一堆乱码,看了其他师傅的wp才知道要先binwalk对文件进行提取 120200.squashfs这是一个linux的压缩文件 我们需要 ...

  7. 返回记录中的指定内容Record.Field…(Power Query 之 M 语言)

    返回指定字段: = Record.Field( 记录, "字段名") = Record.FieldOrDefault( 记录, "字段名", 找不到时返回的值) ...

  8. 4、BFS算法套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  9. 大学MOOC课程视频下载、流文件合并、批量重命名、b站视频下载及学习课程视频推荐

    计算机行业技术更新快,编程语言种类多,在当今大数据和人工智能的时代,为了能在相关领域有所成就,就必须掌握好python.R等语言,较好的数学基础和深入的行业背景知识.计算机从业人员务必践行" ...

  10. IPtables 之“四表五链”

    目录 架构图 IP tables 简介 包过滤防火墙 Iptables如何过滤 "四表" "五链" Iptables流程 架构图 公司架构模式(酒店迎宾比喻) ...