1. 程式人生 > 其它 >Redis分散式快取承載於 “Microsoft.Extensions.Caching.Redis”這個NuGet包

Redis分散式快取承載於 “Microsoft.Extensions.Caching.Redis”這個NuGet包

Redis分散式快取承載於 “Microsoft.Extensions.Caching.Redis”這個NuGet包中,我們需要手動新增針對該NuGet包的依賴。

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;

var cache = new ServiceCollection().AddDistributedRedisCache(options => {
        options.Configuration	= "localhost";
        options.InstanceName 	= "Demo";
    })
.BuildServiceProvider()
.GetRequiredService<IDistributedCache>();

for (int index = 0; index < 5; index++)
{
    Console.WriteLine(await GetCurrentTimeAsync());
    await Task.Delay(1000);
}

async Task<DateTimeOffset> GetCurrentTimeAsync()
{
    var timeLiteral = await cache.GetStringAsync("CurrentTime");
    if (string.IsNullOrEmpty(timeLiteral))
    {
        await cache.SetStringAsync("CurrentTime", timeLiteral = DateTimeOffset.UtcNow.ToString());
    }
    return DateTimeOffset.Parse(timeLiteral);
}

從上面的程式碼片段可以看出,分散式快取和記憶體快取在總體程式設計模式上是一致的,我們需要先完成針對IDistributedCache服務的註冊,然後利用依賴注入框架提供該服務物件來進行快取資料的讀和寫。IDistributedCache服務的註冊是通過呼叫IServiceCollection介面的AddDistributedRedisCache方法來完成的。我們在呼叫這個方法時提供了一個RedisCacheOptions物件,並利用它的Configuration和InstanceName屬性設定Redis資料庫的伺服器與例項名稱。

由於採用的是本地的Redis伺服器,所以我們將Configuration屬性設定為localhost。其實Redis資料庫並沒有所謂的例項的概念,RedisCacheOptions型別的InstanceName屬性的目的在於當多個應用共享同一個Redis資料庫時,快取資料可以利用它進行區分。當快取資料被儲存到Redis資料庫中的時候,對應的Key以InstanceName為字首。應用程式啟動後(確保Redis伺服器被正常啟動),如果我們利用瀏覽器來訪問它,依然可以得到與圖1類似的輸出。

對於基於記憶體的本地快取來說,我們可以將任何型別的資料置於快取之中,但是分散式快取涉及網路傳輸和持久化儲存,置於快取中的資料型別只能是位元組陣列,所以我們需要自行負責對快取物件的序列化和反序列化工作。如上面的程式碼片段所示,我們先將表示當前時間的DateTime物件轉換成字串,然後採用UTF-8編碼進一步轉換成位元組陣列。我們呼叫IDistributedCache介面的SetAsync方法快取的資料是最終的位元組陣列。我們也可以直接呼叫SetStringAsync擴充套件方法將字串編碼為位元組陣列。在讀取快取資料時,我們呼叫的是IDistributedCache介面的GetStringAsync方法,它會將位元組陣列轉換成字串。

快取資料在Redis資料庫中是以雜湊(Hash)的形式存放的,對應的Key會將設定的InstanceName屬性作為字首。為了檢視在Redis資料庫中究竟存放了哪些資料,我們可以按照圖4所示的形式執行Redis命令獲取儲存的資料。從輸出結果可以看出存入Redis資料庫的不僅包括指定的快取資料(Sub-Key為data),還包括其他兩組針對該快取條目的描述資訊,對應的Sub-Key分別為absexp和sldexp,表示快取的絕對過期時間(Absolute Expiration Time)和滑動過期時間(Sliding Expiration Time)。


圖4 檢視Redis資料庫中存放的資料

[S1103]基於SQL Server的分散式快取

除了使用Redis這種主流的NoSQL資料庫來支援分散式快取,還可以使用關係型資料庫SQL Server。針對SQL Server的分散式快取實現在NuGet包“Microsoft.Extensions.Caching.SqlServer”中,我們需要先確保該NuGet包被正常安裝到演示的應用程式中。針對SQL Server的分散式快取實際上就是將表示快取資料的位元組陣列存放在SQL Server資料庫的某個具有固定結構的資料表中,所以我們需要先建立這樣一個快取表。該表可以通過dotnet-sql-cache命令列工具進行建立。如果該命令列工具尚未安裝,我們可以執行“dotnet tool install --global dotnet-sql-cache”進行安裝。

具體來說,儲存快取資料的表可以採用命令列的形式執行“dotnet sql-cache create”命令來建立。執行這個命令應該指定的引數可以按照如下形式通過執行“dotnet sql-cache create --help”命令來檢視。從圖5可以看出,該命令需要指定三個引數,它們分別表示快取資料庫的連線字串、快取表的Schema和名稱。


