日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

从Redis读取.NET Core配置

發布時間:2023/12/29 asp.net 42 coder
生活随笔 收集整理的這篇文章主要介紹了 从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

注意事項

  1. Redis本身自帶持久化策略,但是有的企業/團隊沒有開啟或者是特意關閉了持久化,因此需要謹慎使用此方案。
  2. 強烈建議將存儲配置數據的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配置的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 黄色一级片免费观看 | 五月天婷婷伊人 | 欧美激情国产精品 | av在线有码| 精品成在人线av无码免费看 | 国产精品hd | 超碰人人网 | 高清毛片aaaaaaaaa郊外 | 午夜裸体性播放 | 一级 黄 色 片69 | 在线免费观看黄色av | 成人激情视频在线观看 | 久久婷婷五月国产色综合激情 | 国产精品拍拍 | 国产伦精品一区二区三区四区免费 | 秒拍福利视频 | 18日本xxxxxxxxx95 国产又好看的毛片 | 国产欧美日韩三级 | 久久春色 | 免费成人在线观看视频 | 高清在线一区二区三区 | 久久久久99精品国产片 | 国产伦精品一区二区三区妓女下载 | www.一起操| 日韩综合一区二区 | 欧洲一级黄 | 樱花影院电视剧免费 | 奇米影视久久久 | 精品国产一区二区三 | av基地网| 另类天堂网 | www欧美在线| 久久国产视频网 | 日韩成人看片 | 黄色精品视频 | 三级黄色小视频 | 久久看片 | 女人喷潮完整视频 | 国产精品一区二区黑人巨大 | 乱日视频 | 国产一区影院 | 国产传媒一区 | 一边摸一边抽搐一进一出视频 | 18pao国产成视频永久免费 | 久久久五月 | 快播日韩 | 91激情 | 欧美十大老熟艳星 | 精品国产一区二区三区av性色 | 图片区视频区小说区 | 亚洲国产婷婷香蕉久久久久久99 | 亚洲码在线观看 | 亚洲第一网站 | 精品无码成人久久久久久免费 | 国产精品久久婷婷 | 中文字幕欧美在线 | 九九视频免费看 | 黄色片链接 | 欧美aaa大片 | 91一区二区三区 | 精品日韩一区二区 | 韩日免费av | 成人欧美一区二区三区在线播放 | 波多野结衣一区在线 | 欧美一级淫片免费视频魅影视频 | 国产精品区一 | 97视频网站 | 好色999| 国产乱子轮xxx农村 岛国久久久 | 福利在线播放 | 中文字幕在线视频精品 | 日本精品三区 | 亚洲一区二区免费视频 | 亚洲精品乱码久久久久久自慰 | 一区二区三区视频网 | 青青草偷拍视频 | 午夜福利毛片 | 色先锋在线| 91黄免费| 伊人网在线观看 | 一级黄色大片免费观看 | 三级自拍视频 | 国产一级二级三级 | 日韩高清一区二区 | 国产色无码精品视频国产 | 国产欧美一区二区视频 | 加勒比色综合 | 日xxxx| 国产精品自拍偷拍 | 国产男男chinese网站 | 未满十八18禁止免费无码网站 | 亚洲福利视频一区二区三区 | 青娱网电信一区电信二区电信三区 | 高跟鞋调教—视频|vk | 国产黄站 | 精品久久久久久无码中文野结衣 | 欧美性生交片4 | 日本一区二区视频免费 | 天天舔天天干天天操 |