1. 程式人生 > >ASP.NET Core中的快取[1]:如何在一個ASP.NET Core應用中使用快取

ASP.NET Core中的快取[1]:如何在一個ASP.NET Core應用中使用快取

.NET Core針對快取提供了很好的支援 ,我們不僅可以選擇將資料快取在應用程序自身的記憶體中,還可以採用分散式的形式將快取資料儲存在一個“中心資料庫”中。對於分散式快取,.NET Core提供了針對Redis和SQL Server的原生支援。除了這個獨立的快取系統之外,ASP.NET Core還藉助一箇中間件實現了“響應快取”,它會按照HTTP快取規範對整個響應實施快取。不過按照慣例,在對快取進行系統介紹之前,我們還是先通過一些簡單的例項演示感知一下如果在一個ASP.NET Core應用中如何使用快取。

目錄
一、將資料快取在記憶體中
二、基於Redis的分散式快取
三、基於SQL Server的分散式快取
四、快取整個HTTP響應

一、將資料快取在記憶體中

與針對資料庫和遠端服務呼叫這種IO操作來說,應用針對記憶體的訪問效能將提供不止一個數量級的提升,所以將資料直接快取在應用程序的內容中自然具有最佳的效能優勢。與基於記憶體的快取相關的應用程式設計介面定義在NuGet包“Microsoft.Extensions.Caching.Memory”中,具體的快取實現在一個名為MemoryCache的服務物件中,後者是我們對所有實現了IMemoryCache介面的所有型別以及對應物件的統稱。由於是將快取物件直接置於記憶體之中,中間並不涉及持久化儲存的問題,自然也就無需考慮針對快取物件的序列化問題,所以這種記憶體模式支援任意型別的快取物件。

針對快取的操作不外乎對快取資料的存與取,這兩個基本的操作都由上面介紹的這個MemoryCache物件來完成。如果我們在一個ASP.NET Core應用對MemoryCache服務在啟動時做了註冊,我們就可以在任何地方獲取該服務物件設定和獲取快取資料,所以針對快取的程式設計是非常簡單的。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {        
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:
             .ConfigureServices(svcs => svcs.AddMemoryCache())
   8:             .Configure(app => app.Run(async context =>
   9:                 {
  10:                     IMemoryCache cache = context.RequestServices.GetRequiredService<IMemoryCache>();
  11:                     DateTime currentTime;
  12:                     if (!cache.TryGetValue<DateTime>("CurrentTime", out currentTime))
  13:                     {
  14:                         cache.Set("CurrentTime", currentTime = DateTime.Now);
  15:                     }
  16:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  17:                 }))
  18:             .Build()
  19:             .Run();        
  20:     }
  21: }

在上面這個演示程式中,我們在WebHostBuilder的ConfigureServices方法中通過呼叫ServiceCollection的擴充套件方法AddMemoryCache完成了針對MemoryCache的服務註冊。在WebHostBuilder的Configure方法中,我們通過呼叫ApplicationBuilder的Run方法註冊了一箇中間件對請求做了簡單的響應。我們先從當前HttpContext中得到對應的ServiceProvider,並利用後者得到MemoryCache物件。我們接下來呼叫MemoryCache的Set方法將當前時間快取起來(如果尚未快取),並指定一個唯一的Key(“CurrentTime”)。通過指定響應的Key,我們可以呼叫另一個名為TryGetValue<T>的方法獲取快取的物件。我們最終寫入的響應內容實際上是快取的時候和當前實施的時間。由於快取的是當前時間,所以當我們通過瀏覽器訪問該應用的時候,顯示的時間在快取過期之前總是不變的

1

雖然基於記憶體的快取具有最高的效能,但是由於它實際上是將快取資料存在承載ASP.NET Core應用的Web服務上,對於部署在叢集式伺服器中的應用會出現快取資料不一致的情況。對於這種部署場景,我們需要將資料快取在某一個獨立的儲存中心,以便讓所有的Web伺服器共享同一份快取資料,我們將這種快取形式稱為“分散式快取”。ASP.NET Core為分散式快取提供了兩種原生的儲存形式,一種是基於NoSQL的Redis資料庫,另一種則是微軟自家關係型資料庫SQL Server。

二、基於Redis的分散式快取

