Redis 入門與 ASP.NET Core 緩存


如果你還沒有 redis 集群,可以參考筆者的另一篇文章:搭建分布式 Redis Cluster 集群與 Redis 入門

本文將使用 StackExchange.Redis 庫來連接和操作 Redis 。

StackExchange.Redis 的使用,本文只是參照文檔,換種方式表示,如果英文基礎好,建議閱讀文檔:https://stackexchange.github.io/StackExchange.Redis/Basics

本文內容介紹 StackExchange.Redis 的使用基礎,然后介紹 ASP.NET Core 中的緩存、如何使用 Redis。

基礎

Redis 庫

C# 下 Redis-Client 開源的庫很多,有 BeetleX.Redis、csredis、Nhiredis、redis-sharp、redisboost、Rediska、ServiceStack.Redis、Sider、StackExchange.Redis、TeamDev Redis Client。

這里我們使用 StackExchange.Redis,另外 csredis 現在葉老板(Freesql作者)貢獻了大量維護,並且葉老板新開了一個叫 FreeRedis 的框架,目前正在開發中,有興趣可以參與開發或提出建議。

連接 Redis

創建一個 .NET Core 項目,Nuget 庫添加引用 StackExchange.Redis ,使用最新版本。

Redis 默認端口為 6379,如果要連接本地 Redis 服務:

ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
// ”{ip}:{port}“

如果使用 redis 集群,則使用 , 分隔地址:

ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:port1,server2:port2,server3:port3");

可能要注意區分集群模式,多 redis 實例的地址,適合主從模式的集群或者 redis culster 集群,哨兵模式筆者還沒有測試過。

能用 redis 干啥

redis 具有很多應用場景,一般使用到的場景有:

  • 存儲數據(當數據庫使用)
  • 利用 pub/sub 做消息隊列

接下來將介紹這兩種場景的使用方法。

Redis 數據庫存儲

訪問 redis 數據庫:

IDatabase db = redis.GetDatabase();

Redis 默認有 16 個數據庫,可以 GetDatabase(int db ) 獲取指定的數據庫。

使用了 redis cluster 集群的 redis 節點,只有一個數據庫,不能自由選擇。這里我們只需要使用 redis.GetDatabase() 即可 。

Redis 使用比較簡單的,大多時候,只要有相應的應用場景,我們查詢文檔很快就可以掌握,所以這里只介紹字符串的使用。

字符串

redis 的字符串參考:https://www.cnblogs.com/whuanle/p/13837153.html#字符串string

IDatabase 中包含 string 類型的數據操作,其 API 使用 String 開頭,辨識度高。

設置一個字符串數據:

            db.StringSet("A", "這是一條字符串數據的值");
            var value = db.StringGet("A");

