.Net缓存之MemoryCahe
1. MemoryCahe
NetCore中的缓存和System.Runtime.Caching很相似,但是在功能上做了增强,缓存的key支持object类型;提供了泛型支持;可以读缓存和单个缓存项的大小做限定,可以设置缓存的压缩比例。
通过实现微软官方的Microsoft.Extensions.Caching里面的IDistributedCache接口实现缓存集成到ASPNETCore中
1.1 简单入门
netcore中缓存相关的类库都在 Microsoft.Extensions.Caching ,使用MemoryCache首先安装包
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
注入
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//添加缓存配置
services.AddMemoryCache();
}
使用
private readonly IMemoryCache _cache;
public HomeController(IMemoryCache cache)
{
_cache = cache;
}
[HttpGet]
public string Set()
{
//写
_cache.Set("login", "4545478244");
return "";
}
[HttpGet]
public string Get()
{
//读
var value = _cache.Get("login");
return "";
}
1.2 过期时间
//1.最简单使用方式
_cache.Set("mykey", "myvalue");
//2.绝对过期时间,3秒后过期
_cache.Set("key1", "value1", new DateTimeOffset(DateTime.Now.AddSeconds(3)));
//3.绝对过期时间,效果同上
_cache.Set("key2", "value2", TimeSpan.FromSeconds(3));
//4.滑动过期时间,3秒后,即三秒钟内被访问,则重新刷新缓存时间为3秒后
_cache.Set("key3", "value3", new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromSeconds(3),
});
Console.WriteLine("-----------暂停2秒");
Thread.Sleep(2000);//暂停2秒
Console.WriteLine($"key1的值:{_cache.Get("key1") ?? "key1被清除"}");
Console.WriteLine($"key2的值:{_cache.Get("key2") ?? "key2被清除"}");
Console.WriteLine($"key3的值:{_cache.Get("key3") ?? "key3被清除"}");
Console.WriteLine("-----------暂停2秒");
Thread.Sleep(2000);//再次暂停2秒
Console.WriteLine($"key1的值:{_cache.Get("key1") ?? "key1被清除"}");
Console.WriteLine($"key2的值:{_cache.Get("key2") ?? "key2被清除"}");
Console.WriteLine($"key3的值:{_cache.Get("key3") ?? "key3被清除"}");
在例子中key1,key2都是使用的绝对过期时间,key3使用的相对过期时间,2秒后第一次访问key1、key2、key3都没过期,其中key3的过期时间刷新了,重新设置为3秒后,所以再次暂停2秒后,key1、key2都过期了,key3仍然存在。
程序运行结果如下:
1.2 常用配置
下边的例子介绍netcore中缓存的常用配置,直接看代码
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache(options =>
{
//缓存大小
options.SizeLimit = 3;//如果设置了该值,那么每个set都必须设置size,并且超过了这个值的大小的会自动销毁
//缓存满了时,压缩20%(即删除20份优先级低的缓存项)
options.CompactionPercentage = 0.2;
//两秒钟查找一次过期项
options.ExpirationScanFrequency = TimeSpan.FromSeconds(3);
});
}
[HttpGet]
public string TestSize()
{
//SizeLimit配置3
_cache.Set("item1", "11111", new MemoryCacheEntryOptions
{
//缓存大小占1份
Size = 2
});
_cache.Set("item2", "22222", new MemoryCacheEntryOptions
{
Size = 2
});
var item1 = _cache.Get("item1");//输出 11111
var item2 = _cache.Get("item2");//输出 null
return "";
}
[HttpGet]
public string TestOptions()
{
//单个缓存项的配置
MemoryCacheEntryOptions cacheEntityOps = new MemoryCacheEntryOptions()
{
//绝对过期时间1
//AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(2)),
//绝对过期时间2
//AbsoluteExpirationRelativeToNow=TimeSpan.FromSeconds(3),
//相对过期时间
SlidingExpiration = TimeSpan.FromSeconds(3),
//优先级,当缓存压缩时会优先清除优先级低的缓存项
Priority = CacheItemPriority.Low,//优先级等级:Low,Normal,High,NeverRemove
//缓存大小占1份
Size = 1
};
//注册缓存项被清除时的回调,可以注册多个回调
cacheEntityOps.RegisterPostEvictionCallback((key, value, reason, state) =>
{
Console.WriteLine($"回调函数输出【键:{key},值:{value},被清除的原因:{reason}】");
});
_cache.Set("mykey", "myvalue", cacheEntityOps);
Console.WriteLine($"mykey的值:{_cache.Get("mykey") ?? "mykey缓存被清除了"}");
Console.WriteLine("------------------暂停3秒");
Thread.Sleep(3000);
Console.WriteLine($"mykey的值:{_cache.Get("mykey") ?? "mykey缓存被清除了"}");
return "";
}
注意netcore中设置缓存和缓存项大小是没有单位的
缓存被清空的回调函数可以注册多个(System.Runtime.Caching清除缓存的回调只能是一个)。
程序执行结果
1.3 IChangeToken
上边我们已经简单了解了通过滑动过期时间和绝对过期时间来控制缓存的有效性,但是有时缓存的过期与否和时间没有联系,比如我们缓存一个文件的内容,不管缓存多久只要文件没有发生变化缓存都是有效的。在net framework中我们可以通过CacheDependency来控制,在net core中怎么控制呢?net core中我们可以使用IChangeToken接口轻松实现缓存的过期策略。先看一下IChangeToken接口:
public interface IChangeToken
{
// 是否有变化发生
bool HasChanged { get; }
// token是否会调用回调函数,为true时才会有效
bool ActiveChangeCallbacks { get; }
// 注册一个回调函数,当有变化时触发回调
IDisposable RegisterChangeCallback(Action<object> callback, object state);
}
看一下IChangeToken实现缓存过期策略的两个例子
1.3.1 监控文件
需要安装组件:Microsoft.Extensions.FileProviders.Physical
internal class Program
{
private static void Main(string[] args)
{
string fileName = Path.Combine(Environment.CurrentDirectory, "someCacheData.xml");
var fileInfo = new FileInfo(fileName);
MemoryCache myCache = new MemoryCache(new MemoryCacheOptions() { });
MemoryCacheEntryOptions cacheEntityOps = new MemoryCacheEntryOptions();
//PollingFileChangeToken是IChangeToken的实现类,通过轮询监控文件变化
cacheEntityOps.AddExpirationToken(new Microsoft.Extensions.FileProviders.Physical.PollingFileChangeToken(fileInfo));
//缓存失效时,回调函数
cacheEntityOps.RegisterPostEvictionCallback((key, value, reason, state) => { Console.WriteLine($"文件【{key}】改动了"); });
//添加缓存,key为文件名,value为文件内容
myCache.Set(fileInfo.Name, File.ReadAllText(fileName), cacheEntityOps);
Console.WriteLine(myCache.Get(fileInfo.Name));
}
}
PollingFileChangeToken通过轮询来监控文件有没有发生变化,如果文件中的内容发生改变,缓存就会自动过期。
1.3.2 通过代码控制缓存过期
class Program
{
static void Main(string[] args)
{
MemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions());
MemoryCacheEntryOptions cacheEntityOps = new MemoryCacheEntryOptions();
//使用CancellationChangeToken控制缓存过期
CancellationTokenSource tokenSource = new CancellationTokenSource();
cacheEntityOps.AddExpirationToken(new CancellationChangeToken(tokenSource.Token));
//设置缓存
memoryCache.Set("mykey", "myvalue", cacheEntityOps);
Console.WriteLine(memoryCache.Get("mykey") ?? "缓存被清除了");
//通过代码清除缓存
tokenSource.Cancel();
Console.WriteLine(memoryCache.Get("mykey") ?? "缓存被清除了");
}
}
tokenSource.Cancel方法发送取消信号,这个方法会触发缓存过期,基于此我们可以通过Cancel方法灵活的实现自定义的缓存策略。
程序执行结果如下:
1.4 引用Nuget包
直接引用我自己简单封装的一个Nuget包(简单封装自己用,不要嘲笑)
<PackageReference Include="Common.Cache.MemoryCache" Version="1.1.0" />
注入到容器
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//注入
services.AddMemoryCacheExtension();
}
使用
# 在需要使用的地方进行注入
private readonly IMemoryCachimg _cache;
public HomeController(IMemoryCachimg cache)
{
_cache = cache;
}
参考文档
作者:捞月亮的猴子 https://www.cnblogs.com/wyy1234/p/10519681.html
官方教程:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/memory?view=aspnetcore-5.0