Redis數目前較為流行NoSQL資料庫,很多的程式設計平臺都將它作為分散式快取的首選,接下來我們來演示如何在一個ASP.NET Core應用中如何採用基於Redis的分散式快取。考慮到一些人可能還沒有體驗過Redis,所以我們先來簡單介紹一下如何安裝Redis。Redis最簡單的安裝方式就是採用Chocolatey(https://chocolatey.org/) 命令列,後者是Windows平臺下一款優秀的軟體包管理工具(類似於NPM)。

   1: PowerShell prompt :
   2: iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
   3:  
   4: CMD.exe:
   5: @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

我們既可以採用PowerShell (要求版本在V3以上)命令列或者普通CMD.exe命令列來安裝Chocolatey ,具體的命令如上所示。在確保Chocolatey 被本地正常安裝情況下,我們可以執行執行如下的命令安裝或者升級64位的Redis。

   1: C:\>choco install redis-64
   2: C:\>choco upgrade redis-64

Redis伺服器的啟動也很簡單,我們只需要以命令列的形式執行redis-server命令即可。如果在執行該命名之後看到如下圖所示的輸出,則表示本地的Redis伺服器被正常啟動,輸出的結果會指定伺服器採用的網路監聽埠。

2

接下來我們會對上面演示的例項進行簡單的修改,將基於記憶體的本地快取切換到針對Redis資料庫的分散式快取。針對Redis的分散式快取實現在NuGet包“Microsoft.Extensions.Caching.Redis”之中,所以我們需要確保該NuGet包被正常安裝。不論採用Redis、SQL Server還是其他的分散式儲存方式,針對分散式快取的操作都實現在DistributedCache這個服務物件向,該服務對應的介面為IDistributedCache。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddDistributedRedisCache(options =>
   8:                 {
   9:                     options.Configuration    = "localhost";
  10:                     options.InstanceName     = "Demo";
  11:                 }))
  12:             .Configure(app => app.Run(async context =>
  13:                 {
  14:                     var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
  15:                     string currentTime = await cache.GetStringAsync("CurrentTime");
  16:                     if (null == currentTime)
  17:                     {
  18:                         currentTime = DateTime.Now.ToString();
  19:                         await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
  20:                     }
  21:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  22:                 }))
  23:             .Build()
  24:             .Run();
  25:     }
  26: }

從上面的程式碼片段可以看出,針對分散式快取和記憶體快取在總體程式設計模式上是一致的,我們需要先註冊針對DistributedCache的服務註冊,但是利用依賴注入機制提供該服務物件來進行快取資料的設定和快取。我們呼叫IServiceCollection的另一個擴充套件方法AddDistributedRedisCache註冊DistributedCache服務,在呼叫這個方法的時候藉助於RedisCacheOptions這個物件的Configuration和InstanceName屬性設定Redis資料庫的伺服器和例項名稱。由於採用的是本地的Redis伺服器,所以我們將前者設定為“localhost”。其實Redis資料庫並沒有所為的例項的概念,RedisCacheOptions的InstanceName屬性的目的在於當多個應用共享同一個Redis資料庫的時候,快取資料可以利用它來區分,當快取資料被儲存到Redis資料庫中的時候,對應的Key會以它為字首。修改後的應用啟動後(確保Redis伺服器被正常啟動),如果我們利用瀏覽器來訪問它,依然會得到與前面類似的輸出。

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

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

3

三、基於SQL Server的分散式快取

除了使用Redis這種主流的NoSQL資料庫來支援分散式快取,微軟在設計分散式快取時也沒有忘記自家的關係型資料庫採用SQL Server。針對SQL Server的分散式快取實現在“Microsoft.Extensions.Caching.SqlServer”這個NuGet包中,我們先得確保該NuGet包被正常裝到演示的應用中。

所謂的針對SQL Server的分散式快取,實際上就是將標識快取資料的位元組陣列存放在SQL Server資料庫中某個具有固定結構的資料表中,因為我們得先來建立這麼一個快取表,該表可以藉助一個名為sql-cache 的工具來建立。在執行sql-cache 工具建立快取表之前,我們需要在project.json檔案中按照如下的形式為這個工具新增相應的NuGet包“Microsoft.Extensions.Caching.SqlConfig.Tools”。

   1: {
   2:   …
   3:   "tools": {
   4:     "Microsoft.Extensions.Caching.SqlConfig.Tools": "1.1.0-preview4-final"
   5:   }
   6: }

當針對上述這個NuGet包復原(Restore)之後,我們可以執行“dotnet sql-cache create”命令來建立,至於這個執行這個命令應該指定怎樣的引數,我們可以按照如下的形式通過執行“dotnet sql-cache create --help”命令來檢視。從下圖可以看出,該命名需要指定三個引數,它們分別表示快取資料庫的連結字串、快取表的Schema和名稱。

4

接下來我們只需要在演示應用所在的專案根目錄(project.json檔案所在的目錄)下執行dotnet sql-cache create就可以在指定的資料庫建立快取表了。對於我們演示的例項來說,我們按照下圖所示的方式執行這dotnet sql-cache create命令列在本機一個名為demodb的資料庫中建立了一個名為AspnetCache的快取表,該表採用dbo作為Schema。