如果字符串使用 byte[] (二進制)存儲,也可以設置值:

            byte[] str=... ...
            db.StringSet("A", str;

Redis 里面,還有其它很多類型,這里我們只介紹字符串,因為 API 其實就那么些,用到的時候再學也可以的。先學字符串的使用,其它就是觸類旁通了。

訂閱發布

訂閱某個 Topic,當其改變狀態時,訂閱者可以收到通知,做分布式消息隊列也行。類似 MQTT 協議這樣。

獲取訂閱器:

ISubscriber sub = redis.GetSubscriber();

選擇訂閱的 Topic,並設置回調函數:

sub.Subscribe("Message", (channel, message) => {
    Console.WriteLine((string)message);
});

當某一方訂閱了 Message ,在另一個地方,有別的客戶端(也可以是自己)推送 Topic :

sub.Publish("Message","你有一條新的消息,請注意查收");

Topic 推送后,訂閱方可以收到推送的消息。

測試代碼

            ISubscriber sub = redis.GetSubscriber();

            sub.Subscribe("Message", (channel, message) => {
                Console.WriteLine((string)message);
            });

            Thread.Sleep(1000);

            sub.Publish("Message","你有一條新的消息,請注意查收");

channel :Topic 的名稱,即上面的 Message。

message:推送的消息內容。

RedisValue

使用 API 設置值的時候,都會有這個參數。因為 Redis 中的值只能是 “字符串”,因此 C# 中也要遵守這種規則,但是 C# 是強類型語言,而且有那么多值類型,只使用 string ,編寫代碼時會有諸多不便。

因此,就創建了 RedisValue 這個類型,里面有大量的隱式轉換重載,所以我們可以使用 C# 的簡單類型存儲數據以及獲取數據,避免手工轉換。

當然這個說法不是很准確,使用 RedisValue 主要考慮轉換方便。

RedisValue隱式轉換

入門的知識就介紹到這里,更多的 Redis 知識可以查看官方文檔。下面開始介紹 AS.NET Core 使用分布式緩存。

ASP.NET Core 緩存與分布式緩存

ASP.NET Core 里面有很多定義的標准接口,例如日志、緩存等,這些接口為開發者設置了統一的定義和功能,上層服務不需要變更代碼就能切換類庫,底層使用哪種庫對上層沒有影響。

ASP.NET Core 中的緩存,可以使用多種方式完成,例如 Redis,內存,關系型數據庫,文件緩存等。而且根據拓展性,可以分為本機緩存,分布式緩存。

本機緩存常見的是內存緩存,內存緩存可以存儲任何對象。 分布式緩存最常見的是 Redis,分布式緩存接口僅限 byte[](指參數,繼續看到后面的小節就明白了) 。 內存緩存和分布式緩存都使用鍵值對來存儲緩存項。

內存中的緩存

ASP.NET Core 的內存緩存

ASP.NET Core 內存緩存是指一般是單機(本機)使用的,一般這種內存緩存框架是 System.Runtime 或 Microsoft 包提供的,因為不需要考慮分布式或者復雜的結構,所以一般不需要第三方庫。這里的內存緩存並不只是指數據在內存中,所以需要區分 Redis 這類專業的緩存框架。且這里緩存只是作為提高性能而用。

這種緩存主要有兩種功能比較豐富的實現 System.Runtime.CachingMemoryCache`。

在內存中緩存、存儲數據

在 ASP.NET Core 的內存緩存之外,我們來討論一下,編寫代碼時,自己設置的內存緩存是否合理。

我們都知道,使用內存緩存是為了提高代碼性能而用的

這里筆者個人認為可以從兩個層次來對這種緩存歸類討論。

第一種,對於要多次使用、而每次使用都需要計算、源數據相同則結果相同的,可以使用內存緩存。例如反射就比較消耗時間(速度慢),可以使用內存緩存起來,下次直接取得信息而不需要重新計算。

下面筆者說一下理由。

內存緩存用在反射緩存這類緩存上,緩存的數據源是可確定的、可計算總量的,而且這部分內存不需要頻繁增加或者減少,不僅提高了性能,對 GC 來說也可以一定程度上減少回收壓力,更重要的是開發者可以降低緩存的復雜程度。

這種緩存主要為了避免重復計算,或者重復導入(例如加載程序集、從文件加載數據)等。如果數據最近出現過,而且后面一段時間不會變化,使用內存來緩存也很實在,例如 MVC 的視圖、每15分鍾刷新一次的排行榜等。

第二種是使用內存存儲數據,很多人單純是因為內存存儲數據特別快,把內存當作數據庫來玩,因此很容易導致內存泄露。最常見的就是使用靜態字典、靜態列表等,然后編寫方法增刪查改數據,這一類在壓力測試下或者請求量大一些、變動比較頻繁的時候,內存堆積特別厲害。

需要頻繁變化或需要實時變化的數據,存儲在內存中確實速度非常快,如何確定數據失效、去除無用數據等需要有很深的考慮。

另外,在內存中如使用字典大量存儲數據,數據量很多的情況下,每次索引數據的時間都會變長,如果使用了 Linq 或者 for 或者 foreach 等檢索數據,也很容易出現耗時長的時間復雜度。這種情況下,你是相信自己的代碼,還是相信 Mysql、SqlServer 等數據庫? Hash 算法和紅黑樹都了解了嘛?

如果實在有需求需要使用內存緩存數據,並且可能動態增加或移除數據的話,可以使用 WeakReference 弱引用,即在引用對象的同時仍然允許 GC 回收該對象。缺點是數據可能丟失,不適合需要持久化的數據。

但無論情況,我們可以確定:

  • 緩存都是副本
  • 緩存丟失不影響程序的使用
  • 緩存不能無限增長
  • 緩存避免復雜結構
  • ... ...

IMemoryCache

IMemoryCache 提供的接口太少了:

        ICacheEntry CreateEntry(object key);
        void Remove(object key);
        bool TryGetValue(object key, out object value);

適合單一的鍵值緩存。

此接口在 Microsoft.Extensions.Caching.Memory 中有實現,例如 MemoryCache 。適合 ASP.NET Core 中使用。

MemoryCache

這里的 MemoryCache 並不是指 IMemoryCache 的實現,而是指 System.Runtime.Caching.MemoryCache,需要安裝 Nuget 包。

可以實現對實例對象的緩存,請查看查看官方文檔:https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.caching.memorycache?view=dotnet-plat-ext-3.1

另外內存緩存還有一個分布式內存緩存,但不是真正的分布式,信息可以參考:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.1#distributed-memory-cache

分布式緩存

ASP.NET Core 分布式緩存,則使用了 IDistributedCache 這個統一的接口。如果你在 Nuget 搜索 IDistributedCache ,會發現相關的庫非常多。

分布式緩存的使用,除了最常見的 Redis,SQLServer 也行,只要實現了 IDistributedCache 就ok。

IDistributedCache

IDistributedCache

IDistributedCache 接口提供的方法實在太少了,有四個異步方法四個同步方法,這里只介紹異步方法。

方法 說明
GetAsync(String, CancellationToken) 獲取一個鍵的值
RefreshAsync(String, CancellationToken) 基於緩存中某個值的鍵刷新該值,並重置其可調到期超時(如果有)
RemoveAsync(String, CancellationToken) 刪除一個鍵
SetAsync(String, Byte[], DistributedCacheEntryOptions, CancellationToken) 設置一個鍵的值

局限還是很大的,只能使用字符串。估計大家可能沒怎么使用?

ASP.NET Core 官方支持的分布式緩存,目前主要有 NCache、Redis、SqlServer。本節只討論 Redis。

Redis 緩存

StackExchange.Redis 是 ASP.NET Core 官方推薦的 Redis 框架,並且官方對其做了封裝,可以到 Nuget 搜索 Microsoft.Extensions.Caching.StackExchangeRedis

RedisCache 繼承了 IDistributedCache 接口。

Startup.ConfigureServices 中配置服務注冊:

            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = "ip:端口,ip1:端口,ip2:端口";	// redis 集群或單機
                options.InstanceName = "mvc";						// 實例 名稱
            });

依賴注入:

        private readonly IDistributedCache _cache;

示例:

        public async Task<string> Test(string key,string value)
        {
            await _cache.SetStringAsync(key, value);
            return await _cache.GetStringAsync(key);
        }

設置緩存時間:

            var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
            await _cache.SetStringAsync(key, value, options);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM