.net core 2.x - 緩存的四種方式


其實這些微軟docs都有現成的,但是現在的人想對浮躁些,去看的不會太多,所以這里就再記錄下 ,大家一起懶一起浮躁,呵呵.

0.基礎知識

通過減少生成內容所需的工作,緩存可以顯著提高應用的性能和可伸縮性。 緩存對不經常更改的數據效果最佳。 緩存生成的數據副本的返回速度可以比從原始源返回更快。 在編寫並測試應用時,應避免依賴緩存的數據。ASP.NET Core 支持多種不同的緩存。 最簡單的緩存基於 IMemoryCache,它表示存儲在 Web 服務器內存中的緩存。 在包含多個服務器的服務器場上運行的應用應確保在使用內存中緩存時,會話是粘性的。 粘性會話可確保來自客戶端的后續請求都轉到同一台服務器。 例如,Azure Web 應用使用應用程序請求路由(ARR) 將所有的后續請求路由到同一台服務器。Web 場中的非粘性會話需要分布式緩存以避免緩存一致性問題。 對於某些應用,分布式的緩存可以支持更高版本向外縮放比內存中緩存。 使用分布式緩存可將緩存內存卸載到外部進程。內存中緩存可以存儲任何對象;分布式緩存接口僅限於byte[]

這里只說有這么幾種方式,但是不去深入.不喜拉到.

1.常見緩存響應的四種方式

1.1.內存緩存

  顧名思義,緩存在內存中,生命周期默認伴隨應用程序

1.2.響應緩存

  響應緩存可減少客戶端或代理到 web 服務器發出的請求數。 響應緩存還減少了工作的 web 服務器執行以生成響應。 響應緩存控制標頭,指定要如何客戶端、 代理和響應緩存中間件。

1.3.響應緩存中間件

  Microsoft.AspNetCore.ResponseCaching 包中的ResponseCaching 

1.4.分布式緩存

  就是你對象,她會霸道的和你說和別人說,你是他的....但是到最后到底誰會不是是誰的誰 誰也不知道,但是她有對你的使用權,逛街包要掛在你身上,取東西要從你身上的包里拿出來.

其中的1-3點都是對core本身開刀的手術,4點是不僅對自己動刀子,還要對別人家(服務)動刀子...嗯,比較壞.

1.1內存緩存

  內存緩存即我們常用的System.Runtime.Caching/MemoryCache.有關緩存方法的說明,請參閱IMemoryCache 方法CacheExtensions 方法 .

  對象    : IMemoryCache

  可選配置項: MemoryCacheEntryOptions

      • 設置絕對到期時間。 這是條目可以被緩存的最長時間,防止可調過期持續更新時該條目過時太多。
      • 設置可調過期時間。 訪問此緩存項的請求將重置可調過期時鍾。
      • 緩存優先級設置為CacheItemPriority.NeverRemove
      • 設置一個PostEvictionDelegate它將在條目從緩存中清除后調用。 在代碼中運行該回調的線程不同於從緩存中移除條目的線程。

  在core中我們使用內存緩存只需要注入 IMemoryCache 即可,然后就是一股腦地 get,getString, set....一頓操作猛如虎;具體案例見官方提供的參考.

