从Redis读取.NET Core配置
在本文中,我们将创建一个自定义的.NET Core应用配置源和提供程序,用于从Redis中读取配置。在此之前,您需要稍微了解一些.NET Core配置提供程序的工作原理,相关的内容可以在Microsoft开发者官网搜索到。另外您可能还需要了解一些Redis的基础知识,比如Redis的基础数据类型,持久化等等。
一、配置的数据格式
.NET Core应用支持多种配置源(例如json、xml、ini文件,环境变量,内存字典,自定义源等),并且支持同时添加多个配置源,这也是本文的前提条件。应用程序会按照加入的先后顺序替换或补充配置。默认情况下,.NET Core应用的配置是存储在appsettings.json文件中的。在早期的.NET Core应用中,Program.cs的CreateHost方法里面还能看到AddJsonFile("appsettings.json").AddJsonFile($"appsetting.{env.Environment}.json")
这样的代码,但是.NET 5以后,这段代码默认被隐藏了。
看过源码的朋友应该知道,.NET Core应用读取配置后,会将数据转换为一个Key和Value都是string的字典。Key的格式为Node1:Node2:abc
。例如:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=myserver;Database=mydb;User=myuser;Password=mypassword;"
},
"AppSettings": {
"ApiBaseUrl": "https://api.example.com",
"ApiKey": "your-api-key"
},
"AllowedHost":["foo1.com","foo2.com"]
}
转换后的数据为:
Logging:LogLevel:Default=Information
Logging:LogLevel:Microsoft=Warning
Logging:LogLevel:Microsoft.Hosting.Lifetime=Information
ConnectionStrings:DefaultConnection=Server=myserver;Database=mydb;User=myuser;Password=mypassword;
AppSettings:ApiBaseUrl=https://api.example.com
AppSettings:ApiKey=your-api-key
AllowedHost:0=foo1.com
AllowedHost:1=foo2.com
二、Redis的Hash类型
通过上面介绍,Redis的Hash数据结构刚好完美的切合了这一特点。先简单的介绍一下:
在Redis中,Hash是一种数据结构,用于存储键值对的集合,其中每个键都映射到一个值。Redis的Hash是一个键值对的无序集合,其中的每个键都是唯一的。Hash是一个类似于字典或关联数组的概念,在其他编程语言中也称为Map或Dictionary。
三、代码实现
创建好项目之后,我们需要安装一个NuGet包,就是大家熟知的StackExchange.Redis,到目前为止应该是.NET应用程序使用最多的Redis客户端。
PM> Install-Package StackExchange.Redis -v 2.7.10
您也可以通过Visual Studio、Rider自带的NuGet客户端安装,或者是直接在csproj文件中加入<PackageReference Include="StackExchange.Redis" Version="2.7.10" />
。
RedisConfigurationProvider.cs
public sealed class RedisConfigurationProvider : ConfigurationProvider, IAsyncDisposable
{
private readonly ConnectionMultiplexer _connection;
private readonly IDatabase _database;
private readonly string _key;
public RedisConfigurationProvider(RedisConfigurationSource source)
{
_key = source.Key;
_connection = ConnectionMultiplexer.Connect(source.ConnectionString);
_database = _connection.GetDatabase(source.Database);
}
/// <inheritdoc />
public override void Load()
{
Data = _connection.HashGetAll(_key).ToDictionary(x => x.Name.ToString(), x => ReadRedisValue(x.Value);
}
private static string ReadRedisValue(RedisValue value)
{
if (value.IsNull)
{
return null;
}
return value.IsNullOrEmpty ? string.Empty : value.ToString();
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
await _connection.CloseAsync();
await _connection.DisposeAsync();
}
}
RedisConfigurationSource.cs
public sealed class RedisConfigurationSource : IConfigurationSource
{
/// <summary>
/// The Redis connection string.
/// </summary>
[DisallowNull]
public string ConnectionString { get; set; }
/// <summary>
/// Gets or sets the Redis database ID.
/// </summary>
public int Database { get; set; } = -1;
/// <summary>
/// Gets or sets the Redis key this source will read from.
/// </summary>
/// <remarks>
/// The key is expected to be a hash.
/// </remarks>
public string Key { get; set; } = "appsettings";
/// <inheritdoc />
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new RedisConfigurationProvider(this);
}
}
关键代码就这些,看上去似乎很简单……事实上确实很简单。
添加配置源
添加配置源的方法也很简单
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.Add(new RedisConfigurationSource
{
ConnectionString = "localhost:6379",
Key = "appsettings.dev"
});
RedisConfigurationSource里面总共只有三个属性,ConnectionString用于配置Redis连接字符串,Database用于指定从哪个数据库读取数据,也可以在连接字符串里面指定。Key用于指定要读取的键名称。
通过编写一些简单的代码,我们实现了一个能满足基本需求的分布式.NET Core配置提供程序。
Starfish.Redis
不想动手的朋友可以直接用我已经制作好的包
安装
Visual Studio包管理器搜索Starfish.Redis,或者执行dotnet add package Starfish.Redis
。
配置
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddRedis("127.0.0.1:6379,defaultDatabase=0,connectTimeout=5000,connectRetry=3", "appsettings");
启用Redis Keyspace Notifications
Starfish.Redis有两种机制用于实现ReloadOnChanged(配置修改后重新加载数据),一种是定时查询指定的Key,时效性稍微差一些。另一种是利用Redis的Keyspace Event和Pub/Sub模式来实现,当订阅的Key发生变化(删除、修改、过期等)时会主动发送通知给订阅者,使用这种模式需要配置Redis服务的notify-keyspace-events。
关于notify-keyspace-events配置,可参考下面的描述:
- K:Keyspace事件,将会以__keyspace@__作为事件的前缀
- E:Keyevent事件,将会以__keyevent@__作为事件的前缀
- g:非特定类型的通用命令,例如DEL、EXPIRE、RENAME等
- $:字符串命令,例如SET、INCR等
- l:列表命令,例如LPUSH、LPOP等
- s:集合命令,例如SADD、SREM等
- h:哈希表命令,例如HSET、HINCRBY等
- z:有序集合命令,例如ZSET、ZREM等
- t:流命令,例如XADD、XDEL等
- x:过期事件(在每个发生键过期的时侯产生)
- e:淘汰事件(在每个发生键被淘汰的时候产生)
- m:未命中事件(在访问某个不存在的键使产生)
- A:配置g$lshztxe的别名,但不包括未命中事件m
简单起见,我们直接配置为AKE(启用所有事件的通知)。
方法一:redis-cli
redis-cli config set notify-keyspace-events AKE
方法二:docker参数
docker run -d --name redisname -p 6379:6379 redis --notify-keyspace-events AKE
方法三:配置文件
找到并打开打开redis.conf,在末尾加上
notify-keyspace-events AKE
注意事项
- Redis本身自带持久化策略,但是有的企业/团队没有开启或者是特意关闭了持久化,因此需要谨慎使用此方案。
- 强烈建议将存储配置数据的key设置为永不过期(TTL设置为-1),避免key过期带来一些不必要的麻烦。
导入appsettings.json到Redis
微软.NET库提供了一个内部类JsonConfigurationFileParser
用于将json格式的配置转换为Dictionary<string, string>
。
namespace Microsoft.Extensions.Configuration.Json
{
internal sealed class JsonConfigurationFileParser
{
private JsonConfigurationFileParser() { }
private readonly Dictionary<string, string?> _data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _paths = new Stack<string>();
public static IDictionary<string, string?> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private Dictionary<string, string?> ParseStream(Stream input)
{
var jsonDocumentOptions = new JsonDocumentOptions
{
CommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
using (var reader = new StreamReader(input))
using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
{
if (doc.RootElement.ValueKind != JsonValueKind.Object)
{
throw new FormatException(SR.Format(SR.Error_InvalidTopLevelJSONElement, doc.RootElement.ValueKind));
}
VisitObjectElement(doc.RootElement);
}
return _data;
}
private void VisitObjectElement(JsonElement element)
{
var isEmpty = true;
foreach (JsonProperty property in element.EnumerateObject())
{
isEmpty = false;
EnterContext(property.Name);
VisitValue(property.Value);
ExitContext();
}
SetNullIfElementIsEmpty(isEmpty);
}
private void VisitArrayElement(JsonElement element)
{
int index = 0;
foreach (JsonElement arrayElement in element.EnumerateArray())
{
EnterContext(index.ToString());
VisitValue(arrayElement);
ExitContext();
index++;
}
SetNullIfElementIsEmpty(isEmpty: index == 0);
}
private void SetNullIfElementIsEmpty(bool isEmpty)
{
if (isEmpty && _paths.Count > 0)
{
_data[_paths.Peek()] = null;
}
}
private void VisitValue(JsonElement value)
{
Debug.Assert(_paths.Count > 0);
switch (value.ValueKind)
{
case JsonValueKind.Object:
VisitObjectElement(value);
break;
case JsonValueKind.Array:
VisitArrayElement(value);
break;
case JsonValueKind.Number:
case JsonValueKind.String:
case JsonValueKind.True:
case JsonValueKind.False:
case JsonValueKind.Null:
string key = _paths.Peek();
if (_data.ContainsKey(key))
{
throw new FormatException(SR.Format(SR.Error_KeyIsDuplicated, key));
}
_data[key] = value.ToString();
break;
default:
throw new FormatException(SR.Format(SR.Error_UnsupportedJSONToken, value.ValueKind));
}
}
private void EnterContext(string context) =>
_paths.Push(_paths.Count > 0 ?
_paths.Peek() + ConfigurationPath.KeyDelimiter + context :
context);
private void ExitContext() => _paths.Pop();
}
}
点关注,不迷路。
如果您喜欢这篇文章,请不要忘记点赞、关注、转发,谢谢!如果您有任何高见,欢迎在评论区留言讨论……
从Redis读取.NET Core配置的更多相关文章
- JavaScript日历控件开发 C# 读取 appconfig文件配置数据库连接字符串,和配置文件 List<T>.ForEach 调用异步方法的意外 ef 增加或者更新的习惯思维 asp.net core导入excel 一个二级联动
JavaScript日历控件开发 概述 在开篇之前,先附上日历的代码地址和演示地址,代码是本文要分析的代码,演示效果是本文要实现的效果代码地址:https://github.com/aspwebc ...
- 关于Asp.net core配置信息读取的源码分析梳理
概述 我们都知道asp.net core配置信息的读取离不开IConfigurationSource和IConfigurationProvider这两个类,ConfigurationSource可以提 ...
- ASP.NET Core - 配置系统之配置读取
一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为. 配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code(硬编码) ...
- Spring Data Redis入门示例:程序配置(五)
单机配置 redis.properties配置 #redis的服务器地址 redis.host=127.0.0.1 #redis的服务端口 redis.port=6379 #客户端超时时间单位是毫秒 ...
- 莫小安 Linux下Redis的安装与配置
转载自--Linux下Redis的安装与配置 redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcached类似,但很大程度补偿了 memcached的不足,它 ...
- Redis:redis.conf配置文件 - 及配置详解
配置文件详解(文章最后有完整的redis.conf文件) ################################### NETWORK ######################### ...
- Redis安装及基本配置
一.Redis介绍 1.Redis是一个key-value存储系统 2.官方站点http://redis.io 3.Redis和Memcached类似,但支持数据持久化 4.支持更多value类型,除 ...
- Redis:安装、配置、操作和简单代码实例(C语言Client端)
Redis:安装.配置.操作和简单代码实例(C语言Client端) - hj19870806的专栏 - 博客频道 - CSDN.NET Redis:安装.配置.操作和简单代码实例(C语言Client端 ...
- 10分钟就能学会的.NET Core配置
.NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...
- Redis in .NET Core 入门:(1) 安装和主要功能简介
Redis(https://redis.io/), 是一个内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 安装Redis 我很少在开发机中直接装各种数据库,我一般使用Docker,针对 ...
随机推荐
- from my mac
hello
- torch-1 tensor & optim
开个新坑, pytorch源码阅读-从python代码开始读起. torch/ 1.tensor.py 继承自torch._C._TensorBase , 包括各种操作,TODO:随后看cpp代码 _ ...
- jenkins部署及gitlab联调
jenkins部署及gitlab联调 目录 jenkins部署及gitlab联调 一.jenkins安装 1.环境优化 2.安装jdk java 环境 3.下载jenkins 4.启动Jenkins服 ...
- .NET周刊【9月第2期 2023-09-10】
国内文章 使用 OpenTelemetry 构建 .NET 应用可观测性(2):OpenTelemetry 项目简介 https://www.cnblogs.com/eventhorizon/p/17 ...
- redis基本数据类型 set类型
127.0.0.1:6379> SADD s1 a b c (integer) 3 127.0.0.1:6379> SMEMBERS s1 1) "b" 2) &quo ...
- OpenJDK17-JVM源码阅读-ZGC-并发标记
1.ZGC简介 1.1 介绍 ZGC 是一款低延迟的垃圾回收器,是 Java 垃圾收集技术的最前沿,理解了 ZGC,那么便可以说理解了 java 最前沿的垃圾收集技术. 从 JDK11 中作为试验特性 ...
- [NISACTF 2022]level-up
[NISACTF 2022]level-up 查看源码,根据这个提示就可以反应出是需要去访问robots.txt这个文件 访问level_2_1s_h3re.php进入第二关 需要post进去arra ...
- 基于 Canal 设计可扩展、高可用 binlog 同步集群
问题 https://github.com/alibaba/canal binlog 同步组件,canal 使用是比较广泛的,canal 逻辑架构如图: 部署架构如图: canal 基于主从模式,任务 ...
- k8s Redis安装部署
一.文档简介 作者:lanjiaxuan 邮箱:lanheader@163.com 博客地址:https://www.cnblogs.com/lanheader/ 更新时间:2021-07-09 安装 ...
- 7/10 luoguRound 10 庆典 div3T1
#include<bits/stdc++.h> using namespace std; int m,n,x; int arr[100005]; int maxi = -1,maxn = ...