欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#系列之聊聊.Net Core的InMemoryCache

程序员文章站 2022-06-21 13:28:33
作者:暴王 个人博客:http://www.boydwang.com/2017/12/net core in memory cache/ 这两天在看.net core的in memory cache,这里记录一下用法,主要涉及MemoryCache的Get/Set/Expire/Flush。 首先我 ......

作者:暴王
个人博客:

这两天在看.net core的in memory cache,这里记录一下用法,主要涉及memorycache的get/set/expire/flush。
首先我们先用dotnet命令创建一个mvc的项目,这里我们将使用postman来请求server,

dotnet new mvc 

因为我们要用到 microsoft.extensions.caching.memory这个nuget包,所以需要添加引用,用vscode(或任何编辑器)打开刚才建的mvc项目路径下的*.csproj文件,在这里我的是cache.csproj,找到这个标签,添加如下代码:

<packagereference include="microsoft.extensions.caching.memory" version="2.0.0.0"/>

注意版本号可能不一样,我用的是.net core 2.0.
之后我们需要注册cache服务,打开startup.cs文件,找到configureservices方法,添加如下代码:

services.addmemorycache();

configureservices方法看起来应该是这样:

public void configureservices(iservicecollection services)
{
    services.addmemorycache();
    services.addmvc();
}

之后我们就可以在controller里通过构造注入的方式使用inmemorycache啦。
打开homecontroller或者自己新建一个controller,在修改构造方法

private imemorycache _cache;
public homecontroller(imemorycache cache)
{
    this._cache = cache;
}

先来看看memorycache的定义:

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)

我们用到的大部分都是 扩 展 方 法,这是一个奇怪的现象,但这不是这篇文章讨论的重点,这里会使用到

trygetvalue(object, object)
set<titem>(imemorycache, object, titem, memorycacheentryoptions)

这两个方法,来get/set/expire缓存项。

首先我们来添加一个get的webapi:

[httpget("cache/{key}")]
public iactionresult getcache(string key)
{
    object result = new object();
    _cache.trygetvalue(key, out result);
    return new jsonresult(result);
}

它接受一个key作为参数,如果找到则返回找到的值,若找不到则返回空
现在我们可以在命令行里输入

dotnet restore
dotnet run

来启动web项目,之后我们可以通过

http://localhost:5000/cache/{key}

这个url来访问cache,此时cache还没有值
C#系列之聊聊.Net Core的InMemoryCache
因为此时我们还没有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则一直不超时。

high    
low 
neverremove 
normal

registerpostevictioncallback
这是个方法需要传一个回调,在缓存项被移除(超时)的时候触发回调。

接着我们来添加一个set方法,并且为它添加一个canceltoken,以便我们能够手动控制强制清空缓存。

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缓存了,地址是:

http://localhost:5000/cache

C#系列之聊聊.Net Core的InMemoryCache

接下来我们来添加一个flush api,我们能够手动清空缓存。这里我们利用了上面在set时添加的cancellationtokensource

[httpget("cache/flush")]
public iactionresult flush()
{
    cancellationtokensource.cancel();
    return ok();
}

访问地址:

http://localhost:5000/cache/flush

调用这个api会发现在console里有一行输出

parent entry was evicted. reason: tokenexpired, key: a.

可以在多个缓存项中添加同一个token,达到同时清除多个缓存项的目的。

遇到的坑:
1.token不work的问题.
我在最初实现的时候,加了一个token,是这么写的

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方法用的有问题。
直到我换成了下面的代码,大家体会一下。

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指明了缓存项被移除的原因

 public delegate void postevictiondelegate(object key, object value, evictionreason reason, object state);
evictionreason
none = 0,
removed = 1,  缓存项被remove()方法移除
replaced = 2,  缓存项被更新
expired = 3,  缓存项超时
tokenexpired = 4, 缓存由token触发超时
capacity = 5 缓存空间不足

因此我们需要在callback里根据需要判断缓存是因为什么原因被移除,才能避免意外的回调触发。