.NET Core ResponseCache【快取篇(一)】

晨曦001發表於2020-07-16

一、前言

  原始碼

   1、最近一直在看專案效能優化方式,俗話說的好專案優化第一步那當然是新增快取,我們的專案之所以卡的和鬼一樣,要麼就是你的程式碼迴圈查詢資料庫(這個之前在我們的專案中經常出現,現在慢慢在修正)或者程式碼做了很多不該做的事情。這個時候就可以引入我們的快取了。(只要你的程式碼不是寫的特別差,比如之前實習的我)。

  2、快取主要分為兩種 客戶端(瀏覽器快取)、服務端快取。當我們的資料不需要及時返回的時候,可以考慮將頁面快取到客戶的瀏覽器中進行儲存,在一定的時間內訪問直接讀取瀏覽器快取的資訊。我們通過設定HTTP的響應頭 Cache-Control 來完成頁面儲存到瀏覽器快取中如下所示:

 二、客戶端(瀏覽器快取)

  1、在老的版本的MVC裡面,有一種可以快取檢視的特性(OutputCache),可以保持同一個引數的請求,在N段時間內,直接從mvc的快取中讀取,不去走檢視的邏輯。

//老版本的.NET 做法
[OutputCache(Duration =20)]//設定過期時間為20秒  
    public ActionResult ExampleCacheAction()  
    {  
        var  time=DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒");  
        ViewBag.time= time;  
        return View();  
    }  

  2、在.Net core 中就沒有(OutputCache)了,使用的是(ResponseCache)特性。官方文件上稱:響應快取可減少客戶端或代理對 web 伺服器的請求數。 響應快取還可減少量工作的 web 伺服器執行程式生成響應。 響應快取由標頭,指定你希望客戶端、 代理和快取響應的中介軟體如何控制。

 /*
         Duration 代表快取持續時間(秒)至少1秒
         VaryByHeader 設定vary 請求頭資訊使用vary頭有利於內容服務的動態多樣性。例如,使用Vary: User-Agent頭,快取伺服器需要通過UA判斷是否使用快取的頁面。
         Location 快取位置
                  None 報頭設定為“no-cache”不使用快取
                  Client 只快取在客戶端。設定“Cache-control”標題為“private”。
                  Any 快取在代理和客戶端。設定“Cache-control”標題為“public”。
        NoStore   快取中不得儲存任何關於客戶端請求和服務端響應的內容。每次由客戶端發起的請求都會下載完整的響應內容。如果設定為False Duration必須大於0
        VaryByQueryKeys 可以按照相同頁面,不同的引數進行相應的儲存
        CacheProfileName 設定快取配置檔案的值,可以通過設定不同的快取引數
         */
        [ResponseCache(Duration = 50, VaryByQueryKeys = new string[] { "q","name" })]
        public IActionResult Index(int q,string name)
        {
            return View(DateTime.Now);
        }

  3、通過執行我們可以看到,瀏覽器多了一個cache-control:public,max-age=50 它的意思是public快取在代理和客戶端。max-age=50代表快取的時間50秒。

  4、還有一種簡單粗暴的實現方式,因為我們知道新增了這個特性只是在響應請求頭中新增了一個cache-control:public,max-age=50,那麼我們可以也可以直接在請求響應中設定這個請求頭就完事了,效果都是一樣的。

public IActionResult Index()
{
    //直接一,簡單粗暴,不要拼寫錯了就好~~
    Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.CacheControl] = "public, max-age=600";
    
    //直接二,略微優雅點
    //Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
    //{
    //    Public = true,
    //    MaxAge = TimeSpan.FromSeconds(600)
    //};

    return View();
}

  5、有時候為了統一管理快取配置,我們可以將快取配置提前寫到配置中,使用名字進行呼叫。[ResponseCache(CacheProfileName ="test")],在Startup中注入檢視的時候寫入。

//設定一些快取策略
            services.AddControllersWithViews(options =>
            {
                options.CacheProfiles.Add("default", new CacheProfile
                {
                    Duration = 60
                });

                options.CacheProfiles.Add("test", new CacheProfile
                {
                    Duration = 30,
                    Location=ResponseCacheLocation.Client
                });
            });

  6、[ResponseCache] 引數

    •   Duration 設定快取的儲存時間(以秒為單位)。設定“Cache-control”中的“max-age”。
    •   Location
      •   Any 快取在代理和客戶端。設定“Cache-control”標題為“public”。
      •   Client 只快取在客戶端。設定“Cache-control”標題為“private”。
      •   None 每次有請求發出時,快取會將請求發到伺服器 ,伺服器端會驗證請求中所描述的快取是否過期,若未過期(注:實際就是返回304),則快取才使用本地快取副本。 報頭設定為“no-cache”。
    •   NoStore 快取中不得儲存任何關於客戶端請求和服務端響應的內容。每次由客戶端發起的請求都會下載完整的響應內容。
    •   VaryByHeader 使用vary頭有利於內容服務的動態多樣性。例如,使用Vary: User-Agent頭,快取伺服器需要通過UA判斷是否使用快取的頁面。
    •   VaryByQueryKeys 可以按照相同頁面,不同的引數進行相應的儲存
    •   CacheProfileName 設定快取配置檔案的值,可以通過設定不同的快取引數