5

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

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddDistributedSqlServerCache(options =>
   8:             {
   9:                 options.ConnectionString   = "server=.;database=demodb;uid=sa;pwd=password";
  10:                 options.SchemaName         = "dbo";
  11:                 options.TableName          = "AspnetCache";
  12:             }))
  13:             .Configure(app => app.Run(async context =>
  14:                 {
  15:                     var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
  16:                     string currentTime = await cache.GetStringAsync("CurrentTime");
  17:                     if (null == currentTime)
  18:                     {
  19:                         currentTime = DateTime.Now.ToString();
  20:                         await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
  21:                     }
  22:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  23:                 }))
  24:             .Build()
  25:             .Run();
  26:     }
  27: }

如果想看看最終存入SQL Server資料庫中的究竟包含哪些快取資料,我們只需要直接在所在資料庫中檢視對應的快取表了。對於演示例項快取的資料,它會以下圖所示的形式儲存在我們建立的快取表(AspnetCache)中,與基於Redis的快取類似,與指定快取資料的值一併儲存的還包括快取的過期資訊。

6

四、快取整個HTTP響應

上面演示的兩種快取都要求我們利用註冊的服務物件以手工的方式儲存和提取具體的快取資料,而接下來我們演示的快取則不再基於某個具體的快取資料,而是將服務端最終生成的響應主體內容予以快取,我們將這種快取形式稱為響應快取(Response Caching)。標準的HTTP規範,不論是HTTP 1.0+還是HTTP 1.1,都會快取做了詳細的規定,這是響應規範的理論機制和指導思想。我們將在後續內容中詳細介紹HTTP快取,在這之前我們先通過一個簡單的例項來演示一下整個響應內容是如何藉助一個名為ResponseCachingMiddleware中介軟體被快取起來的。該中介軟體由“Microsoft.AspNetCore.ResponseCaching”這個NuGet包提供。

