ASP NetCore IMemoryCache缓存
Net Core 缓存系列:
2、Distributed Cache(分布式缓存)-SqlServer
3、Distributed Cache(分布式缓存)-Redis
欢迎交流学习!!! GitHub源码
缓存可以减少向服务器发送请求的次数,从而提高应用程序的性能和可伸缩性。适用于不经常更改的数据,从服务器获取比较耗时等(这里不包含实时性的数据)。
NetCore支持多种缓存。最简单是基于IMemoryCache。IMemoryCache表示存储在web服务器的内存中的缓存。当应用程序运行在多台服务器上应确保会话在使用内存中缓存时处于粘滞状态。粘滞会话确保来自客户端的后续请求都将发送到相同的服务器。
Web中非粘滞性会话需要分布式缓存,以避免缓存一致性问题。对于某些应用,分布式缓存可支持比内存中缓存更高的向外扩展。使用分布式缓存会将缓存卸载到外部进程。
这里用console app 示例详细说下IMemoryCache的应用:
1、Nuget下载以下packages
2、添加MemoryCache服务
注意: CloudMemoryCache和TokenCacheService是自定义的service方法,用于缓存token测试,这里先介绍IMemoryCahce的用法,项目实例代码稍后会上传到Github。
3、通过构造函数依赖注入IMemoryCache
private readonly IMemoryCache _cache; public TokenCacheService(IMemoryCache memoryCache) { this._cache = memoryCache; }
4、获取缓存项
public async Task<string> TryGetValue(string username, string password, TokenType tokenType, string clientId = "")
{
var token = string.Empty;
_confidentialTokenKey = BuildCacheKey(username, password, tokenType, clientId);
if (!_cache.TryGetValue(_confidentialTokenKey, out token))
{
token = await _tokenService.GetAccessToken(username, password, tokenType, clientId);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(10))
.SetPriority(CacheItemPriority.High)
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this)
.SetSize(1);
_cache.Set(_confidentialTokenKey, token, cacheEntryOptions);
}
return token;
}
说明:TryGetValue方法参数不用care,这里是项目需要。主要说明下MemoryCacheEntryOptions类,这个是对缓存项的条件设置,SetSlidingExpiration :设置缓存的滑动过期时间(在滑动过期时间内不断有客户端请求,每次请求会更新缓存项为滑动过期时间,话句话说,如果在滑动过期时间内,一直有客户端请求,那么缓存项永远不会过期),也可以设置固定过期时间,但个人建议设置滑动过期时间。
SetPriority: 设置缓存项的优先级,它的作用是当缓存超过了设置的最大缓存(SizeLimit),MemoryCache compact会按照一定的顺序删除缓存的指定百分比:
- 所有过期项。
- 按优先级排序。 首先删除最低优先级项。
- 最近最少使用的对象。
- 绝对过期的项。
- 具有最早的可调过期项的项。
这里就用到了缓存的priority。上面提到了SizeLimit这里就说下哈:
MemoryCache实例可以选择指定并强制实施大小限制。缓存大小限制没有定义的度量单位,因为缓存没有度量条目大小的机制。如果设置了缓存大小限制,则所有条目都必须指定size。ASP.NET CORE运行时不会根据压力限制缓存大小。开发人员需要限制缓存大小。 指定的大小以开发人员选择的单位为单位。
例如:
- 如果 web 应用主要是缓存字符串,则每个缓存条目大小都可以是字符串长度。
- 应用可以将所有条目的大小指定为1,而大小限制则为条目的计数。
如果 SizeLimit 未设置,则缓存的大小不受限制。 当系统内存不足时,ASP.NET Core 运行时不会剪裁缓存。 应用必须构建为:
public class CloudMemoryCache { public MemoryCache Cache { get; set; } public CloudMemoryCache() { Cache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 10, CompactionPercentage = 0.33, ExpirationScanFrequency = TimeSpan.FromMinutes(5) }); } }
SizeLimit设置为10,最大缓存数为10,每个缓存项都需要设置size为1
var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(10)) .SetPriority(CacheItemPriority.High) .RegisterPostEvictionCallback(callback: EvictionCallback, state: this) .SetSize(1);
CompactionPercentage为缓存删除百分比。
ExpirationScanFrequency为刷新缓存时间间隔。
RegisterPostEvictionCallback:是当缓存项过期或者删除时的回调函数,如下:
private void EvictionCallback(object key, object value, EvictionReason reason, object state) { Console.WriteLine($"key: {key}, reason: {reason}"); }
state:this 意思是传入的当前调用对象。
5、设置缓存项
public void TrySetValue<T>(string key, T obj, object expirationSettings) { try { if (string.IsNullOrEmpty(_cache.Get<string>(key))) { if (expirationSettings == null) { _cache.Set(_confidentialTokenKey, obj); } if (expirationSettings is TimeSpan) { _cache.Set(_confidentialTokenKey, obj, (TimeSpan)expirationSettings); } else if (expirationSettings is DateTimeOffset) { _cache.Set(key, obj, (DateTimeOffset)expirationSettings); } else if (_cache is MemoryCacheEntryOptions) { _cache.Set(key, obj, (MemoryCacheEntryOptions)_cache); } else if (_cache.GetType().GetInterface(typeof(IChangeToken).Name) != null) { _cache.Set(key, obj, (IChangeToken)_cache); } else { throw new TokenException(ErrorCode.InvalidArguments, string.Format(ExceptionConstants.InvalidParameter, $"{expirationSettings}")); } } } catch (Exception ex) { throw new TokenException(ex.Message, ErrorCode.Unknown, ex); } }
ExpirationSettings是外部传入参数,作用是可以根据不同的类型解析调用MemoryCache.Set重载函数。
6、缓存依赖关系
在依赖条目过期或者删除时使缓存条目过期,主要用到的是将CancellationChangeToken 添加到缓存项,当Cancel时调用CancellationTokenSource,删掉缓存项。
public void CreateDependencyEntries() { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); using (var entry = _cache.CreateEntry($"{_confidentialTokenKey}_parent")) { entry.Value = DateTime.Now; entry.RegisterPostEvictionCallback(EvictionCallback, this); //auto expiration var memoryOptions = new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(cts.Token)) .SetSize(1); //remove cache //new CancellationChangeToken(cts.Token) //_cache.Set($"{_confidentialTokenKey}_cts", cts); _cache.Set($"{_confidentialTokenKey}_child", DateTime.Now, memoryOptions); } }
示例设置自动过期
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
也可以设置当删除依赖项时删除缓存项,示例代码如下:
var cts = new CancellationTokenSource();
_cache.Set($"{_confidentialTokenKey}_cts", cts);
public void RemoveDependencyEntry() { _cache.Get<CancellationTokenSource>($"{_confidentialTokenKey}_cts").Cancel(); }
如果 $"{_confidentialTokenKey}_cts" 被删除或者自动过期,那么 $"{_confidentialTokenKey}_parent" 和 $"{_confidentialTokenKey}_child" 缓存项都会被删除或者过期,同时触发回调函数 EvictionCallback,此函数可以用来记录日志或其他后续操作。
OK,以上是对IMemoryCache的简单介绍,说简单但涵盖了IMemoryCache大部分常用功能,详细代码参考Github项目(与Azure token的结合),感兴趣的小伙伴可以关注我,陆续更新Azure Cloud相关的blog呦...