1.2響應緩存

  a)  HTTP 1.1 緩存的緩存控制標頭的規范需要接受是有效的緩存Cache-Control客戶端發送的標頭。 客戶端可以發出請求的no-cache標頭值和強制服務器生成的每個請求的新響應。始終遵循客戶端Cache-Control請求標頭是有意義,如果您考慮 HTTP 緩存的目標。 在正式規范,緩存旨在減少的滿足請求的客戶端、 代理和服務器的網絡延遲和網絡開銷。 它不一定是一種方法來控制源服務器上的負載。沒有任何當前開發人員可以控制此緩存的行為使用時響應緩存中間件因為中間件遵循正式緩存規范。 未來的增強功能到中間件將允許配置中間件,若要忽略的請求Cache-Control標頭決定用於緩存的響應時。 使用中間件時,這將為您提供更好地控制負載在服務器上的機會。

  b) ResponseCacheAttribute指定緩存響應中設置相應的標頭所需的參數。ResponseCache特性可應用於操作 (方法) 和控制器 (類)。 方法級屬性重寫在類級別特性中指定的設置。

 1.3響應緩存中間件

  引用Microsoft.AspNetCore.App 元包或添加到的包引用Microsoft.AspNetCore.ResponseCaching包。

  配置:

  在Startup.ConfigureServices,將中間件添加到服務集合。

  services.AddResponseCaching();

  將應用配置為使用與中間件UseResponseCaching擴展方法,它將中間件添加到請求處理管道。 該示例應用將添加 Cache-Control 緩存最多 10 秒的可緩存響應的響應標頭。 該示例將發送 Vary 標頭用於配置中間件來提供緩存的響應才 Accept-Encoding 后續請求標頭匹配的原始請求。 中的代碼示例中, CacheControlHeaderValueHeaderNames需要using語句Microsoft.Net.Http.Headers命名空間。

  在startUp.cs的 Configure中

   app.UseResponseCaching();

   app.Use(async (context, next) => {

      // For GetTypedHeaders, add: using Microsoft.AspNetCore.Http;

    context.Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue() {Public = true, MaxAge = TimeSpan.FromSeconds(10) };

    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = new string[] { "Accept-Encoding" };

    await next();

    });

  app.UseMvc();

  選項:

  中間件提供了三個選項用於控制響應緩存。

選項 描述
UseCaseSensitivePaths 確定是否在區分大小寫的路徑上會緩存響應。 默認值為 false
MaximumBodySize 以字節為單位的響應正文最大緩存大小。 默認值是64 * 1024 * 1024(64 MB)。
大小限制 以字節為單位的響應緩存中間件大小限制。 默認值是100 * 1024 * 1024(100 MB)。

下面的示例配置到中間件:

    • 小於或等於 1024 字節的緩存響應。
    • 將響應存儲通過區分大小寫的路徑 (例如,/page1/Page1單獨存儲)。
  services.AddResponseCaching(options =>   {   options.UseCaseSensitivePaths = true;   options.MaximumBodySize = 1024;   }); 

VaryByQueryKeys:

在使用 MVC/Web API 控制器或 Razor 頁面頁模型ResponseCache屬性指定設置適當的標頭為響應緩存所需的參數。 唯一參數ResponseCache嚴格需要中間件的屬性是VaryByQueryKeys,這並不對應於實際的 HTTP 標頭。 有關詳細信息,請參閱ResponseCache 屬性

不使用時ResponseCache屬性中,響應緩存可以與變化VaryByQueryKeys功能。 使用ResponseCachingFeature直接從IFeatureCollectionHttpContext:

 var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();
if (responseCachingFeature != null){
   responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };   } 

使用單個值等於*VaryByQueryKeys隨緩存所有請求查詢參數而都變化。

   響應緩存中間件所使用的 HTTP 標頭

   用 HTTP 標頭配置中間件的響應緩存。

  

  

