这篇文章(主要翻译于官网,水平有限,见谅)讲解asp.net core 中的 Cache in-memory (内存缓存).

Cache in-memory in ASP.NET Core

Caching basics

Caching 可以显著的提升应用的performance(表现) 和 scalability,通过减少生成内容所必需的做的工作。Caching 在变动比较的数据上工作的最好。Caching 可以做一个备份数据,使得数据比从原来的地方取的快一些。

ASP.NET Core支持几种不同的缓存。最简单的缓存是基于IMemoryCache, 它代表一个存储在web服务器的内存上的cache(缓存)。当使用in-memory cache时,运行在多个服务器上的服务器集群应该确保sessions是不动的,不动的sessions(Sticky sessions)确保随后的从一个client发来的请求全都到同一台服务器。例如,Azure Web apps用Application Request Routing(ARR)来路由所有随后的请求到同一个服务器。

在一个web集群上的Non-sticky sessions 要求一个distributed cache(分布式缓存)来避免缓存一致性问题。对于一些应用,a distributed cache 可以支持更高的扩展比in-memory cache. 用一个分布式缓存卸载内存缓存到一个外部处理中。

In-memory cache 可以存储任意对象;distributed cache interface 仅限于byte[]. 对于in-memory和distributed cache 存储cache items为key-value pairs.

System.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache可以被用在:

  • .NET Standard 2.0 or later

  • Any .NET implementation that targets .NET Standard 2.0 or later. 例如, ASP.NET Core 2.0 or later

  • .NET Framework 4.5 or later

Microsoft.Extensions.Caching.Memory/IMemoryCache 被推荐在System.Runtime.Cachign/MemoryCache之上使用, 因为Microsoft.Extensions.Caching.Memory/IMemoryCache是更好的集成在ASP.NET Core中。例如,IMemory 天生可以用ASP.NET Core的依赖注入工作。

用System.Runtime.Caching/MemoryCache作为一个兼容桥梁,当移植代码从ASP.NET 4.X 到ASP.NET Core时。

Cache guidelines

  • 代码应该总有一个可靠的选项来取数据并且不是依赖于缓存的可得到的值

  • 缓存使用稀少的资源,内存。限制缓存增长(cache growth)(内存是稀缺资源, 如果在内存中使用缓存,需要限制缓存增长):

    • 不要使用外部输入作为cache keys.

    • 使用expirations(过期时间)限制缓存增长

    • 使用SetSize, Size和SizeLimit来限制cache size.  ASP.NET Core runtime不会根据memory pressure(内存压力)来限制cache size,它取决于开发者限制cache size.

Using IMemoryCache

In-memory caching 是一个从你的应用中使用依赖注入引入的服务(service)。在ConfigureServices中调用 AddMemoryCache:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
} public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}

Request the IMemoryCache实例在构造函数中:

public class HomeController : Controller
{
private IMemoryCache _cache; public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

IMemoryCache要求有NuGet package Microsoft.Extensions.Caching.Memory, 它在Microsoft.AspNetCore.App metapackage也是可用的。

下面的代码使用 TryGetValue 来检验 if a time is in the cache. If a time isn’t cached, a new entry is created and added to the cache with Set. 检验一个时间值是否在缓存中。如果时间值没有被缓存,一个新的entry被创建并且with Set加入到缓存中。(即,如果没被缓存,则加入缓存)

public static class CacheKeys
{
public static string Entry { get { return "_Entry"; } }
public static string CallbackEntry { get { return "_Callback"; } }
public static string CallbackMessage { get { return "_CallbackMessage"; } }
public static string Parent { get { return "_Parent"; } }
public static string Child { get { return "_Child"; } }
public static string DependentMessage { get { return "_DependentMessage"; } }
public static string DependentCTS { get { return "_DependentCTS"; } }
public static string Ticks { get { return "_Ticks"; } }
public static string CancelMsg { get { return "_CancelMsg"; } }
public static string CancelTokenSource { get { return "_CancelTokenSource"; } }
}
public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry; // Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now; // Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds()); // Save data in cache.
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
} return View("Cache", cacheEntry);
}

当前时间和缓存时间都被展示了:

@model DateTime?

<div>
<h2>Actions</h2>
<ul>
<li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
<li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreateAsync">GetOrCreateAsync</a></li>
<li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
</ul>
</div> <h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>

当requests在超时时间之内时,缓存的时间值保留在缓存中。下面的图片展示了当前时间和从缓存中检索的更早的时间。

下面的代码使用GetOrCreate和GetOrCreateAsync来缓存数据。

public IActionResult CacheGetOrCreate()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds();
return DateTime.Now;
}); return View("Cache", cacheEntry);
} public async Task<IActionResult> CacheGetOrCreateAsync()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds();
return Task.FromResult(DateTime.Now);
}); return View("Cache", cacheEntry);
}

下面的代码调用Get来取到缓存时间:

public IActionResult CacheGet()
{
var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
return View("Cache", cacheEntry);
}

GetOrCreate, GetOrCreateAsyc, 和Get 是CacheExtensions类的扩展方法的一部分,CacheExtension类扩展了IMemory的能力。

