从Redis读取.NET Core配置
2023/12/25 14:03:06
本文主要是介绍从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
不想动手的朋友可以直接用我已经制作好的包
https://www.nuget.org/packages/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配置的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-11-18微软研究:RAG系统的四个层次提升理解与回答能力
- 2024-11-15C#中怎么从PEM格式的证书中提取公钥?-icode9专业技术文章分享
- 2024-11-14云架构设计——如何用diagrams.net绘制专业的AWS架构图?
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#
- 2024-01-24Advanced .Net Debugging 1:你必须知道的调试工具