.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 的简单使用方式,剩下的就自行研究吧~

相关文章
相关标签/搜索