MemoryCacheEntryOptions

下面的例子(用来设置内存缓存的一些选项):

  • 设置完全的expiration time(超时时间)。这是这个记录可以被缓存的最大时间,并且可以防止这个记录变的太陈旧,当变化的expiration 是不断更新的。
  • 设置一个变化的expiration time. 到达这个缓存(cache item)的请求将重新设置变化的时间。
  • 给CacheItemPriority.NeverRemove设置cache priority(缓存优先级)
  • 设置PostEvictionDelegate(回调),它将会被调用,在记录被从缓存驱逐之后。Callback(回调)是运行在一个不同于从缓存中移除缓存项的线程的其他线程。(Callback运行在一个区别于移除缓存项线程的其他线程)
public IActionResult CreateCallbackEntry()
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(CacheItemPriority.NeverRemove)
// Add eviction callback
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this); _cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions); return RedirectToAction("GetCallbackEntry");
} public IActionResult GetCallbackEntry()
{
return View("Callback", new CallbackViewModel
{
CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
Message = _cache.Get<string>(CacheKeys.CallbackMessage)
});
} public IActionResult RemoveCallbackEntry()
{
_cache.Remove(CacheKeys.CallbackEntry);
return RedirectToAction("GetCallbackEntry");
} private static void EvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

使用SetSize, Size和SizeLImit来限制 cache size

一个MemoryCache实例可以选择指定或者强制一个size limit。 The memory size limit 没有一个定义的测量单元,因为cache没有结构来测量记录(entries)大小(size). 如果cache memory size limit被设置了,所有的entries必须指定size.  ASP.NET Core runtime不会根据memory pressure来limit cache size . 它取决于开发者limit cache size. The size spcified is in units the developer chooses.

MemoryCache instance may optionally specify and enforce a size limit. The memory size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. If the cache memory size limit is set, all entries must specify size. The ASP.NET Core runtime does not limit cache size based on memory pressure. It's up to the developer to limit cache size. The size specified is in units the developer chooses.

例如:

  • 如果一个web应用主要caching string , 每个cache entry size应该是字符串长度

  • 应用可以指定the size of all entries (所有的entry)为1,并且这个size limit是the count of entries. (注意:这里指定所有的entry的大小为1,则size limit可以用entry的数量表示。即两者一个是cache entry size(单个entry大小),另一个是limit size(缓存限制的大小))

下面的代码创建了一个unitless fiexed size MemoryCache accessible(易接近的) by dependency injection:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache
{
public MemoryCache Cache { get; set; }
public MyMemoryCache()
{
Cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit =
});
}
}

SizeLimit没有 units .   如果cache memory size 已经被设置,Cached entries 必须指定最合适的size in whatever units they deem(认为)。一个cache 实例的所有用户应该用同样的unit system . 如果the sum of the cached entry sizes 超过通过SizeLimit指定的值, An entry 将不会被缓存. 如果no cache size limit被设置,the cache size set on the entry 将会被忽略。

SizeLimit does not have units. Cached entries must specify size in whatever units they deem most appropriate if the cache memory size has been set. All users of a cache instance should use the same unit system. An entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. If no cache size limit is set, the cache size set on the entry will be ignored.

下面的代码使用依赖注入容器注册MyMemoryCache.

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache被创建为一个independent memory cache 的组件,这个组件了解size limited cahce并且知道怎么合适的设置cache entry size 。

下面是使用MyMemoryCache的代码:

public class AboutModel : PageModel
{
private MemoryCache _cache;
public static readonly string MyKey = "_MyKey"; public AboutModel(MyMemoryCache memoryCache)
{
_cache = memoryCache.Cache;
} [TempData]
public string DateTime_Now { get; set; } public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString(); var cacheEntryOptions = new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds()); // Set cache entry size via property.
// cacheEntryOptions.Size = 1; // Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
} DateTime_Now = cacheEntry; return RedirectToPage("./Index");
}
}

The size of the cache entry 可以被设置,通过Size和SetSize扩展方法

public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString(); var cacheEntryOptions = new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds()); // Set cache entry size via property.
// cacheEntryOptions.Size = 1; // Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
} DateTime_Now = cacheEntry; return RedirectToPage("./Index");
}

Cache dependencies

下面的示例展示了怎么设置过期一个缓存记录(how to expire a cache entry)如果一个dependent entry expires(过期). 一个CancellationChangeToken 被加入到cached item. 当Cancel 在CancellationTokenSource上被调用,两个cache entry 都被抛弃.

public IActionResult CreateDependentEntries()
{
var cts = new CancellationTokenSource();
_cache.Set(CacheKeys.DependentCTS, cts); using (var entry = _cache.CreateEntry(CacheKeys.Parent))
{
// expire this entry if the dependant entry expires.
entry.Value = DateTime.Now;
entry.RegisterPostEvictionCallback(DependentEvictionCallback, this); _cache.Set(CacheKeys.Child,
DateTime.Now,
new CancellationChangeToken(cts.Token));
} return RedirectToAction("GetDependentEntries");
} public IActionResult GetDependentEntries()
{
return View("Dependent", new DependentViewModel
{
ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
Message = _cache.Get<string>(CacheKeys.DependentMessage)
});
} public IActionResult RemoveChildEntry()
{
_cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
return RedirectToAction("GetDependentEntries");
} private static void DependentEvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Parent entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

