翼度科技»论坛 编程开发 .net 查看内容

ASP.NET Core - 缓存之内存缓存(下)

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
话接上篇 [ASP.NET Core - 缓存之内存缓存(上)],所以这里的目录从 2.4 开始。
2.4 MemoryCacheEntryOptions

MemoryCacheEntryOptions 是内存缓存配置类,可以通过它配置缓存相关的策略。除了上面讲到的过期时间,我们还能够设置下面这些:

  • 设置缓存优先级。
  • 设置在从缓存中逐出条目后调用的 PostEvictionDelegate。
    回调在与从缓存中删除项的代码不同的线程上运行。
  • 限制缓存大小
  1. var memoryCacheEntryOption = new MemoryCacheEntryOptions();
  2. memoryCacheEntryOption.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3);
  3. // 缓存优先级,Low, Normal, High, NeverRemove,Normal是默认值,与缓存删除时的策略有关
  4. memoryCacheEntryOption.SetPriority(CacheItemPriority.Normal);
  5. // 注册缓存项删除回调事件
  6. memoryCacheEntryOption.RegisterPostEvictionCallback(PostEvictionDelegate);
  7. _cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);
  8. public void PostEvictionDelegate(object cacheKey, object cacheValue, EvictionReason evictionReason, object state)
  9. {
  10.         var memoryCache = (IMemoryCache)state;
  11.         Console.WriteLine($"Entry {cacheKey}:{cacheValue} was evicted: {evictionReason}.");
  12. }
复制代码

缓存大小限制要配合 MemoryCache 实例的配置来使用。MemoryCache 实例可以选择指定并强制实施大小限制。 缓存大小限制没有定义的度量单位,因为缓存没有度量条目大小的机制。 如果设置了缓存大小限制,则所有条目都必须指定大小。 ASP.NET Core 运行时不会根据内存压力限制缓存大小。 由开发人员限制缓存大小。 指定的大小采用开发人员选择的单位。
例如:

  • 如果 Web 应用主要缓存字符串,则每个缓存条目的大小可以是字符串长度。
  • 应用可以将所有条目的大小指定为 1,大小限制是条目计数。
    如果未设置 SizeLimit,则缓存会无限增长。 系统内存不足时,ASP.NET Core 运行时不会剪裁缓存。 应用必须构建为:

    • 限制缓存增长。
    • 在可用内存受限时调用 CompactRemove

这里的意思是,缓存大小没有单位,我们可以设置一个总的大小,然后为每个缓存条目设置一个大小。如果没有设置大小的情况下,缓存可能会无限增长,直至用完服务器上的所有内存。
  1. // 我们可以在进行内存缓存注册的时候设置缓存大小限制
  2. services.AddMemoryCache(options =>
  3. {
  4.         options.SizeLimit = 1024;
  5. });
  6. // 之后设定每个缓存项的大小,可根据开发人员的判断设置,例如这里无论多大都设置为 1,则最多有 1024 个缓存项
  7. memoryCacheEntryOption.SetSize(1);
  8. _cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);
复制代码
2.5 缓存清理

缓存到期后不会在后台自动清理。 没有计时器可以主动扫描缓存中的到期项。 而缓存上的任何活动(Get、Set、Remove)都可触发在后台扫描到期项。 如果使用了CancellationTokenSource ,则其计时器 (CancelAfter) 也会删除条目并触发扫描到期项,这个是下面要讲到的。
除了在对缓存进行操作时,会触发对相应的缓存项进行过期检查之外,我们还可以通过手动调用 Remove 方法和 Compact 方法对缓存进行清理。
  1. _cache.Remove(cacheKey);
  2. _cache.Compact(.25);
复制代码
Compact 方法会尝试按以下顺序删除指定百分比的缓存:

  • 所有到期项。
  • 按优先级排列的项。 首先删除最低优先级的项。
  • 最近最少使用的对象。
  • 绝对到期时间最短的项。
  • 可调到期时间最短的项。
永远不会删除优先级为 NeverRemove 的固定项。 上面的代码的作用就是删除cacheKey对于的缓存项,并调用 Compact 以删除 25% 的缓存条目。
2.6 缓存组

