Redis 入門與 ASP.NET Core 緩存

若是你尚未 redis 集羣,能夠參考筆者的另外一篇文章:搭建分佈式 Redis Cluster 集羣與 Redis 入門html

本文將使用 StackExchange.Redis 庫來鏈接和操做 Redis 。git

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

本文內容介紹 StackExchange.Redis 的使用基礎,而後介紹 ASP.NET Core 中的緩存、如何使用 Redis。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 的框架,目前正在開發中,有興趣能夠參與開發或提出建議。sql

鏈接 Redis

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

Redis 默認端口爲 6379,若是要鏈接本地 Redis 服務:api

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 集羣,哨兵模式筆者尚未測試過。mvc

能用 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);
相關文章
相關標籤/搜索