使用CancellationTokenSource允许多个cache entries作为一组被抛弃。使用代码中有用的模式,cache entires

Created inside the using block will inherit triggers and expiration settings.

Additional notes

  • 当用一个回调来增加一个cache item:

    • 可以发现多个requests缓存的键的value 是空.因为回调没有完成。

    • 这可能会导致几个线程增加cached item

  • 当一个cache entry被使用来创建另一个cache entry,child复制父母亲的entry的expration tokens 并且基于时间的exiration settings. THe child不会通过手动删除或者parent entry的更新而过期
  • 用PostEvictionCallback来设置callback, 这个callback将会被触发。

参考资料:

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2

asp.net core 系列之Reponse caching之cache in-memory (2)的更多相关文章

  1. asp.net core 系列之Reponse caching 之 Response Caching Middleware(4)

    这篇文章介绍 Response Caching Middleware . Response Caching Middleware in ASP.NET Core 通过在ASP.NET Core应用中 ...

  2. asp.net core 系列之Response caching(1)

    这篇文章简单的讲解了response caching: 讲解了cache-control,及对其中的头和值的作用,及设置来控制response caching; 简单的罗列了其他的缓存技术:In-me ...

  3. asp.net core 系列之Response caching 之 Distributed caching(3)

    这篇文章讲解分布式缓存,即 Distributed caching in ASP.NET Core Distributed caching in ASP.NET Core 分布式缓存是可以在多个应用服 ...

  4. asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)

    一.概述 HTTP 是无状态的协议. 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息. 本文介绍了几种保留请求间用户数据和应用状态的方法.下面以表格形式列出这些存储方式,本篇专讲Sess ...

  5. 【目录】asp.net core系列篇

    随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...

  6. Ajax跨域问题及解决方案 asp.net core 系列之允许跨越访问(Enable Cross-Origin Requests:CORS) c#中的Cache缓存技术 C#中的Cookie C#串口扫描枪的简单实现 c#Socket服务器与客户端的开发(2)

    Ajax跨域问题及解决方案   目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP ...

  7. 1.1专题介绍「深入浅出ASP.NET Core系列」

    大家好,我是IT人张飞洪,专注于.NET平台十年有余. 工作之余喜欢阅读和写作,学习的内容包括数据结构/算法.网络技术.Linux系统原理.数据库技术原理,设计模式.前沿架构.微服务.容器技术等等…… ...

  8. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  9. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

随机推荐

  1. ZOJ1608 Two Circles and a Rectangle

    Time Limit: 2 Seconds      Memory Limit: 65536 KB Give you two circles and a rectangle, your task is ...

  2. 十步完全理解 SQL(转载)

    1 SQL是一种声明式语言 SQL 语言是为计算机声明了一个你想从原始数据中获得什么样的结果的一个范例,而不是告诉计算机如何能够得到结果.学好SQL要改变传统函数式编程思想,例如用变量传参.使用循环语 ...

  3. transform与position:fixed的那些恩怨

    1. 前言 在写这篇文章之前,我理解的fixed元素是这样的:(摘自CSS布局基础) 固定定位与absolute定位类型类似,但它的相对移动的坐标是视图(屏幕内的网页窗口)本身.由于视图本身是固定的, ...

  4. 802.11 MAC层

    1. 介绍 本文主要介绍了802.11 MAC层 2. 访问机制 CSMA/CA:  Carrier Sense Multiple Access with Collision Avoidance Wi ...

  5. 刷leetcode是什么样的体验?【转】

    转自:https://www.zhihu.com/question/32322023 刷leetcode是什么样的体验? https://leetcode.com/ 1 条评论   默认排序 按时间排 ...

  6. Spring Boot学习——数据库操作及事务管理

    本文讲解使用Spring-Data-Jpa操作数据库. JPA定义了一系列对象持久化的标准. 一.在项目中使用Spring-Data-Jpa 1. 配置文件application.properties ...

  7. 列表的 sort

    题目:输入三个整数x,y,z,请把这三个数由小到大输出. 实例 #!/usr/bin/python # -*- coding: UTF-8 -*- l = [] for i in range(3): ...

  8. Codeforces Round #324 (Div. 2) Dima and Lisa 哥德巴赫猜想

    原题链接:http://codeforces.com/contest/584/problem/D 题意: 给你一个奇数,让你寻找三个以内素数,使得和为这个奇数. 题解: 这题嘛...瞎比搞搞就好,首先 ...

  9. Codeforces Round #324 (Div. 2) Olesya and Rodion 构造

    原题链接:http://codeforces.com/contest/584/problem/A 题意: 给你n和t,让你构造一个长度为n的数,并且被t整除 题解: 方法很多,可以乱构造.....不过 ...

  10. Longest Valid Parentheses - LeetCode

    Given a string containing just the characters '(' and ')', find the length of the longest valid (wel ...