一、緩存 緩存指在中間層中存儲數據的行為,該行為可使後續數據檢索更快。 從概念上講,緩存是一種性能優化策略和設計考慮因素。 緩存可以顯著提高應用性能,方法是提高不常更改(或檢索成本高)的數據的就緒性。 二、RFC9111 在最新的緩存控制規範文件RFC9111中,詳細描述了瀏覽器緩存和伺服器緩存控制 ...
一、緩存
緩存指在中間層中存儲數據的行為,該行為可使後續數據檢索更快。 從概念上講,緩存是一種性能優化策略和設計考慮因素。 緩存可以顯著提高應用性能,方法是提高不常更改(或檢索成本高)的數據的就緒性。
二、RFC9111
在最新的緩存控制規範文件RFC9111中,詳細描述了瀏覽器緩存和伺服器緩存控制的規範,其中有一個最重要的響應報文頭Cache-Control
。
該報文頭的設置會影響我們的緩存,包括瀏覽器端和服務端。
RFC911:https://www.rfc-editor.org/rfc/rfc9111#name-cache-control
三、網頁端緩存
在Cache-Control
中,如果設置max-age=10
,則表示告訴瀏覽器緩存10s,而為什麼瀏覽器要認這個表示呢,就是上面我們說的前後端都要根據RFC標準規範去實現,就是硬體的統一插口,不然其他生成出來的就用不了。
那麼在Asp.net Core 中只需要在介面上打上ResponseCacheAttribute
並設置max-age
的時間即可。
首先建一個Asp.Net Core WebAPI 項目,寫一個獲取學生的Get
介面。
namespace WebAPI_Cache.Controllers
{
[ApiController]
[Route("[controller]")]
public class CacheController : ControllerBase
{
public CacheController()
{
}
[HttpGet]
public ActionResult<Student> GetStudent()
{
return new Student()
{
Id = 1,
Name = "Test",
Age = Random.Shared.Next(0, 100),
};
}
}
}
namespace WebAPI_Cache.Model
{
public class Student
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
}
在介面中我返回Student
的age
為1-100的隨機數。啟動項目測試,短時間內兩次調用返回的age
不一樣
第一次age:
第二次age:
當我在介面方法打上[ResponseCache(Duration = 10)]
,再次調用介面返回的信息可以看到已經有了cache-control: public,max-age=10
的Header。
並且我在10秒內的請求,只有第一次請求過伺服器,其他都是從緩存中取的,查看edge瀏覽器網路訪問如下:
四、伺服器緩存
網頁端緩存是放在瀏覽器端的,對於單點請求會有用,但是如果是多個不同前端請求呢。這個時候我們可以將緩存放置在後端服務中,在ASP.NET Core 中配置響應緩存中間件。
在 Program.cs中,將響應緩存中間件服務 AddResponseCaching 添加到服務集合,並配置應用,如果使用 CORS 中間件時,必須在 UseResponseCaching 之前調用 UseCors。
如果header包含 Authorization,Set-Cookie 標頭,也不會緩存,因為這些用戶信息緩存會引起數據混亂。
然後對於我們需要伺服器緩存的介面打上ResponseCache
屬性,和設置瀏覽器緩存一樣,還有其他參數可設置。我們通過兩個進程來測試,一個用瀏覽器swagger,一個用postman,可以看到兩個請求的age都是等於18的。所以可以確定伺服器端確實存在緩存。
但是在用postman測試的時候記得在settings裡面把Send no-cache header
勾掉,如果不去掉,發送的時候就會在請求頭裡麵包含Cache-Control:no-cache
,這樣服務端即便有緩存也不會使用緩存。
對於瀏覽器端相當於禁用緩存,如果禁用了緩存,發送的請求頭也會帶上Cache-Control:no-cache
,服務端看到no-cache 後便不會再使用緩存進行響應。
而這個約定就是RFC9111的規範,所以這個後端緩存策略比較雞肋,如果用戶禁用緩存就沒用了,因此我們還可以使用記憶體緩存。
五、記憶體緩存
記憶體緩存基於 IMemoryCache。 IMemoryCache 表示存儲在 Web 伺服器記憶體中的緩存。
- 首先Nuget安裝包
Install-Package Microsoft.Extensions.Caching.Memory
- 在Program.cs中添加依賴
builder.Services.AddMemoryCache();
- 緩存數據
我添加一個Post方法模擬id查詢Student
這樣我就將數據緩存到了記憶體,可以設置緩存的絕對過期時間,也可以設置滑動過期,稍後我們會看到過期策略的使用。
六、緩存擊穿
緩存擊穿是指熱點key在某個時間點過期的時候,而恰好在這個時間點對這個Key有大量的併發請求過來,或者是查詢了不存在的數據,緩存裡面沒有,從而大量的請求打到資料庫上形成資料庫壓力。
上面記憶體緩存中的寫法我們可以看到,如果查詢緩存等於null
就會再去查詢數據(我這裡只是模擬,沒有去寫真的資料庫查詢),如果這樣暴力請求攻擊就會有問題。
對於這個問題我們可以使用ImemoryCache
的GetOrCreate
方法,當然它還有非同步方式。通過該方法傳入緩存的key和func 委托方法返回值來進行查詢並緩存,如果沒查詢到返回的null
也會存儲在緩存中,防止惡意查詢不存在的數據。
[HttpPost]
public ActionResult<Student> GetStudent2(int id)
{
//查詢並創建緩存
var student = _memoryCache.GetOrCreate("student_" + id, t =>
{
t.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(20);
//模擬只有id=1有數據
if (id == 1)
{
return new Student()
{
Id = 1,
Name = "Test",
Age = Random.Shared.Next(0, 100),
};
}
else
{
//其他的返回空,但是空值也會緩存,比如查詢 id=2,id=3 都會緩存
return null;
}
});
if (student == null)
{
return NotFound("未找到");
}
else
{
return student;
}
}
七、緩存雪崩
緩存雪崩是指緩存中數據大批量到過期時間,導致所有請求都會去查資料庫,而查詢數據量巨大,引起資料庫壓力過大甚至down機。
對於雪崩情況我們對緩存的策略主要是設置過期時間,部分不重要的站點,比如新聞網站我們將絕對過期時間AbsoluteExpiration設置的久一點。
對於要一定靈活性,能在請求不頻繁的時候進行失效以更新數據的,我們可以用滑動過期時間,就是如果頻繁請求就一值滑動過期時間。
當然為了避免滑動時間一直不過期,還可以兩種方式混合使用。上面的例子,我們設置絕對過期時間是20秒,我們將滑動過期設置5秒,在5秒內有持續訪問就一直續命,直到20秒絕對過期。
那麼如果沒人訪問,在5秒後就過期了,這樣數據下次訪問也能及時查詢最新數據。
八、分散式緩存
有了上面的緩存方案,對付一些小的簡單業務系統完全夠用了,但是如果你是分散式部署服務,那麼像記憶體緩存訪問的數據就是單個伺服器的緩存。
你可能需要多個伺服器的請求之間保持一致、在進行伺服器重啟和應用部署後仍然有效、不使用本地記憶體等情況。
這個時候我們可以使用第三方緩存,比如memecache,Redis等。Asp.Net Core 使用 IDistributedCache 介面與緩存進行交互。
- NuGet安裝包
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
- 在 Program.cs 中註冊 IDistributedCache 實現
Configuration: 為連接配置。
InstanceName: 為存儲鍵首碼。
編寫測試方法GetStuden3
IDistributedCache 接受字元串鍵並以 byte[] 數組的形式添加或檢索緩存項,所以數據是以byte[]形式訪問,但是擴展了一個string類型的方法可以進行使用,我這裡用字元串進行操作。
以上這些就是關於asp.net core 當中使用緩存的重要點和基礎使用方法,詳細參數和文檔可參看官方文檔:ASP.NET Core 中的緩存概述
作者:孫泉
出處:https://www.cnblogs.com/SunSpring/p/17848185.html
如果你喜歡文章歡迎點擊推薦,你的鼓勵對我很有用!
本文版權歸作者所有,轉載需在文章頁面明顯位置給出原文鏈接。