1.4分布式緩存  

  上面的不是什么重點,小項目用用玩不是問題,所以沒太大意思,重點是這里的分布式緩存實現.

  分布式的緩存是由多個應用程序服務器,通常作為對其進行訪問的應用程序服務器的外部服務維護共享緩存。 分布式的緩存可以提高性能和可伸縮性的 ASP.NET Core 應用,尤其是當應用程序托管的雲服務或服務器場。

  分布式的緩存具有幾大優勢,其中緩存的數據存儲在單個應用程序服務器其他緩存方案。

  當分布式緩存的數據,則數據:

  • 是連貫(一致) 跨多個服務器的請求。
  • 服務器重新啟動和應用部署仍然有效。
  • 不使用本地內存。

    分布式的緩存配置是特定於實現的。 本文介紹如何配置 SQL Server 和 Redis 分布式的緩存。 第三方實現也是可用,如NCache(GitHub 上的 NCache)。 無論選擇哪一種實現,該應用程序與使用緩存進行交互IDistributedCache接口。

  系統必備

  若要使用的 SQL Server 分布式緩存,引用Microsoft.AspNetCore.App 元包或添加到的包引用Microsoft.Extensions.Caching.SqlServer包。

  若要使用 Redis 分布式緩存,引用Microsoft.AspNetCore.App 元包並添加到的包引用Microsoft.Extensions.Caching.Redis包。 Redis 包不包括在Microsoft.AspNetCore.App包,因此您必須在項目文件中分別引用 Redis 包。

   IDistributedCache 接口

        IDistributedCache接口提供了以下方法操作的分布式的緩存實現中的項:

  • GetGetAsync –接受字符串鍵和檢索緩存的項作為byte[]數組如果在緩存中找到。
  • SetSetAsync –中添加項 (作為byte[]數組) 到使用字符串鍵的緩存。
  • RefreshRefreshAsync –刷新緩存基於其密鑰,重置其滑動到期超時值 (如果有) 中的項。
  • RemoveRemoveAsync –移除緩存項根據其字符串鍵值。

   建立分布式緩存服務

  注冊的實現IDistributedCacheStartup.ConfigureServices。 本主題中所述的框架提供實現包括:

  分布式的內存緩存

  分布式內存緩存 (AddDistributedMemoryCache) 是框架提供的實現IDistributedCache,在內存中存儲項。 分布式內存緩存不是實際的分布式的緩存。 緩存的項存儲在運行該應用程序的服務器上的應用程序實例。

  分布式內存緩存是一個有用的實現:

  • 在開發和測試方案。
  • 生產和內存消耗情況中使用一台服務器時不會產生問題。 實現分布式內存緩存摘要緩存數據存儲。 它允許實現真正的分布式緩存解決方案在將來如果多個節點或容錯能力變得非常必要。

  示例應用將在開發環境中運行應用時使用的分布式內存緩存:

public void ConfigureServices(IServiceCollection services) { if (_hostContext.IsDevelopment()){ services.AddDistributedMemoryCache();}    else    {       services.AddDistributedSqlServerCache(options =>       {         options.ConnectionString = _config["DistCache_ConnectionString"];         options.SchemaName = "dbo";         options.TableName = "TestCache";        });      }    } }

  1.4.1.分布式的 SQL 服務器緩存

  SQL Server 的分布式緩存實現 (AddDistributedSqlServerCache) 允許使用 SQL Server 數據庫作為其后備存儲分布式的緩存。 若要在 SQL Server 實例中創建的 SQL Server 緩存的項表中,可以使用sql-cache工具。 該工具使用名稱和指定的架構創建一個表。通過運行 SQL Server 中創建一個表sql-cache create命令。 提供 SQL Server 實例 (Data Source),數據庫 (Initial Catalog),架構 (例如, dbo),以及表名 (例如, TestCache):

  dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache 

  記錄一條消息以指示該工具已成功:

   Table and index were created successfully.

     創建的表sql-cache工具具有以下架構:

SqlServer 緩存表

 備注

  應用應操作使用的實例的緩存值IDistributedCache,而不SqlServerCache

  本示例應用實現SqlServerCache非開發環境中:

