.net5 core webapi進階之七:緩存的使用及HTTP緩存的工作原理


緩存可以提高應用程序的響應速度,本篇介紹如何在webapi中進行緩存。

如果將緩存的方式或介質做一個分類,可以分為如下3類:

一、本地內存(可存儲任何對象)

二、分布式存儲(需序列化成字節數組)

 2.1 基於NOSQL(如Redis數據庫)

 2.2 基於SQL(如SQL Server數據庫)

三、響應緩存(瀏覽器緩存數據)

 

一、 本地內存緩存(可存儲任何對象)

1. 還是以上篇中的項目 webapidemo3 來演示,使用內存進行緩存步驟如下:

第1步:安裝Microsoft.Extensions.Caching.Memory包。


第2步:在Startup類的ConfigureServices()方法中配置依賴注入服務(見紅色部分代碼)。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<EfDemoContext>(options => options.UseMySQL(Configuration.GetConnectionString("MySQL")));

            services.AddMemoryCache();

            services.AddControllers();
        }    

第3步:新建控制器 CachesController,引用 using Microsoft.Extensions.Caching.Memory;

並在構造函數中注入 IMemoryCache 實例。

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;

namespace webapidemo3 { [Route("api/[controller]")] [ApiController] public class CachesController : ControllerBase { private IMemoryCache _cache; public CachesController(IMemoryCache cache) { _cache = cache; }
//...... } }

第4步,編碼,新建終結點 Demo1( )用於緩存一個GUID、Demo2( )用於獲取這個GUID,代碼如下:

        [HttpGet]
        [Route("demo1")]
        public string Demo1()
        {
            _cache.Set("guid",Guid.NewGuid().ToString());

            return _cache.Get<string>("guid");
        }

        [HttpGet]
        [Route("demo2")]
        public string Demo2()
        {
            return _cache.Get<string>("guid");
        }

第5步,測試,編譯整個項目后運行,在瀏覽器訪問網址 http://localhost:51630/api/caches/demo1 得到如下結果:

在瀏覽器中開啟一個新tab,訪問網址 http://localhost:51630/api/caches/demo2,結果如下:

訪問終結點 Demo1()Demo2() 得到了相同的結果 。

 

2. 對內存緩存做更多細粒度的設置和操作。

如何設置緩存過期?緩存的過期策略可以是絕對時間(Absolute Time)或滑動時間(Sliding Time),

通過 MemoryCacheEntryOptions 來設置,然后作為 Set( )方法的參數傳遞就可以了, 如下:

        [HttpGet]
        [Route("demo1")]
        public string Demo1()
        {
            MemoryCacheEntryOptions options = new MemoryCacheEntryOptions() {
                //10分鍾內沒有訪問就過期
                //SlidingExpiration = TimeSpan.FromMinutes(10),

                //30分鍾后不管什么情況都過期
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(30)
            };
                
            _cache.Set("guid", Guid.NewGuid().ToString(), options); 

            return _cache.Get<string>("guid");
        }

 

二、基於Redis數據庫的分布式緩存。

Redis數據庫是一種非關系型數據庫(即 Not Only SQL,簡稱NoSQL),

它是以鍵值對(Key-Value)形式來存儲數據的,非常適合做分布式緩存,

.net core提供了對Redis的原生支持,使用起來和內存緩存非常相似,其配置步驟如下:

第1步:下載並安裝 Redis。

github下載地址 :https://github.com/MicrosoftArchive/redis/releases

下載 Redis-x64-3.2.100.zip 解壓縮后的文件清單如下,將文件夾copy到C:\下。

第2步:啟動Redis服務。

打開cmd.exe工具,將目錄切換到Redis文件夾,如下:

輸入命令redis-server.exe redis.windows.conf 可以看到 Redis 服務啟動成功的畫面:

第3步:客戶端連接。

不要關閉第2步中的cmd窗口(作Redis服務器使用),再開一個 cmd.exe窗口,

將目錄切換到redis文件夾目錄,然后使用redis-cli.exe命令連接redis服務器,如下:

redis-cli.exe -h 127.0.0.1 -p 6379,-h 接服務器IP , -p 接端口號。

可以看到已經成功連接了

第4步:使用Redis數據服務。

用set命令設置一個key-value,然后用 get命令取出:

Redis運行良好,至此Redis數據庫服務配置完成。

第5步:安裝 Microsoft.Extensions.Caching.Redis 包。

第6步:在Startup類的ConfigureServices()方法中配置依賴注入服務(見紅色部分代碼)。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<EfDemoContext>(options => options.UseMySQL(Configuration.GetConnectionString("MySQL")));
 