通過同樣是採用基於時間的快取場景,為此我們編寫了如下這個簡單的程式。我們在WebHostBuilder的ConfigureServices方法中呼叫了IServiceCollection介面的擴充套件方法AddResponseCaching註冊了中介軟體ResponseCachingMiddleware依賴的所有的服務,而這個中介軟體的註冊則通過呼叫IApplicationBuilder介面的擴充套件方法UseResponseCaching完成。

   1: public class Program
   2: {
   3:     public static void Main()
            
           

相關推薦

ASP.NET Core快取[1]如何在一個ASP.NET Core應用使用快取

.NET Core針對快取提供了很好的支援 ,我們不僅可以選擇將資料快取在應用程序自身的記憶體中,還可以採用分散式的形式將快取資料儲存在一個“中心資料庫”中。對於分散式快取,.NET Core提供了針對Redis和SQL Server的原生支援。除了這個獨立的快取系統之外,ASP.NET Core還藉助一箇中

ASP.NET Core 入門教程 1、使用ASP.NET Core 構建第一個Web應用

一、前言 1、本文主要內容 Visual Studio Code 開發環境配置 使用 ASP.NET Core 構建Web應用 ASP.NET Core Web 應用啟動類說明 ASP.NET Core Web 專案結構說明 2、本教程環境資訊 軟體/環境說明 作業系統 Windows 10

初學html,任務1一個簡單html頁面,要求內容頁面裝一篇文章 用html來分段

enter pos 工程師 分享圖片 visit 技術 運行 並且 center 這是主要內容部分,用html實現版塊分布。 接下來是樣式部分。 讓頁面所有元素的padding和margin都設置為0 ; 否則加入一張大的覆蓋的背景圖片後,會由於瀏覽器的緣故,圖片周邊有

Python經典練習題1一個整數,它加上100後是一個完全平方數,再加上168又是一個完全平方數,請問該數是多少?

span range pytho 能夠 break clas 完全平方數 imp 經典 Python經典練習題 網上能夠搜得到的答案為: for i in range(1,85): if 168 % i == 0: j = 168 / i;

個人項目1一個能生成小學二年級四則運算題目的“軟件”

交換 signed null width wid 自然數 程序 src 生成 做這個程序,用了差不多半個小時。從開始的思路到語句的實踐,之間也出現了一些語法的錯誤,最後也得到了修正。由於學習的語言種類有限(c#剛剛開始學,大一時學習過c語言),在這裏選擇用C語言編寫這個程序

建議 1不要在常量和變數出現易混淆的字母

在捧讀《編寫高質量程式碼改善Java程式的151個建議》的過程中,也一一印證這自己所遇到的問題,有所感、有所悟,所以借簡書這樣一個平臺希望和大家進行分享。 包名全小寫,類名首字母全大寫,常量全部大寫並用下劃線分割,變數採用駝峰命名法(Camel Case)命名等,這些都是最基本的Java編碼規範

推薦一個很好用的vscode外掛一個可以給出vuexstore定義資訊的vscode外掛

VueThis$Store 想要解決的問題 在使用Vuex管理自己應用的狀態時,因為狀態過多,為了正確性每次都要開啟vuex定義檔案,去複製定義時的函式名或者狀態名,無形中就浪費了許多時間,為了解決這個痛點,開發了這個vscode外掛。 通過使用 ast 和正則表示式,獲取 store 中所有檔案的定義

易學筆記-RabbitMQ教程1一個生產者和一個消費者

易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/

7、ASP.NET MVC入門到精通——第一個ASP.NET MVC程式

開發流程 新建Controller 建立Action 根據Action建立View 在Action獲取資料並生產ActionResult傳遞給View。 View是顯示資料的模板 Url請求→Controller.Action處理→View響應 url請求→Controller.Ac

Asp.NetCore原始碼學習[2-1]配置[Configuration]

Asp.NetCore原始碼學習[2-1]:配置[Configuration] 在Asp. NetCore中,配置系統支援不同的配置源(檔案、環境變數等),雖然有多種的配置源,但是最終提供給系統使用的只有一個物件,那就是ConfigurationRoot。其內部維護了一個集合,用於儲存各種配置源的ICo

Asp.NetCore原始碼學習[2-1]日誌

Asp.NetCore原始碼學習[2-1]:日誌 在一個系統中,日誌是不可或缺的部分。對於.net而言有許多成熟的日誌框架,包括Log4Net、NLog、Serilog 等等。你可以在系統中直接使用這些第三方的日誌框架,也可以通過這些框架去適配ILoggerProvider 和 ILogger介面。適配

在c語言自定義了一個函式,在main呼叫時提示找不到識別符號

解決方案一: 把定義的函式放在,main函式之前。 void f() { printf("Hello"); } main() { f(); } 解決方案二: 在main函式之前宣告。 void f(); main() { f

Android 在一個應用如何啟動另外一個已安裝的應用

在自己的應用開發過程中,有可能需要在自己的應用中啟動其他應用,此時,如果應用對外公開了自己的啟動Intent方式,可以直接建立該Intent,然後通過startActivity(myIntent)。 這種方式相信都很熟悉。 如果不知道想要啟動的應用的Intent,也有

DAV入門之kivy一個簡單的kivy應用

這裡我們採用PyCharm作為開發環境,先介紹下鄙人做的一個示例的庫,取名叫做kivy_cracker,意味著我將一步一步的將kivy擊破(手動剪刀手),這個庫包含了後面我更新的內容以及官網的示例:https://gitee.com/davied9/kivy_cracker.

WebSphere配置的數據源在Web應用引用的寫法

padding websphere 技術分享 org info href adding title framework WebSphere中配置的數據源在Web應用中引用時名稱一定要和數據源的JNDI名稱保持一致,否則會出現無法找到數據源的錯誤。 引用WAS的數據源時只需要

leetcode-645vector.size()的返回型別以及其應用死迴圈或者資料型別衝突的問題

leetcode–645 該題是一道分治問題,求陣列中最大數的下標,然後把該陣列分成左右兩個子區間 class Solution { public: int max(int i, int j,vector<int>&nums){ int m

Android學習筆記 —— Android開發,不呼叫系統瀏覽器直接在應用顯示指定網址的內容

在開發過程中有一個在應用中直接顯示一個網址的內容,而不是呼叫系統瀏覽器顯示。根據網上大神的例子,終於實現了這一功能!現在把這個功能記錄下來,方便以後使用! 首先是xml檔案佈局,就一個簡單的WebView: activity_webview.xml: <?xml v

學習 ASP.NET Core 2.1集成測試使用 WebApplicationFactory

UNC enc sta 測試 修改 構造 creat -a msdn WebApplicationFactory 是 ASP.NET Core 2.1 新特性 MVC functional test infrastructure 中帶來的新東東,它封裝了 TestServe

Kubernetes初探[1]部署你的第一個ASP.NET Core應用到k8s叢集

Kubernetes簡介 Kubernetes是Google基於Borg開源的容器編排排程引擎,作為CNCF(Cloud Native Computing Foundation)最重要的元件之一,它的目標不僅僅是一個編排系統,而是提供一個規範,可以讓你來描述叢集的架構,定義服務的最終狀態,Kubernete

ASP.NET Core應用的錯誤處理[1]三種呈現錯誤頁面的方式

由於ASP.NET Core應用是一個同時處理多個請求的伺服器應用,所以在處理某個請求過程中丟擲的異常並不會導致整個應用的終止。出於安全方面的考量,為了避免敏感資訊的外洩,客戶端在預設的情況下並不會得到詳細的出錯資訊,這無疑會在開發環境下增加查錯糾錯的難度。對於生產環境來說,我們也希望終端使用者能夠根據具體的