public void ConfigureServices(IServiceCollection services) { if (_hostContext.IsDevelopment()) { services.AddDistributedMemoryCache(); } else { services.AddDistributedSqlServerCache(options => { options.ConnectionString = _config["DistCache_ConnectionString"]; options.SchemaName = "dbo"; options.TableName = "TestCache"; }); }

 

 備注

  一個ConnectionString(和 (可選)SchemaNameTableName) 通常存儲在源代碼管理之外 (例如,通過存儲機密管理器中或在appsettings.json /appsettings。{Environment}.json文件)。 連接字符串可能包含應從源代碼管理系統的憑據。

 

  1.4.2.分布式的 Redis 緩存

       Redis是一種開源的內存中數據存儲,通常用作分布式緩存。 您可以使用 Redis 本地,並且您可以配置Azure Redis 緩存Azure 托管 ASP.NET Core 應用。 應用配置緩存實現使用RedisCache實例 (AddDistributedRedisCache):

services.AddDistributedRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "SampleInstance"; });

  若要在本地計算機上安裝 Redis:

  使用分布式的緩存

  若要使用IDistributedCache接口,請求的實例IDistributedCache從任何應用程序中的構造函數。 實例由提供依賴關系注入 (DI)當應用啟動時IDistributedCache注入到Startup.Configure。 使用緩存的當前時間IApplicationLifetime(有關詳細信息,請參閱Web 主機: IApplicationLifetime 接口):

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime, IDistributedCache cache) { lifetime.ApplicationStarted.Register(() => { var currentTimeUTC = DateTime.UtcNow.ToString(); byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC); var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(20)); cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options); });

 

  示例應用程序注入IDistributedCacheIndexModel以供索引頁。

  每次加載索引頁時,緩存時間檢查緩存OnGetAsync。 如果尚未過期的緩存的時間,將顯示時間。 如果自上一次訪問緩存的時間 (已加載此頁的最后一個時間) 已過去 20 秒,該頁將顯示緩存時間已過

  通過選擇,立即更新為當前時間的緩存的時間重置緩存時間按鈕。 按鈕觸發器OnPostResetCachedTime處理程序方法。

public class IndexModel : PageModel { private readonly IDistributedCache _cache; public IndexModel(IDistributedCache cache) { _cache = cache; } public string CachedTimeUTC { get; set; } public async Task OnGetAsync() { CachedTimeUTC = "Cached Time Expired"; var encodedCachedTimeUTC = await _cache.GetAsync("cachedTimeUTC"); if (encodedCachedTimeUTC != null) { CachedTimeUTC = Encoding.UTF8.GetString(encodedCachedTimeUTC); } } public async Task<IActionResult> OnPostResetCachedTime() { var currentTimeUTC = DateTime.UtcNow.ToString(); byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC); var options = new DistributedCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(20)); await _cache.SetAsync("cachedTimeUTC", encodedCurrentTimeUTC, options); return RedirectToPage(); } }

 

     備注

  無需為IDistributedCache實例使用 Singleton 或 Scoped 生命周期(至少對內置實現來說是這樣的)。

  此外可以創建IDistributedCache實例可能需要某一個而不是使用 DI,但在代碼中創建實例會使代碼難以測試和違反顯式依賴關系原則

  建議

  確定哪一種實現的時IDistributedCache最適合於您的應用程序,請考慮以下:

  • 現有的基礎結構
  • 性能要求
  • 成本
  • 團隊體驗

  緩存解決方案通常依賴於內存中存儲提供快速檢索的緩存數據,但內存是有限的資源,而且成本展開。 僅存儲通常用於緩存中的數據。通常情況下,Redis 緩存提供更高的吞吐量和延遲低於 SQL Server 緩存。 但是,進行基准測試時通常需要確定的緩存策略的性能特征。當 SQL Server 用作分布式的緩存后備存儲時,使用的同一個數據庫緩存與應用程序的普通數據存儲和檢索可以對這兩者的性能產生負面影響。 我們建議使用專用的 SQL Server 實例為分布式緩存后備存儲。

 

 2.IDistributedCache簡單封裝