三、服務端快取

  1、ResponseCache也可以設定服務端快取,將我們返回的資料儲存在服務端中在一定的時間內返回儲存的資料,這裡我先引入一個案例,有時候我們需要傳遞不同的引數進行快取。

     案例:當我們訪問的資料帶分頁引數的時候我們怎麼做呢?VaryByQueryKeys前面我們講了這個,可以根據不同的引數進行快取,那麼我們現在使用看看 

        結果:當我們執行的時候,發現報錯了,報錯的意思大致是說我們沒有使用中介軟體,但是為什麼我這個快取要使用到中介軟體呢?其實是因為要區分,我們請求的引數,然後會將我們的資料進行快取起來,就是實現了服務端快取。這裡的我們就要使用微軟提供的中介軟體了。

 2、我們主要是在Startup中注入services.AddResponseCaching();app.UseResponseCaching();中介軟體。服務端快取可以快取頁面資料和API資料,同時如果我們服務端存在資料,也就是快取命中的情況下,會直接從快取中取,不會再進入我們的方法。

 public void ConfigureServices(IServiceCollection services)
        {
              services.AddResponseCaching(options =>
            {
                options.UseCaseSensitivePaths = false;
                options.MaximumBodySize = 1024;
                options.SizeLimit = 100 * 1024*1024;
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseResponseCaching();
        }

服務端快取配置如下,當我們配置新增了中介軟體和注入快取之後,就可以使用VaryByQueryKeys了。當我們訪問一次之後就會將結果快取到我們的客戶端快取中,和服務端快取各一份。當我們使用同一個瀏覽器訪問的時候訪問的就是客戶端快取資訊,當我們切換瀏覽器訪問的時候也不會請求我們的方法,會先進入到我們的中介軟體中檢視是否存在服務端快取,如果存在就是直接拿快取進行返回,如果沒有就會請求方法返回,然後再將結果進行快取。

屬性

描述

MaximumBodySize

響應正文的最大可快取大小(以位元組為單位)。 預設值為 64 * 1024 * 1024 (64 MB)。

SizeLimit

響應快取中介軟體的大小限制(以位元組為單位)。 預設值為 100 * 1024 * 1024 (100 MB)。

UseCaseSensitivePaths

確定是否將響應快取在區分大小寫的路徑上。 預設值是 false。

 3、對於一些常年不變或比較少變的js,css等靜態檔案,也可以把它們快取起來,避免讓它們總是發起請求到伺服器,而且這些靜態檔案可以快取更長的時間!如果已經使用了CDN,這一小節的內容就可以暫且忽略掉了。。。對於靜態檔案,.NET Core有一個單獨的StaticFiles中介軟體,如果想要對它做一些處理,同樣需要在管道中進行註冊。UseStaticFiles有幾個過載方法,這裡用的是帶StaticFileOptions引數的那個方法。因為StaticFileOptions裡面有一個OnPrepareResponse可以讓我們修改響應頭,以達到HTTP快取的效果。

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        context.Context.Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
        { 
            Public = true,
            //for 1 year
            MaxAge = System.TimeSpan.FromDays(365)
        };
    }
});

四、使用前置條件

  • 請求必須導致伺服器響應,狀態程式碼為200(正常)。
  • 請求方法必須為 GET 或 HEAD。
  • 在 Startup.Configure中,響應快取中介軟體必須置於需要快取的中介軟體之前。
  • Authorization 標頭不得存在。
  • Cache-Control 標頭引數必須是有效的,並且響應必須標記為 “public” 且未標記為 “private”。
  • 如果 Cache-Control 標頭不存在,則 Pragma: no-cache 標頭不得存在,因為 Cache-Control 標頭在存在時將覆蓋 Pragma 標頭。
  • Set-Cookie 標頭不得存在。
  • Vary 標頭引數必須有效且不等於 *。
  • Content-Length 標頭值(如果已設定)必須與響應正文的大小匹配。
  • 不使用 IHttpSendFileFeature。
  • Expires 標頭和 max-age 和 s-maxage 快取指令指定的響應不能過時。
  • 響應緩衝必須成功。 響應的大小必須小於配置的或預設 SizeLimit。 響應的正文大小必須小於配置的或預設的 MaximumBodySize。
  • “請求” 或 “響應” 標頭欄位中不得存在 “no-store” 指令。

相關文章