            services.AddDistributedRedisCache( options =>
                {
                    options.Configuration = "localhost"; //Redis數據庫連接信息

            //Redis數據庫實例名稱,如果設置了多個實例需要在這里指定
//options.InstanceName = "RedisCache";

});
services.AddControllers(); }

第7步:在控制器中注入實例。新建RedissController 控制器類,如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using System;

namespace webapidemo3
{
    [Route("api/[controller]")]
    [ApiController]
    public class RedissController : ControllerBase
    {
        private IDistributedCache _cache;
        public RedissController(IDistributedCache cache)
        {
            _cache = cache;
        }
    }
}

第8步:使用Redis分布式緩存。新建終結點Demo1( ),如下:

        [HttpGet]
        [Route("demo1")]
        public string Demo1()
        {
            _cache.Set("mykey", Encoding.UTF8.GetBytes("dotnet core webapi demo."));
return Encoding.UTF8.GetString(_cache.Get("mykey")); }

訪問網址 :http://localhost:51630/api/Rediss/demo1,結果如下:

 

三、響應緩存

響應緩存是指瀏覽器對Web服務器提供的HTTP響應報文進行緩存,.net core是完全按照標准的HTTP規范來操作緩存的,

(通過設置和緩存有關的HTTP頭),在使用前有必要先了解HTTP規范(HTTP/1.1)及HTTP規范中對緩存的描述。

1. HTTP定義

HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,
是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議,
它基於TCP/IP通信協議來傳遞數據(包括但不限於HTML 文件、 圖片、音頻、視頻、文檔、查詢結果等)。

2. HTTP請求/響應消息格式

如果我們訪問 www.baidu.com,用瀏覽器上的探測工具就可以看到請求和響應的報文信息,如下:

3. 響應緩存的約束條件

  • 在GET和HEAD請求時有效
  • 只發生在第一次請求之后

4. 緩存的工作過程:

瀏覽器第一次請求數據時,服務器會將緩存標識(Cache-Control、ETag、Last-Modify等)與數據一起返回,瀏覽器將二者備份起來。
再次請求數據時,客戶端先判斷備份中的緩存標識,決定是使用 "強制緩存" 還是 "協商緩存"

5.  "強制緩存" 和 "協商緩存" 區別:

    5.1 強制緩存:在緩存數據未失效的情況下會直接使用瀏覽器的緩存數據,不會再向服務器發送任何請求。
    5.2 協商緩存:不走強制緩存時瀏覽器就會與服務器進行協商,由服務器端對比判斷資源是否進行了修改更新。
                            如果服務器端的資源沒有修改,那么就返回304狀態碼,告訴瀏覽器可以使用緩存中的數據,
                            如果有更新就會返回200狀態碼,服務器就會返回更新后的數據並且將緩存規則一起返回。

6. 強制緩存的判斷標識

由響應header中的 Pragma、 Expires 、 Cache-Control 這三個字段來標識,取值如下:

7. 判斷緩存是否過期的規則。

用戶發起了一個http請求后,如果所請求的資源有緩存,就開始檢查緩存是否過期,步驟如下:

查看緩存是否有Cache-Control的max-age(s-maxage)指令,

若有,則使用響應報文生成時間Date + s-maxage/max-age獲得過期時間,再與當前時間進行對比;

若沒有,則比較Expires中的過期時間與當前時間(Expires是一個絕對時間)。

8. 協商緩存的使用條件。

當瀏覽器發現緩存過期后,緩存並不一定不能使用了,因為服務器端的資源可能仍然沒有改變,

所以需要與服務器協商,讓服務器判斷本地緩存是否還能使用。

相關的header頭屬性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since),請求頭和響應頭需成對出現 。

