1、使用者端快取(瀏覽器快取)
HTTP有一套控制快取的協定-RFC7234,其中最重要的就是cache-control這個相應報文頭,伺服器返回時,如果Response帶上
cache-control:max-age=5 #表示允許瀏覽器快取5秒(僅是允許,瀏覽器是否快取還看瀏覽器本身機制是否要遵循這套快取協定)
Net 封裝好了一個快取特性,如:
public class HomeController : ControllerBase { [ResponseCache(Duration = 5)]//告訴瀏覽器可用快取5秒,Duration必須定義,不然請求會報錯 [HttpGet("GetNowDateTime")] public string GetNowDateTime() { return DateTime.Now.ToString(); } }
執行多次請求
第一行為首次請求,Response Headers:
cache-control: public,max-age=5 #告訴瀏覽器進行快取 content-encoding: gzip content-type: text/plain; charset=utf-8 date: Sun, 19 Mar 2023 07:17:50 GMT server: Microsoft-IIS/10.0 vary: Accept-Encoding x-powered-by: ASP.NET
後續5s內的請求,都會從快取中取,Size=disk cache即為快取,取到的返回值和首次請求一致,直到過了5s,再次向伺服器發起請求。
PS:勾選 Disable cache,發出的請求頭 Request Headers會加上
cache-control: no-cache #不從快取中取數
2、伺服器端快取
如果有大量使用者端存取伺服器獲取資料,僅依靠使用者端快取,還是會讓伺服器多次執行介面程式
伺服器端快取就是為了解決這個問題
.Net 新增伺服器快取中介軟體
//app.UseCors(); //跨域,如果有 app.UseResponseCaching();//啟動伺服器快取,位置介於這兩中介軟體之間 app.MapControllers();
UseResponseCaching中介軟體需要和ResponseCache特性配合使用
Api程式碼仍為
public class HomeController : ControllerBase { [ResponseCache(Duration = 5)]//告訴瀏覽器可用快取5秒 [HttpGet("GetNowDateTime")] public string GetNowDateTime() { return DateTime.Now.ToString(); } }
開啟兩個瀏覽器存取嘗試,功能實現!!!
不過,這種伺服器快取方式十分雞肋,存在的限制太多:
a)無法解決惡意請求給伺服器帶來的壓力(Request Header帶上了cache-control: no-cache,不僅瀏覽器不讀快取,伺服器也不讀) b)響應碼=200的Get或者Head的響應才會被快取 c)報文頭帶有Authorization、Set-Cookie等響應,不會快取
3、記憶體快取
記憶體快取需要自身在程式碼定義,僅針對業務層面的快取,不受請求頭影響
新增記憶體快取服務
builder.Services.AddScoped<IDBHelper, SqlServerHelper>(); //db注入 builder.Services.AddMemoryCache();//記憶體快取
模擬SqlServerHelper類
namespace DIDemo.Services { public record Staff(int Id, string acc); public class SqlServerHelper : IDBHelper { List<Staff> _staff_list = new List<Staff>(); public SqlServerHelper() { _staff_list.Add(new Staff(1,"tom")); _staff_list.Add(new Staff(2,"jerry")); }public Staff? GetStaff(int Id) { return this._staff_list.Find(x => x.Id == Id); } } }
定義一個查詢員工的介面
using DIDemo.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; namespace DIDemo.Controllers { [Route("api/[controller]")] [ApiController] public class HomeController : ControllerBase { private readonly IDBHelper _db; private readonly IMemoryCache _menCache; public HomeController(IDBHelper db, IMemoryCache menCache) { _db = db; _menCache = menCache; } [HttpGet("GetStaff")] public ActionResult<Staff> GetStaff(int Id) { Console.WriteLine("begin"); //1、從快取取資料 2、快取取不到,從資料庫取,新增快取 var items = _menCache.GetOrCreate<Staff>($"staff_{Id}", (e) => { Console.WriteLine("快取不存在,開始寫快取"); e.AbsoluteExpirationRelativeToNow= TimeSpan.FromSeconds(15); //15s後過期 e.SlidingExpiration = TimeSpan.FromSeconds(5); //5s滑動過期:5s記憶體取過快取,會重新開始計算5s return _db.GetStaff(Id); }); if (items == null) { return NotFound($"員工ID={Id},不存在"); } return items; } } }
GetOrCreate,如果獲取不到,就通過委託,查詢資料庫並寫入快取
AbsoluteExpirationRelativeToNow:固定的過期時間
SlidingExpiration:滑動過期時間
兩個時間可用單獨定義,也可以一起定義,其中一個過期即為過期,通常不單獨使用SlidingExpiration,可能造成快取無限續命
4、分散式快取
涉及微服務,負載均衡,需要一個集中管理的快取服務,也就是我們的分散式快取
老生常談的Redis,看看以前寫的文章吧!