缓存项的过期策略除了上面讲到的过期时间的设置之外,还可以通过 CancellationChangeToken 来控制,通过它可以同时控制多个缓存对象的过期策略,实现相关的缓存同时过期,构成一个组的概念。
以下为示例代码:
首先先定义两个方法,将缓存设置和缓存读取分开:
  1. public interface ICacheService
  2. {
  3.         public void PrintDateTimeNow();
  4.         public void SetGroupDateTime();
  5.         public void PrintGroupDateTime();
  6. }
  7. public class CacheService : ICacheService
  8. {
  9.         public const string CacheKey = "CacheTime";
  10.         public const string DependentCancellationTokenSourceCacheKey = "DependentCancellationTokenSource";
  11.         public const string ParentCacheKey = "Parent";
  12.         public const string ChildCacheKey = "Chlid";
  13.         private readonly IMemoryCache _cache;
  14.         public CacheService(IMemoryCache memoryCache)
  15.         {
  16.                 _cache = memoryCache;
  17.         }
  18.         public void PrintDateTimeNow()
  19.         {
  20.                 var time = DateTime.Now;
  21.                 if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
  22.                 {
  23.                         cacheValue = time;
  24.                         // 设置绝对过期时间
  25.                         // 两种实现的功能是一样的,只是时间设置的方式不同而已
  26.                         // 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
  27.                         // _cache.Set(CacheKey, cacheValue, TimeSpan.FromSeconds(2));
  28.                         // 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
  29.                         // _cache.Set(CacheKey, cacheValue, DateTimeOffset.Now.AddSeconds(2));
  30.                         //var memoryCacheEntryOption = new MemoryCacheEntryOptions();
  31.                         //// 滑动过期时间是一个相对时间
  32.                         //memoryCacheEntryOption.SlidingExpiration = TimeSpan.FromSeconds(3);
  33.                         //_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);
  34.                         var memoryCacheEntryOption = new MemoryCacheEntryOptions();
  35.                         memoryCacheEntryOption.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3);
  36.                         // 缓存优先级,Low, Normal, High, NeverRemove,Normal是默认值,与缓存删除时的策略有关
  37.                         memoryCacheEntryOption.SetPriority(CacheItemPriority.Normal);
  38.                         memoryCacheEntryOption.RegisterPostEvictionCallback(PostEvictionDelegate);
  39.                         // 之后设定每个缓存项的大小,可根据开发人员的判断设置,例如这里无论多大都设置为 1,则最多有 1024 个缓存项
  40.                         memoryCacheEntryOption.SetSize(1);
  41.                         _cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);
  42.                 }
  43.                 time = cacheValue;
  44.                 Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss"));
  45.                 Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  46.         }
  47.         public void PostEvictionDelegate(object cacheKey, object cacheValue, EvictionReason evictionReason, object state)
  48.         {
  49.                 var memoryCache = (IMemoryCache)state;
  50.                 Console.WriteLine($"Entry {cacheKey}:{cacheValue} was evicted: {evictionReason}.");
  51.         }
  52.         public void SetGroupDateTime()
  53.         {
  54.                 // 这里为了将 CancellationTokenSource 保存起来,以便在外部可以获取得到
  55.                 var cancellationTokenSource = new CancellationTokenSource();
  56.                 _cache.Set(
  57.                         DependentCancellationTokenSourceCacheKey,
  58.                         cancellationTokenSource);
  59.                 using var parentCacheEntry = _cache.CreateEntry(ParentCacheKey);
  60.                 parentCacheEntry.Value = DateTime.Now;
  61.                 Task.Delay(TimeSpan.FromSeconds(1)).Wait();
  62.                 _cache.Set(
  63.                         ChildCacheKey,
  64.                         DateTime.Now,
  65.                         new CancellationChangeToken(cancellationTokenSource.Token));
  66.         }
  67.         public void PrintGroupDateTime()
  68.         {
  69.            if(_cache.TryGetValue(ParentCacheKey, out DateTime parentCacheDateTime))
  70.                 {
  71.                         Console.WriteLine("ParentDateTime:" + parentCacheDateTime.ToString("yyyy-MM-dd HH:mm:ss"));
  72.                 }
  73.                 else
  74.                 {
  75.                         Console.WriteLine("ParentDateTime is canceled");
  76.                 }
  77.                 if (_cache.TryGetValue(ChildCacheKey, out DateTime childCacheDateTime))
  78.                 {
  79.                         Console.WriteLine("ChildDateTime:" + childCacheDateTime.ToString("yyyy-MM-dd HH:mm:ss"));
  80.                 }
  81.                 else
  82.                 {
  83.                         Console.WriteLine("ChildDateTime is canceled");
  84.                 }
  85.         }
  86. }
复制代码
之后改造一下入口文件中的测试代码:
  1. var service = host.Services.GetRequiredService<ICacheService>();
  2. service.SetGroupDateTime();
  3. service.PrintGroupDateTime();
  4. service.PrintGroupDateTime();
  5. var cache = host.Services.GetRequiredService<IMemoryCache>();
  6. var cancellationTokenSource = cache.Get<CancellationTokenSource>(CacheService.DependentCancellationTokenSourceCacheKey);
  7. cancellationTokenSource.Cancel();
  8. service.PrintGroupDateTime();
复制代码
从控制台输出可以看得到前两次缓存获取正常,当调用 CancellationTokenSource.Cancel() 方法取消请求之后,缓存过期了。

如果使用 CancellationTokenSource,则允许将多个缓存条目作为一个组逐出。 使用上述代码中的 using 模式,在 using 范围内创建的缓存条目会继承触发器和到期设置。不过这种方式只有 using 范围的缓存项和 using 范围内使用 CancellationTokenSource 的缓存项可以构成一个组,如果范围内还有其他的缓存项,是不算在一个组内的。

如果要把多个缓存项全部纳入一个组,还可以用以下的方式:
  1. _cache.Set(ParentCacheKey,
  2.         DateTime.Now,
  3.         new CancellationChangeToken(cancellationTokenSource.Token));
  4. _cache.Set(
  5.         ChildCacheKey,
  6.         DateTime.Now,
  7.         new CancellationChangeToken(cancellationTokenSource.Token));
  8. _cache.Set(
  9.         ChildsCacheKey,
  10.         DateTime.Now,
  11.         new CancellationChangeToken(cancellationTokenSource.Token));
复制代码

2.7 一些注意事项


  • 使用回调重新填充缓存项时:
    • 多个请求可以发现缓存的键值为空,因为回调没有完成。
    • 这可能会导致多个线程重新填充缓存项。
  • 当使用一个缓存条目创建另一个缓存条目时,子条目会复制父条目的到期令牌和基于时间的到期设置。 手动删除或更新父条目时,子条目不会过期。
官方文档上还有另外几条,但是我这边在上面有讲过了,这里就不再重复了。


参考文章:
ASP.NET Core 中的内存中缓存


ASP.NET Core 系列:
目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 缓存之内存缓存(上)

来源:https://www.cnblogs.com/wewant/archive/2023/04/11/17114113.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具