它們的取值如下:

9. 協商緩存判斷本地緩存是否可用的規則:

瀏覽器先判斷緩存中是否有 ETag 或 Last-Modified 字段,如果沒有,則發起一個http請求,服務器根據請求返回資源;

如果有這兩個字段,則在請求頭中添加If-None-Match字段(有ETag字段的話添加)、If-Modified-Since字段(有Last-Modified字段的話添加)。

如果同時發送If-None-Match 、If-Modified-Since字段,服務器只要比較If-None-Match和ETag的內容是否一致即可(因為優先級更高);

如果內容一致,服務器認為緩存仍然可用,則返回狀態碼304,瀏覽器直接讀取本地緩存,這就完成了協商緩存的過程。

如果內容不一致,則視情況返回其他狀態碼,並返回所請求資源。

 

四、響應緩存的使用。

.net core使用 ResponseCachingMiddleware中間件處理HTTP響應緩存,步驟如下:

第1步:安裝 NuGet 包 Microsoft.AspNetCore.ResponseCaching。

第2步:在 Startup 的 ConfigureServices(IServiceCollection services) 方法中注冊服務 。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<EfDemoContext>(options => options.UseMySQL(Configuration.GetConnectionString("MySQL")));

            services.AddResponseCaching();
  
            services.AddControllers();
        }

第3步:在 Startup 的 Configure( ) 方法中啟用響應緩存服務

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseResponseCaching();

            app.UseRouting();

            app.UseAuthorization();

            app.UseImageRequestMiddleware();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

第4步:新建控制器 ResponseCachesController,在終結點中使用響應緩存特性就可以了。

using Microsoft.AspNetCore.Mvc;
using System;

namespace webapidemo3
{
    [Route("api/[controller]")]
    [ApiController]
    public class ResponseCachesController : ControllerBase
    {
        
        [HttpGet]
        [Route("demo1")]
        [ResponseCache(Duration = 127)]
        public string Demo1()
        {
            //Duration = 127 對應 Cache-control: public, max-age=127
            return Guid.NewGuid().ToString();
        }
    }
}

第5步,編譯項目后用 POSTMAN 訪問網址 http://localhost:51630/api/ResponseCaches/demo1 ,結果如下:

在響應Header中增加了 Cache-Control 的 key ,其值是 public, max-age=127。

其他Demo:緩存設置2:

        [HttpGet]
        [Route("demo2")]
        [ResponseCache(Location = ResponseCacheLocation.Client, Duration = 35)] 
        public string Demo2()
        {
            //ResponseCache對應  Cache-Control: private, max-age=35
            return Guid.NewGuid().ToString();
        }

緩存設置3:

1         [HttpGet]
2         [Route("demo3")]
3         [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
4         public string Demo3()
5         {
6             //ResponseCache對應  Cache-Control: no-store,no-cache
7             return Guid.NewGuid().ToString();
8         }

緩存設置4:

        [HttpGet]
        [Route("demo4")]
        [ResponseCache(Duration = 735, VaryByQueryKeys = new string[] { "pageindex","pagesize" })]
        public string Demo4()
        {
            //根據查詢參數 pageindex,pagesize 緩存
            return Guid.NewGuid().ToString();
        }

最后,附上 ResponseCacheAttribute 的定義,如下:

    namespace Microsoft.AspNetCore.Mvc
    {       
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
        public class ResponseCacheAttribute : Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter
        {
            public ResponseCacheAttribute();
            public int Duration { get; set; }    
            public ResponseCacheLocation Location { get; set; } 
            public bool NoStore { get; set; }
            public string VaryByHeader { get; set; }      
            public string[] VaryByQueryKeys { get; set; }
            public string CacheProfileName { get; set; }
            public int Order { get; set; }
            public bool IsReusable { get; }

            public IFilterMetadata CreateInstance(IServiceProvider serviceProvider);
            public CacheProfile GetCacheProfile(MvcOptions options);
        }
    }

設置響應緩存特性的時候使用其中的屬性名稱就可以了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM