.NET Core 的緩存篇之MemoryCache

前言

對於緩存咱們都已經很熟悉了,緩存分爲不少種,瀏覽器緩存、試圖緩存、服務器緩存、數據庫緩存等等一些,那今天咱們先介紹一下視圖緩存和MemoryCache內存緩存的概念和用法:html

視圖緩存

在老的版本的MVC裏面,有一種能夠緩存視圖的特性(OutputCache),能夠保持同一個參數的請求,在N段時間內,直接從mvc的緩存中讀取,不去走視圖的邏輯。git

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

在Asp.Net core 2.1中,官方文檔上稱:響應緩存可減小客戶端或代理對 web 服務器的請求數。 響應緩存還可減小量工做的 web 服務器執行程序生成響應。 響應緩存由標頭,指定你但願客戶端、 代理和緩存響應的中間件如何控制。github

在Asp.Net Core 2.1 中,沒有了OutputCache,換成了ResponseCache,ResponseCache必須帶一個參數:Duration 單位爲秒,最少設置一秒鐘web

//.NET Core2.1作法
[ResponseCache(Duration = 5)]
        public IActionResult About()
        {

            ViewBag.time = DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒");

            return View();
        }

而後再瀏覽器請求這個視圖數據庫

在瀏覽器的響應頭的Cache-Control 中出現max-age=5, Http協議對此的解釋是瀏覽器

客戶端將不會接受其保留時間大於指定的秒數的響應。 示例: max-age=60 (60 秒), max-age=2592000 (1 個月)緩存

若是在瀏覽器中禁用緩存,那麼ResponseCache不會有任何效果服務器

Vary過濾mvc

 

[ResponseCache(VaryByHeader = "User-Agent", Duration = 5)]
        public IActionResult About()
        {

            ViewBag.time = DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒");

            return View();
        }

關於vary在Http響應頭的做用就是:告訴緩存服務器或者CDN,我仍是同一個瀏覽器的請求,你給我緩存就好了,若是你換個瀏覽器去請求,那麼vary的值確定爲空,那麼緩存服務器就會認爲你是一個新的請求,就會去讀取最新的數據給瀏覽器函數

參考資料:http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

 禁用緩存(NoStore 和 Location.None)

在Http中 :no-store,請求和響應的信息都不該該被存儲在對方的磁盤系統中

 

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult About()
        {

            ViewBag.time = DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒");

            return View();
        }

 

ResponseCacheLocation.None是在Cache-Control設置一個no-cache屬性,讓瀏覽器不緩存當前這個URL

緩存配置(CacheProfiles)

 

在一個正常的項目中,確定有不少個控制器,可是不可能每一個控制器的緩存策略都同樣,這時候,咱們就須要一個緩存的配置來靈活應對這個問題
在mvc的服務注入的時候,咱們能夠在option裏面注入進咱們的緩存策略
services.AddMvc(option=> {
                option.CacheProfiles.Add("test1", new CacheProfile()
                {
                    Duration = 5
                });
                option.CacheProfiles.Add("test2", new CacheProfile()
                {
                    Location = ResponseCacheLocation.None,
                    NoStore = true
                });
            });

而後咱們在使用的時候,直接使用配置策略的名稱就行了

[ResponseCache(CacheProfileName = "test1")]
        public IActionResult About()
        {

            ViewBag.time = DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒");

            return View();
        }

這樣咱們就能和以前在特性後邊配置同樣了,這是視圖緩存,下面咱們就來看看MemoryCache 是個什麼東東

MemoryCache

若是回到老版本的.NET,說到內存緩存你們可能立馬想到了HttpRuntime.Cache,它位於System.Web命名空間下,可是在ASP.NET Core中System.Web已經不復存在。今兒個就簡單的聊聊如何在ASP.NET Core中使用內存緩存

有幾個問題咱們須要先進行了解:

1.何時須要用到緩存?

通常將常常訪問可是又不是常常改變的數據放進緩存是再好不過了,這樣能夠明顯提升應用程序的性能。

2.緩存的好處?

建議http://www.baidu.com

 

使用

不一樣於 ASP.NET Web 窗體和 ASP.NET MVC,ASP.NET Core 沒有內置的 Cache 對象,能夠拿來在控制器裏面直接使用。 這裏,內存緩存時經過依賴注入來啓用的,所以第一步就是在 Startup 類中註冊內存緩存的服務。如此,就得打開 Startup 類而後定位到 ConfigureServices() 方法,像下面這樣修改 ConfigureServices() 方法:

①首先須要在ConfigureServices中註冊緩存服務

 services.AddMemoryCache()

爲了向你的應用程序加入內存緩存能力,你須要在服務集合上調用 AddMemoryCache() 方法。採用這種辦法就可讓一個內存緩存(它是一個 IMemoryCache 對象)的默認實現能夠被注入到控制器中去。

②在下面的代碼中從Home控制器的構造函中獲取IMemoryCache實例(內存緩存使用依賴注入來注入緩存對象)