圖5 dotnet sql-cache create命令的幫助文件

接下來只需要以命令列的形式執行“dotnet sql-cache create”命令就可以在指定的資料庫中建立快取表。對於演示的例項來說,可以按照圖6所示的方式執行“dotnet sql-cache create”命令,該命令會在本機一個名為DemoDB的資料庫中(資料庫需要預先建立好)建立一個名為AspnetCache的快取表,該表採用dbo作為Schema。


圖6 執行“dotnet sql-cache create”命令建立快取表

在所有的準備工作完成之後,我們只需要對上面的程式做如下修改就可以將快取儲存方式從Redis資料庫切換到針對SQL Server的資料庫。由於採用的同樣是分散式快取,所以針對快取資料的設定和提取的程式碼不用做任何改變,我們需要修改的地方僅僅是服務註冊部分。如下面的程式碼片段所示,我們呼叫IServiceCollection介面的AddDistributedSqlServerCache擴充套件方法完成了對應的服務註冊。在呼叫這個方法的時候,我們通過設定SqlServerCacheOptions物件三個屬性的方式指定了快取資料庫的連線字串、快取表的Schema和名稱。

public class Program
{
    public static void Main()
    {
        Host.CreateDefaultBuilder()
            .ConfigureWebHostDefaults(builder => builder.ConfigureServices(svcs => svcs.AddDistributedSqlServerCache(options =>
                {
                    options.ConnectionString = "server=.;database=demodb;uid=sa;pwd=password";
                    options.SchemaName 	= "dbo";
                    options.TableName 	= "AspnetCache";
                }))
                .Configure(app => app.Run(async context =>
                {
                    var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
                    var currentTime = await cache.GetStringAsync("CurrentTime");
                    if (null == currentTime)
                    {
                        currentTime = DateTime.Now.ToString();
                        await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
                    }
                    await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");

                })))
            .Build()
            .Run();
    }
}

若要檢視最終存入SQL Server資料庫中的快取資料,我們只需要在資料庫中檢視對應的快取表即可。對於演示例項快取的時間戳,它會以圖7所示的形式儲存在我們建立的快取表(AspnetCache)中。與基於Redis資料庫的儲存方式類似,與快取資料的值一併儲存的還包括快取的過期資訊。


圖7 儲存在快取表中的資料

Redis分散式快取承載於 “Microsoft.Extensions.Caching.Redis”這個NuGet包中,我們需要手動新增針對該NuGet包的依賴。

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;

var cache = new ServiceCollection().AddDistributedRedisCache(options => {
        options.Configuration	= "localhost";
        options.InstanceName 	= "Demo";
    })
.BuildServiceProvider()
.GetRequiredService<IDistributedCache>();

for (int index = 0; index < 5; index++)
{
    Console.WriteLine(await GetCurrentTimeAsync());
    await Task.Delay(1000);
}

async Task<DateTimeOffset> GetCurrentTimeAsync()
{
    var timeLiteral = await cache.GetStringAsync("CurrentTime");
    if (string.IsNullOrEmpty(timeLiteral))
    {
        await cache.SetStringAsync("CurrentTime", timeLiteral = DateTimeOffset.UtcNow.ToString());
    }
    return DateTimeOffset.Parse(timeLiteral);
}

從上面的程式碼片段可以看出,分散式快取和記憶體快取在總體程式設計模式上是一致的,我們需要先完成針對IDistributedCache服務的註冊,然後利用依賴注入框架提供該服務物件來進行快取資料的讀和寫。IDistributedCache服務的註冊是通過呼叫IServiceCollection介面的AddDistributedRedisCache方法來完成的。我們在呼叫這個方法時提供了一個RedisCacheOptions物件,並利用它的Configuration和InstanceName屬性設定Redis資料庫的伺服器與例項名稱。

由於採用的是本地的Redis伺服器,所以我們將Configuration屬性設定為localhost。其實Redis資料庫並沒有所謂的例項的概念,RedisCacheOptions型別的InstanceName屬性的目的在於當多個應用共享同一個Redis資料庫時,快取資料可以利用它進行區分。當快取資料被儲存到Redis資料庫中的時候,對應的Key以InstanceName為字首。應用程式啟動後(確保Redis伺服器被正常啟動),如果我們利用瀏覽器來訪問它,依然可以得到與圖1類似的輸出。

