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

来源:http://www.cnblogs.com/artech/archive/2017/02/12/cache-4-asp-net-core-01.html
-Advertisement-
Play Games

.NET Core針對緩存提供了很好的支持 ,我們不僅可以選擇將數據緩存在應用進程自身的記憶體中,還可以採用分散式的形式將緩存數據存儲在一個“中心資料庫”中。對於分散式緩存,.NET Core提供了針對Redis和SQL Server的原生支持。除了這個獨立的緩存系統之外,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:     }
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 先上基礎,下圖是Linux的記憶體映射模型 1. 每一個進程都有自己的進程空間,進程空間的0 3G是用戶空間,3G 4G是內核空間 2. 每個進程的用戶空間不在同一個物理記憶體頁,但是所有的進程的內核空間對應同樣的物理地址 3. vmalloc分配的地址可以高端記憶體,也可以是低端記憶體 4. 0 896M ...
  • 內核中關於GPIO的操作API主要集中在和中,前者主要是GPIO直接與設備樹相關的操作,在 "Linux 設備樹操作API" 中已經記錄過,後者主要是針對GPIO本身的操作,常用的有下麵幾個。 ...
  • 1.前言 string是屬於引用類型的,這個大家都知道吧?但是平常在使用的過程中,發現它還是擁有一些值類型的特征的,這到底是為什麼呢? 原因就是.Net考慮到假如大量的操作string對象的時候,大量對引用對象進行操作的時候,性能肯定不如值類型來的爽快。.Net為了提高這個性能,提供了一個專門的解決 ...
  • 一、水晶報表簡介 Crystal Reports(水晶報表)是一款商務智能(BI)軟體,主要用於設計及產生報表。水晶報表是業內最專業、功能最強的報表系統,它除了強大的報表功能外。最大的優勢是實現了與絕大多數流行開發工具的集成和介面。在VS.Net平臺做過報表開發的程式員,一定都對水晶報表強大、高效、 ...
  • OSS.Http項目對於.Net Standard標準庫的支持已經遷移完畢,OSS開源系列兩個最底層的類庫已經具備跨運行時支持的能力。由於OSS.Http類庫是幾年前我參照RestSharp的思路,完成的一個輕量型Http請求框架。因為時間較久底層使用的還是HttpWebRequest,這次基本上是 ...
  • 對象持久化是指將記憶體中的對象保存到可永久保存的存儲設備中(如磁碟)的一種技術。 本文介紹的是除資料庫之外的幾種對象持久化方式。 具體如下: 涉及知識點: 如下圖所示【主要功能是將用戶輸入的信息保存成各種格式,並從各個文檔中進行讀取出來】: 保存和讀取文本文檔代碼如下: 1 /// <summary> ...
  • 有很多網站讀者能換自己喜歡的樣式,還有一些網站想多站點共用後端代碼而只動前段樣式,可以採用動態替換CSS樣式和JS。 如果是webform 開發,可以用下列方法: 流程是首先從數據中或者xml讀取數據,然後賦值給前端頁面 HTML <meta> 標簽添加 HtmlMeta mtdes = new H ...
  • 上面是建立的一個類,因時間不多,命名比較隨意。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...