C#系列之聊聊.Net Core的InMemoryCache
目录
作者:暴王
个人博客:http://www.boydwang.com/2017/12/net-core-in-memory-cache/
这两天在看.net core的in memory cache,这里记录一下用法,主要涉及MemoryCache的Get/Set/Expire/Flush。
首先我们先用dotnet命令创建一个mvc的项目,这里我们将使用postman来请求server,
1 | dotnet new MVC |
因为我们要用到 Microsoft.Extensions.Caching.Memory这个nuget包,所以需要添加引用,用VsCode(或任何编辑器)打开刚才建的mvc项目路径下的*.csproj文件,在这里我的是cache.csproj,找到
1 | <PackageReference Include= "Microsoft.Extensions.Caching.Memory" Version= "2.0.0.0" /> |
注意版本号可能不一样,我用的是.net core 2.0.
之后我们需要注册cache服务,打开Startup.cs文件,找到ConfigureServices方法,添加如下代码:
1 | services.AddMemoryCache(); |
ConfigureServices方法看起来应该是这样:
1 2 3 4 5 | public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddMvc(); } |
之后我们就可以在controller里通过构造注入的方式使用InMemoryCache啦。
打开HomeController或者自己新建一个Controller,在修改构造方法
1 2 3 4 5 | private IMemoryCache _cache; public HomeController(IMemoryCache cache) { this ._cache = cache; } |
先来看看MemoryCache的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Constructors: MemoryCache(IOptions) Properties: Count(Gets the count of the current entries for diagnostic purposes.) Methods: Compact(Double) CreateEntry(Object) Dispose() Dispose(Boolean) Finalize() Remove(Object) TryGetValue(Object, Object) Extension Methods: Get(IMemoryCache, Object) Get(IMemoryCache, Object) GetOrCreate(IMemoryCache, Object, Func) GetOrCreateAsync(IMemoryCache, Object, Func>) Set(IMemoryCache, Object, TItem) Set(IMemoryCache, Object, TItem, MemoryCacheEntryOptions) Set(IMemoryCache, Object, TItem, IChangeToken) Set(IMemoryCache, Object, TItem, DateTimeOffset) Set(IMemoryCache, Object, TItem, TimeSpan) TryGetValue(IMemoryCache, Object, TItem) |
我们用到的大部分都是 扩 展 方 法,这是一个奇怪的现象,但这不是这篇文章讨论的重点,这里会使用到
1 2 | TryGetValue(Object, Object) Set<TItem>(IMemoryCache, Object, TItem, MemoryCacheEntryOptions) |
这两个方法,来Get/Set/Expire缓存项。
首先我们来添加一个get的webapi:
1 2 3 4 5 6 7 | [HttpGet( "cache/{key}" )] public IActionResult GetCache( string key) { object result = new object (); _cache.TryGetValue(key, out result); return new JsonResult(result); } |
它接受一个key作为参数,如果找到则返回找到的值,若找不到则返回空
现在我们可以在命令行里输入
1 2 | dotnet restore dotnet run |
来启动web项目,之后我们可以通过
1 | http: //localhost:5000/cache/{key} |
这个url来访问cache,此时cache还没有值
因为此时我们还没有set值。
接下来添加一个Set方法,在添加之前,我们先来看一下MemoryCacheEntryOptions的定义。
Constructors:
MemoryCacheEntryOptions()
Properties:
AbsoluteExpiration
AbsoluteExpirationRelativeToNow
ExpirationTokens
PostEvictionCallbacks
Priority
Size
SlidingExpiration
Extension Methods:
AddExpirationToken(MemoryCacheEntryOptions, IChangeToken)
RegisterPostEvictionCallback(MemoryCacheEntryOptions, PostEvictionDelegate)
RegisterPostEvictionCallback(MemoryCacheEntryOptions, PostEvictionDelegate, Object)
SetAbsoluteExpiration(MemoryCacheEntryOptions, DateTimeOffset)
SetAbsoluteExpiration(MemoryCacheEntryOptions, TimeSpan)
SetPriority(MemoryCacheEntryOptions, CacheItemPriority)
SetSize(MemoryCacheEntryOptions, Int64)
SetSlidingExpiration(MemoryCacheEntryOptions, TimeSpan)
这里有几个概念:
AbsoluteExpiration
代表了绝对绝对超时时间,在一定时间后必定超时(比如15分钟)
SlidingExpiration
代表了滑动超时时间(我自己翻译的。。),滑动的意思就是假如你设置了SlidingExpiration超时时间为5分钟,如果在5分钟里,有新的请求来获取这个cached item,那么这个5分钟会重置,直到超过5分钟没有请求来,才超时
CacheItemPriority
这是一个枚举,代表了缓存的优先级,默认值为Normal,如果设置为NeverRemove则一直不超时。
1 2 3 4 | High Low NeverRemove Normal |
RegisterPostEvictionCallback
这是个方法需要传一个回调,在缓存项被移除(超时)的时候触发回调。
接着我们来添加一个Set方法,并且为它添加一个canceltoken,以便我们能够手动控制强制清空缓存。
1 2 3 4 5 6 7 8 9 10 11 12 | private static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); [HttpPost( "cache/" )] public IActionResult SetCache([FromBody]CacheItem item) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(5)) .RegisterPostEvictionCallback(DependentEvictionCallback, null ) .AddExpirationToken( new CancellationChangeToken(cancellationTokenSource.Token)); _cache.Set(item.key, item.value, cacheEntryOptions); return Ok(); } |
然后我们就可以用postman的post请求来Set缓存了,地址是:
1 | http: //localhost:5000/cache |
接下来我们来添加一个flush api,我们能够手动清空缓存。这里我们利用了上面在Set时添加的cancellationTokenSource
1 2 3 4 5 6 | [HttpGet( "cache/flush" )] public IActionResult Flush() { cancellationTokenSource.Cancel(); return Ok(); } |
访问地址:
1 | http: //localhost:5000/cache/flush |
调用这个api会发现在console里有一行输出
1 | Parent entry was evicted. Reason: TokenExpired, Key: a. |
可以在多个缓存项中添加同一个token,达到同时清除多个缓存项的目的。
遇到的坑:
1.token不work的问题.
我在最初实现的时候,加了一个token,是这么写的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private CancellationTokenSource cancellationTokenSource; public HomeController(IMemoryCache cache) { this ._cache = cache; cancellationTokenSource = new CancellationTokenSource(); } [HttpGet( "cache/flush" )] public IActionResult Flush() { cancellationTokenSource.Cancel(); return Ok(); } |
然后发现调用flush一直不生效,cache并没有被清掉,很纳闷,以为我的token方法用的有问题。
直到我换成了下面的代码,大家体会一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 | private static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); public HomeController(IMemoryCache cache) { this ._cache = cache; } [HttpGet( "cache/flush" )] public IActionResult Flush() { cancellationTokenSource.Cancel(); return Ok(); } |
仅仅是一个static的问题,就产生了不一样的结果,这是因为每次httprequest过来,都会启动一个新线程去响应它,因此在set的时候加进去的那个token,并不是flush请求过来的token,因为又调用了一次构造方法,生成了一个新的CancellationTokenSource对象,因此调用token.Cancel()方法必然会失效,因此改成了static,让每次请求的都是同一个token,这样就不会造成不同token导致的Cancel方法不work的问题,清空cache也就能正常工作了。
2.RegisterPostEvictionCallback重复触发的问题
RegisterPostEvictionCallback不仅仅在缓存超时的时候触发,也会在缓存被替换(更新)的时候触发,在PostEvictionDelegate有一个参数为EvictionReason指明了缓存项被移除的原因
1 | public delegate void PostEvictionDelegate( object key, object value, EvictionReason reason, object state); |
1 2 3 4 5 6 7 | EvictionReason None = 0, Removed = 1, 缓存项被Remove()方法移除 Replaced = 2, 缓存项被更新 Expired = 3, 缓存项超时 TokenExpired = 4, 缓存由token触发超时 Capacity = 5 缓存空间不足 |
因此我们需要在callback里根据需要判断缓存是因为什么原因被移除,才能避免意外的回调触发。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述