private readonly IMemoryCache _memoryCache;
public HomeController(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

如你所見,上述代碼聲明瞭一個 ImemoryCache 的私有變量。該變量會被構造器中被賦值。構造器會經過 DI(依賴注入)接收到緩存參數,而後被存儲在本地變量總,提供後續使用。

③關於緩存的使用經常使用的就是Set Get Remove,通常有如下幾種作法能夠參考:

⑴可使用 Set() 方法來在緩存中存東西

等你有了這個 IMemoryCache 對象,就能夠讀取或者向它寫入數據了。向緩存寫入數據項是至關直接的

public IActionResult Index()
{
  _memoryCache.Set<string>("timestamp", DateTime.Now.ToString());
  return View();
}

上述代碼在 Index() 這個 action 中設置了一個緩存項。這是經過使用 IMemoryCache 的 Set<T>() 來完成的。Set() 方法的第一個參數是鍵名,用來標識該數據項。第二個參數是鍵的取值。在此例中,咱們存儲一個字符串的鍵和一個字符串的值,而你也能夠存儲其它類型 (原生以及自定義的類型) 的鍵值對。

⑵可使用 Get 方法來從緩存中獲取到一個數據項

等你向緩存中添加好了數據,也許會想要在應用程序的其它地方去獲取到該數據,能夠用 Get() 來作到。以下代碼會告訴你如何來作這件事情。

public IActionResult Show()
{
  string timestamp = _memoryCache.Get<string>("timestamp");
  return View("Show",timestamp);
}

上述代碼從 HomeController 的另一個action(Show)那裏獲取到了一個緩存的數據項。Get() 方法會指定數據項的類型以及它的鍵名。若是該數據項存在的話,就會被返回而且被賦值給 timestamp 這個字符串變量。而後這個 timestamp 的值就會被傳遞給 Show 視圖。

Show 視圖只是簡單地輸出了 timestamp 的值,以下所示:

<h1>TimeStamp : @Model</h1>
<h2>@Html.ActionLink("Go back", "Index", "Home")</h2>

 

若是你觀察前面的示例,會發現每次你導航至 /Home/Index 的時候, 都會有一個新的 timestamp 被賦值給了緩存項。這是由於咱們並無對此進行檢查,規定只有在數據項不存在的時候才賦值。許多時候你都會想要這樣作的。這裏有兩種辦法能夠在 Index() 這個 action 裏面來作這樣的檢查。咱們把兩種辦法都在下面列了出來

可使用 TryGet() 來檢查緩存中是否存在特定的鍵值

//first way
if (string.IsNullOrEmpty
(_memoryCache.Get<string>("timestamp")))
{
  _memoryCache.Set<string>("timestamp", DateTime.Now.ToString());
}

//second way
if (!_memoryCache.TryGetValue<string>
("timestamp", out string timestamp))
{
    _memoryCache.Set<string>("timestamp", DateTime.Now.ToString());
}

第一種辦法使用了你早先用過的同一個 Get() 方法,這一次它被拿來跟 if 塊一塊兒用。若是 Get() 不能在緩存中找到指定的數據項,IsNullOrEmpty() 就會返回 true。而只有這時候 Set() 纔會被調用,一次來添加數據項。

第二種辦法更加優雅一點。它使用 TryGet() 方法來獲取一個數據項。TryGet() 方法會返回一個布爾值來指明數據項有沒有被找到。實際的數據項可使用一個輸出參數拉取出來。若是 TryGet() 返回false,Set() 就會被用來添加數據。

 

 若是不存在的話,可使用 GetOrCreate() 來添加一項

 有時你須要從緩存中檢索現有項。若是該項目不存在,則但願添加該項。這兩個任務 - 若是它存在獲取值,不然建立之 - 可使用 GetOrCreate() 方法來實現。修改後的 Show() 方法展現瞭如何實現的

public IActionResult Show()
{
  string timestamp = cache.GetOrCreate<string>
  ("timestamp", entry => { 
return DateTime.Now.ToString(); });
  return View("Show",timestamp);
}

Show() 動做如今使用 GetOrCreate() 方法。 GetOrCreate() 方法將檢查時間戳的鍵值是否存在。若是是,現有值將被賦值給局部變量。不然,將根據第二個參數中指定的邏輯建立一個新條目並將其添加到緩存中。

在緩存的數據項上面設置絕對和滾動的過時時間

一個緩存項只要被添加到緩存就會一直存儲,除非它被明確地使用 Remove() 從緩存中移除。你也能夠在一個緩存項上面設置一個絕對和滾動的過時時間。一個絕對的過時設置意味着該緩存項會在嚴格指定的日期和時間點被移除,而滾動過時設置則意味着它在給定的一段時間量處於空閒狀態(也就是沒人去訪問)以後被移除。

爲了能在一個緩存項上面設置這兩種過時策略,你要用到 MemoryCacheEntryOptions 對象。以下代碼向你展現瞭如何去使用。

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration = DateTime.Now.AddMinutes(1);
options.SlidingExpiration = TimeSpan.FromMinutes(1);
_memoryCache.Set<string>("timestamp", DateTime.Now.ToString(), options);

上述代碼來自於修改過的 Index() action,它建立了一個 MemoryCacheEntryOptions 的對象,而後將它的 AbsoluteExpiration 屬性設置爲今後刻到一分鐘以後的一個 DateTime 值,它還將 SlidingExpiration 屬性設置爲一分鐘。這些值都指定了該緩存項會在一分鐘以後從緩存移除,無論其是否會被訪問。此外,若是該緩存項如初持續空閒了有一分鐘,它也會被從緩存中移除。

等你將 AbsoluteExpiration 和 SlidingExpiration 的值設置後, Set() 方法就能夠被用來將一個數據項添加到緩存。這一次 MemoryCacheEntryOptions 對象會被做爲第三個參數傳遞給 Set() 方法。

當緩存項會被移除時,能夠鏈接回調

有時你會想要在緩存項從緩存中被移除時收到通知。可能會有多種緣由須要從緩存中移除數據項。例如,由於明確地執行了 Remove() 方法而移除了一個緩存項, 也有多是由於它的 AbsoluteExpiration 和 SlidingExpiration 值已經到期而被移除,諸如此類的緣由。

爲了能知道項目是什麼時候從緩存移除的,你須要編寫一個緩存函數。以下代碼向你展現瞭如何去作這件事情

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration =DateTime.Now.AddMinutes(1);options.SlidingExpiration = TimeSpan.FromMinutes(1);
options.RegisterPostEvictionCallback(MyCallback, this);
_memoryCache.Set<string>("timestamp", DateTime.Now.ToString(), options);

private static void MyCallback(object key, object value,EvictionReason reason, object state)
{
    var message = $"Cache entry was removed : {reason}";
    ((HomeController)state).
_memoryCache.Set("callbackMessage", message);
}

請仔細觀察這段代碼。 MyCallback() 是 HomeController 類裏面的一個私有靜態函數,它有四個參數。前面兩個參數表示剛剛刪除的緩存項的鍵和值,第三個參數表示的是該數據項被刪除的緣由。EvictionReason 是一個枚舉類型,它維護者各類可能的刪除緣由,如過時,刪除以及替換。
在回調函數的內部,咱們會基於刪除的緣由構造一個字符串消息。咱們想要將此消息設置成另一個緩存項。這樣作的話就須要訪問 HomeController 的緩存對象,此時狀態參數就能夠排上用場了。使用狀態對象,你能夠對 HomeController 的緩存對象進行控制,並使用 Set() 增長一個 callbackMessage 緩存項。
你能夠經過 Show() 這個 action 來訪問到 callbackMessage,以下所示:

public IActionResult Show()
{
  string timestamp = cache.Get<string>("timestamp");
  ViewData["callbackMessage"] = 
    _memoryCache.Get<string>("callbackMessage");
  return View("Show",timestamp);
}

最後就能夠在 Show 視圖中顯示出來了:

<h1>TimeStamp : @Model</h1>
<h3>@ViewData["callbackMessage"]</h3>
<h2>@Html.ActionLink("Go back", "Index", "Home")</h2>

 

 一些建議,像上面提到的設置緩存數據項的過時時間那塊,若是一個項目中全部的緩存過時時間是一致的,咱們能夠有更簡單的作法,而不是每一個地方都去寫一堆這個:

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration = DateTime.Now.AddMinutes(1);
options.SlidingExpiration = TimeSpan.FromMinutes(1);

 

能夠在頭部定義一個共有的

readonly MemoryCacheEntryOptions _options = Cache.GetMemoryCacheEntryOptions();

Cache是自定義的一個緩存類,其中GetMemoryCacheEntryOptions就是獲取當前緩存設置項的,代碼以下:

 public static MemoryCacheEntryOptions GetMemoryCacheEntryOptions()
        {
           var options = new MemoryCacheEntryOptions
            {
                AbsoluteExpiration =
                    DateTimeOffset.Now.AddMinutes(double.Parse($"{SiteConfig.GetSite("ExpiredTime")}")),
                SlidingExpiration = TimeSpan.FromMinutes(double.Parse($"{SiteConfig.GetSite("ExpiredTime")}"))
            };

            return options;
        }

這裏面咱們將過時時間放到配置文件中,這裏讀取配置文件能夠參考前面的博客:http://www.javashuo.com/article/p-fnnqcima-kg.html

這樣,咱們的寫法就能夠簡寫不少,固然,剛纔提到了是使用的 IMemoryCache 的 Set<T>() 來完成的,因此,這裏咱們不只能夠傳String,還能夠按需傳遞,好比:

var homeCache = _memoryCache.Get<Task<HomeInfo>>("HomeCache");//獲取HomeCache
if (homeCache == null)//判斷是否存在
{
   homeCache = _dadaServices.GetHomeData();//調用API獲取數據
   _memoryCache.Set<Task<HomeInfo>>("HomeCache", homeCache, _options);//存放HomeCache,傳入data數據和設置的數據項
}
return View("~/Views/Home/Index.cshtml", homeCache.Result);//返回Cache

 到這裏,你已經大概知道了MemoryCache 的簡單使用方式,剩下的就自行研究吧~

相關文章
相關標籤/搜索