/// <summary>
    /// <see cref="IDistributedCache"/>擴展方法
    /// </summary>
    public static class DistributedCacheExtensions
    {
        /// <summary>
        /// 將對象存入緩存中
        /// </summary>
        public static void SetCache(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
        {
            string json = JsonConvert.SerializeObject(value);
            if (options == null)
            {
                cache.SetString(key, json);
            }
            else
            {
                cache.SetString(key, json, options);
            }
        }

        /// <summary>
        /// 異步將對象存入緩存中
        /// </summary>
        public static async Task SetCacheAsync(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
        {
            string json = JsonConvert.SerializeObject(value);
            if (options == null)
            {
                await cache.SetStringAsync(key, json);
            }
            else
            {
                await cache.SetStringAsync(key, json, options);
            }
        }

        /// <summary>
        /// 將對象存入緩存中,使用指定時長
        /// </summary>
        public static void Set(this IDistributedCache cache, string key, object value, int cacheSeconds)
        {
            DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
            options.SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheSeconds));
            cache.SetCache(key, value, options);
        }

        /// <summary>
        /// 異步將對象存入緩存中,使用指定時長
        /// </summary>
        public static Task SetAsync(this IDistributedCache cache, string key, object value, int cacheSeconds)
        {
            DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
            options.SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheSeconds));
            return cache.SetCacheAsync(key, value, options);
        }

        /// <summary>
        /// 將對象存入緩存中,使用功能配置
        /// </summary>
        public static void Set(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions cacheOptions)
        {
            if (cacheOptions == null)
            {
                return;
            }
            cache.SetCache(key, value, cacheOptions);
        }

        /// <summary>
        /// 異步將對象存入緩存中,使用功能配置
        /// </summary>
        public static Task SetAsync(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions cacheOptions)
        {
            if (cacheOptions == null)
            {
                return Task.FromResult(0);
            }
            return cache.SetCacheAsync(key, value, cacheOptions);
        }

        /// <summary>
        /// 獲取指定鍵的緩存項
        /// </summary>
        public static TResult Get<TResult>(this IDistributedCache cache, string key)
        {
            string json = cache.GetString(key);
            if (json == null)
            {
                return default(TResult);
            }
            return JsonConvert.DeserializeObject<TResult>(json);
        }

        /// <summary>
        /// 異步獲取指定鍵的緩存項
        /// </summary>
        public static async Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key)
        {
            string json = await cache.GetStringAsync(key);
            if (json == null)
            {
                return default(TResult);
            }
            return JsonConvert.DeserializeObject<TResult>(json);
        }

        /// <summary>
        /// 獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static TResult GetCache<TResult>(this IDistributedCache cache, string key, Func<TResult> getFunc, DistributedCacheEntryOptions options = null)
        {
            TResult result = cache.Get<TResult>(key);
            if (!Equals(result, default(TResult)))
            {
                return result;
            }
            result = getFunc();
            if (Equals(result, default(TResult)))
            {
                return default(TResult);
            }
            cache.SetCache(key, result, options);
            return result;
        }

        /// <summary>
        /// 異步獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static async Task<TResult> GetCacheAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> getAsyncFunc, DistributedCacheEntryOptions options = null)
        {
            TResult result = await cache.GetAsync<TResult>(key);
            if (!Equals(result, default(TResult)))
            {
                return result;
            }
            result = await getAsyncFunc();
            if (Equals(result, default(TResult)))
            {
                return default(TResult);
            }
            await cache.SetCacheAsync(key, result, options);
            return result;
        }

        /// <summary>
        /// 獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static TResult Get<TResult>(this IDistributedCache cache, string key, Func<TResult> getFunc, int cacheSeconds)
        {
            DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
            options.SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheSeconds));
            return cache.GetCache<TResult>(key, getFunc, options);
        }

        /// <summary>
        /// 異步獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> getAsyncFunc, int cacheSeconds)
        {
            DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
            options.SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheSeconds));
            return cache.GetAsync<TResult>(key, getAsyncFunc, options);
        }

        /// <summary>
        /// 獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static TResult Get<TResult>(this IDistributedCache cache, string key, Func<TResult> getFunc, DistributedCacheEntryOptions cacheOptions)
        {
            if (cacheOptions == null)
            {
                return getFunc();
            }
            return cache.Get<TResult>(key, getFunc, cacheOptions);
        }

        /// <summary>
        /// 獲取指定鍵的緩存項,不存在則從指定委托獲取,並回存到緩存中再返回
        /// </summary>
        public static Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> getAsyncFunc, DistributedCacheEntryOptions cacheOptions)
        {
            if (cacheOptions == null)
            {
                return getAsyncFunc();
            }
            return cache.GetAsync<TResult>(key, getAsyncFunc, cacheOptions);
        }
    }
View Code

 

使用時候注入IDistributedCache即可,實現的效果和 內存緩存效果是一樣的,.....還沒看其實現源碼,所以,,吹不起來.

 這時候再上一篇中我們通過掃碼登錄之后獲取到token,保存到 idistributedCache對象即可

 

 


免責聲明!

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



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