對於基於記憶體的本地快取來說,我們可以將任何型別的資料置於快取之中,但是分散式快取涉及網路傳輸和持久化儲存,置於快取中的資料型別只能是位元組陣列,所以我們需要自行負責對快取物件的序列化和反序列化工作。如上面的程式碼片段所示,我們先將表示當前時間的DateTime物件轉換成字串,然後採用UTF-8編碼進一步轉換成位元組陣列。我們呼叫IDistributedCache介面的SetAsync方法快取的資料是最終的位元組陣列。我們也可以直接呼叫SetStringAsync擴充套件方法將字串編碼為位元組陣列。在讀取快取資料時,我們呼叫的是IDistributedCache介面的GetStringAsync方法,它會將位元組陣列轉換成字串。

快取資料在Redis資料庫中是以雜湊(Hash)的形式存放的,對應的Key會將設定的InstanceName屬性作為字首。為了檢視在Redis資料庫中究竟存放了哪些資料,我們可以按照圖4所示的形式執行Redis命令獲取儲存的資料。從輸出結果可以看出存入Redis資料庫的不僅包括指定的快取資料(Sub-Key為data),還包括其他兩組針對該快取條目的描述資訊,對應的Sub-Key分別為absexp和sldexp,表示快取的絕對過期時間(Absolute Expiration Time)和滑動過期時間(Sliding Expiration Time)。


圖4 檢視Redis資料庫中存放的資料

[S1103]基於SQL Server的分散式快取

除了使用Redis這種主流的NoSQL資料庫來支援分散式快取,還可以使用關係型資料庫SQL Server。針對SQL Server的分散式快取實現在NuGet包“Microsoft.Extensions.Caching.SqlServer”中,我們需要先確保該NuGet包被正常安裝到演示的應用程式中。針對SQL Server的分散式快取實際上就是將表示快取資料的位元組陣列存放在SQL Server資料庫的某個具有固定結構的資料表中,所以我們需要先建立這樣一個快取表。該表可以通過dotnet-sql-cache命令列工具進行建立。如果該命令列工具尚未安裝,我們可以執行“dotnet tool install --global dotnet-sql-cache”進行安裝。

具體來說,儲存快取資料的表可以採用命令列的形式執行“dotnet sql-cache create”命令來建立。執行這個命令應該指定的引數可以按照如下形式通過執行“dotnet sql-cache create --help”命令來檢視。從圖5可以看出,該命令需要指定三個引數,它們分別表示快取資料庫的連線字串、快取表的Schema和名稱。


圖5 dotnet sql-cache create命令的幫助文件

接下來只需要以命令列的形式執行“dotnet sql-cache create”命令就可以在指定的資料庫中建立快取表。對於演示的例項來說,可以按照圖6所示的方式執行“dotnet sql-cache create”命令,該命令會在本機一個名為DemoDB的資料庫中(資料庫需要預先建立好)建立一個名為AspnetCache的快取表,該表採用dbo作為Schema。


圖6 執行“dotnet sql-cache create”命令建立快取表

在所有的準備工作完成之後,我們只需要對上面的程式做如下修改就可以將快取儲存方式從Redis資料庫切換到針對SQL Server的資料庫。由於採用的同樣是分散式快取,所以針對快取資料的設定和提取的程式碼不用做任何改變,我們需要修改的地方僅僅是服務註冊部分。如下面的程式碼片段所示,我們呼叫IServiceCollection介面的AddDistributedSqlServerCache擴充套件方法完成了對應的服務註冊。在呼叫這個方法的時候,我們通過設定SqlServerCacheOptions物件三個屬性的方式指定了快取資料庫的連線字串、快取表的Schema和名稱。

public class Program
{
    public static void Main()
    {
        Host.CreateDefaultBuilder()
            .ConfigureWebHostDefaults(builder => builder.ConfigureServices(svcs => svcs.AddDistributedSqlServerCache(options =>
                {
                    options.ConnectionString = "server=.;database=demodb;uid=sa;pwd=password";
                    options.SchemaName 	= "dbo";
                    options.TableName 	= "AspnetCache";
                }))
                .Configure(app => app.Run(async context =>
                {
                    var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
                    var currentTime = await cache.GetStringAsync("CurrentTime");
                    if (null == currentTime)
                    {
                        currentTime = DateTime.Now.ToString();
                        await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
                    }
                    await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");

                })))
            .Build()
            .Run();
    }
}

若要檢視最終存入SQL Server資料庫中的快取資料,我們只需要在資料庫中檢視對應的快取表即可。對於演示例項快取的時間戳,它會以圖7所示的形式儲存在我們建立的快取表(AspnetCache)中。與基於Redis資料庫的儲存方式類似,與快取資料的值一併儲存的還包括快取的過期資訊。


圖7 儲存在快取表中的資料

  分類: [02] 程式設計技巧