【.NET Core框架】IMemoryCache、IDistributedCache
介绍
.Net Core框架中有两个缓存接口:
- IMemoryCache:内存缓存接口,内存缓存可以存储任何对象
- IDistributedCache:分布式缓存接口(Redis、Sqlserver、MongoDB、DB...)
微软缓存组件源码在:https://github.com/aspnet/Caching
内存缓存(MemoryCache)
如何使用
1、安装程序集:Microsoft.Extensions.Caching.Memory
,如果是是Core MVC程序自带的Microsoft.AspNetCore.App
包里已经涵盖了 Microsoft.Extensions.Caching.Memory
,无需重复下载。
2、AddControllers()
中已经注册了缓存服务services.AddMemoryCache();
。如果是控制台项目使用内存缓存,需要手动注册缓存服务: services.AddMemoryCache();
。
3、构造函数注入 IMemoryCache
IMemoryCache
内存缓存接口只有三个方法
public interface IMemoryCache : IDisposable
{
ICacheEntry CreateEntry(object key);//添加一个缓存
void Remove(object key);//删除一个缓存
bool TryGetValue(object key, out object value);//获取一个缓存(并可得到具体的缓存是否存在)
}
ICacheEntry代表一条缓存
public interface ICacheEntry : IDisposable
{
DateTimeOffset? AbsoluteExpiration{get;set;}
TimeSpan? AbsoluteExpirationRelativeToNow{get;set;}
IList<IChangeToken> ExpirationTokens{get;}
object Key{get;}
IList<PostEvictionCallbackRegistration> PostEvictionCallbacks{get;}
CacheItemPriority Priority{get;set;}
long? Size{get;set;}
TimeSpan? SlidingExpiration{get;set;}
object Value{get;set;}
}
- Key 缓存key
- Value 缓存值
- AbsoluteExpiration 绝对过期时间,为null则条件无效
- AbsoluteExpirationRelativeToNow 相对当前时间的绝对过期时间(使用TimeSpan),为null条件无效
- SlidingExpiration 滑动过期时间
- ExpirationTokens 提供用来自定义缓存过期
- PostEvictionCallbacks 缓存失效回调
- Priority 缓存项优先级(在缓存满载的时候决定清除的顺序)
- Size 代表缓存数据的大小,在内存缓存中一般为null
演示:
IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); ;
using (ICacheEntry cacheEntry = cache.CreateEntry("k1"))
{
cacheEntry.SetValue("v1");
}
string v1 = cache.Get<string>("k1");
MS提供了IMemoryCache
扩展方法类CacheExtensions
CacheExtensions:
public static class CacheExtensions
{
public static object Get(this IMemoryCache cache, object key){}
public static Task<TItem> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory){}
public static TItem GetOrCreate<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factory){}
public static TItem Get<TItem>(this IMemoryCache cache, object key){}
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value){}
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options){}
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, IChangeToken expirationToken){}
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, DateTimeOffset absoluteExpiration){}
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, TimeSpan absoluteExpirationRelativeToNow){}
public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem value){}
}
缓存案例
绝对时间、滑动时间
//绝对过期
return memoryCache.GetOrCreate("c1", entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(2);
return ++time;
});
//绝对过期
return memoryCache.GetOrCreate("c2", entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(2);
return ++time;
});
//滑动过期
return memoryCache.GetOrCreate("c3", entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(2);
return ++time;
});
//过期组合策略
return memoryCache.GetOrCreate("c4", entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(2);
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(2);
return ++time;
});
//过期回调
return memoryCache.GetOrCreate("c1", entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(2);
entry.RegisterPostEvictionCallback((key, value, reason, state) =>
{
Console.WriteLine($"key:{key}");
Console.WriteLine($"value:{key}");
Console.WriteLine($"reason:{key}");
Console.WriteLine($"state:{key}");
});
return ++time;
});
通过IChangeToken清除缓存
public class CacheProvider
{
private static CancellationTokenSource _resetCacheToken = new CancellationTokenSource();
private readonly IMemoryCache _innerCache;
public T Set<T>(object key, T value)
{
var options = new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.Normal).SetAbsoluteExpiration(typeExpiration);
options.AddExpirationToken(new CancellationChangeToken(_resetCacheToken.Token));
_innerCache.Set(CreateKey(type, key), value, options);
return value;
}
public void Reset()
{
if (_resetCacheToken != null && !_resetCacheToken.IsCancellationRequested && _resetCacheToken.Token.CanBeCanceled)
{
_resetCacheToken.Cancel();
_resetCacheToken.Dispose();
}
_resetCacheToken = new CancellationTokenSource();
}
}
MemoryCacheOptions
Clock 顾名思义,是用来提供当前系统时间(默认UTC),
CompactOnMemoryPressure 已经被废弃,可以不用管
ExpirationScanFrequency 过期扫描频率(默认为1分钟,可以理解为每过多久移除一次过期的缓存项)
SizeLimit 缓存大小限制(这属于一个说明性属性,而且单位也不是缓存数目,而是缓存真正占用的空间大小)
CompactionPercentage 压缩率(默认0.05,百分比)
Clock
默认当前系统的当前UTC时间,可以重写 Clock 来实现自己自定义的获取当前时间的逻辑。
缓存里面用此时间来判断缓存是否过期。
ExpirationScanFrequency
缓存无非是一个字典表,当一些缓存项过期失效时候我们需要移除字典表里面的内容。
MemoryCache提供了一个属性来设置,间隔多久才进行一次过期缓存移除(默认1分钟)。
因为MemoryCache里面没有使用定时器来进行过期扫描,在添加、获取、删除缓存项时校验ExpirationScanFrequency
属性,如果超过了间隔时间就移除缓存
SizeLimit
这个属性的作用是:当所有缓存大小超过这个值的时候进行一次缓存压缩。
这个属性并不是缓存项的数量,而是缓存真正占用的空间大小,如这个缓存项占用了多少内存。
在 MemoryCache 中关于缓存项的大小默认都是null或0,当然你可以通过手动设置缓存项的Size来启用相关功能。
CompactionPercentage
当内存大小超过 SizeLimit 时候进行压缩的比率,默认值是0.05,也就是百分之5。
缓存清理优先级:
压缩顺序:Low、Normal、High
NeverRemove 永远不会在超过 SizeLimt 时候进行清理。
CacheEntry 默认的 优先级为:Normal。
使用 SetSize、Size 和 SizeLimit 限制缓存大小
MemoryCache 实例可以选择指定并强制实施大小限制。 缓存大小限制没有定义的度量单位,因为缓存没有度量条目大小的机制。 如果设置了缓存大小限制,则所有条目都必须指定大小。 ASP.NET Core 运行时不会根据内存压力限制缓存大小。 由开发人员限制缓存大小。 指定的大小采用开发人员选择的单位。
SizeLimit 没有单位。 如果已设置缓存大小限制,则缓存条目必须以其认为最适合的任何单位指定大小。 缓存实例的所有用户都应使用相同的单位系统。 如果缓存的条目大小之和超出了 SizeLimit 指定的值,则不会缓存条目。 如果未设置缓存大小限制,则会忽略条目上设置的缓存大小。
例如:
如果 Web 应用主要缓存字符串,则每个缓存条目的大小可以是字符串长度。
应用可以将所有条目的大小指定为 1,大小限制是条目计数。
设置SizeLimit:
new MemoryCacheOptions
{
SizeLimit = 1024
};
使用 SetSize 扩展方法或 Size 属性设置缓存条目的大小:
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSize(1);
// cacheEntryOptions.Size = 1;
_memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
MemoryCache.Compact(清理缓存)
MemoryCache.Compact 尝试按以下顺序删除指定百分比的缓存:
所有到期项。
按优先级排列的项。 首先删除最低优先级的项。
最近最少使用的对象。
绝对到期时间最短的项。
可调到期时间最短的项。
永远不会删除优先级为 NeverRemove 的固定项。
调用 Compact 以删除 50% 的缓存条目:
((MemoryCache)memoryCache).Compact(0.5);
分布式缓存(IDistributedCache)
IDistributedCache:
public interface IDistributedCache
{
//获取
byte[] Get(string key);
Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken));
//刷新并重置其过期超时的值(如果有)
void Refresh(string key);
Task RefreshAsync(string key, CancellationToken token = default(CancellationToken));
//删除
void Remove(string key);
Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));
//添加
void Set(string key, byte[] value, DistributedCacheEntryOptions options);
Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken));
}
Redis分布式缓存如何使用
安装 Microsoft.Extensions.Caching.Redis包
控制台中使用:
static void Main(string[] args)
{
//RedisCache实现了接口IDistributedCache
RedisCache redisCache = new RedisCache(new RedisCacheOptions() {
Configuration="192.168.254.134:6379",
InstanceName="test"
});
redisCache.SetString("key","value");
var val = redisCache.GetString("key");
}
asp.net core中使用:
//将Redis分布式缓存服务添加到服务中
services.AddDistributedRedisCache(options =>
{
//Redis实例名
options.InstanceName = "RedisDistributedCache";
options.ConfigurationOptions = new ConfigurationOptions()
{
ConnectTimeout = 2000,
DefaultDatabase = 1,
//Password = "xxxxxx",
AllowAdmin = true,
AbortOnConnectFail = false,//当为true时,当没有可用的服务器时则不会创建一个连接
};
options.ConfigurationOptions.EndPoints.Add("xxxxxx:6379");
});
需要缓存的服务中注入IDistributedCache即可:
public HomeController(IDistributedCache Cache){}
MongoDB分布式缓存如何使用
安装 MarkCBB.Extensions.Caching.MongoDB 包
控制台中使用:
static void Main(string[] args)
{
MongoDBCache mongoDBCache = new MongoDBCache(new MongoDBCacheOptions()
{
ConnectionString = "mongodb://192.168.254.135:27017",
DatabaseName = "sample",
CollectionName = "sample"
});
mongoDBCache.Set("username", Encoding.UTF8.GetBytes("jack"), new DistributedCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Now.AddDays(1)
});
var info = mongoDBCache.GetString("username");
}