一、前言

  .Net Core缓存源码

  1、上篇.NET Core ResponseCache【缓存篇(一)】中我们提到了使用客户端缓存、和服务端缓存。本文我们介绍MemoryCache缓存组件,说到服务端缓存我们一般都会想到MemoryCache、Redis等等优秀的缓存组件,各自有各自使用的场景。MemoryCache的类型比较单一是Object对象存储、Redis的数据类型就相对比较多 String(字符串),List(列表),set(去重集合),zset(去重排序集合),hash(哈希)。还有HyperLogLog,bitMap,GeoHash,BloomFilter这四种还没有详细了解,等下篇讲解Redis的时候详细给各位姥爷供上。

二、MemoryCache缓存组件使用

  1、首先我们需要将MemoryCache组件注入到程序中。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. //添加内存缓存
  4. services.AddMemoryCache();
  5. }

  2、使用的方法也比较简单和我们平常写代码差不多。在控制其中注入基类IMemoryCache,看到下面这个基类,我还以为会只有两个方法。

  1. #region 注入缓存
  2. private readonly IMemoryCache Cache;
  3. public MemoryCacheController(IMemoryCache cache)
  4. {
  5. Cache = cache;
  6. }
  7. #endregion

  3、当我正常使用的时候发现方法原来使用推展方法写在这里。使用起来很简单我就直接代码了。

  4、设置缓存主要是四种过期时间。

      1:永久不过期 就是老子就是不过期气死你。

       2:绝对过期时间 这个就好比我们买的安眠药,2020年07月22日过期。那你今天就要好好利用这个时间了。

      3:相对过期时间 这个好比你吃饭三个小时,肚子就呱呱叫了,相对于当前时间延长。

      4:滑动过期时间 这个就是相对过期时间的升级版,我们每隔三个小时就会肚子饿,那么我们是不是可以等到两个半小时就是吃饭,肚子就不会叫了。这样在一定时间内使用这个缓存就会在你最后使用缓存的时间上延长。

  1. //设置缓存 当我们没有指定过期时间,理论是永久的(后面会说到不理论的)
  2. Cache.Set("key", "value");
  3. Console.WriteLine(Cache.Get("key"));
  4.  
  5. //设置缓存绝对过期时间
  6. Cache.Set("key1", "value", new DateTimeOffset(new DateTime(, , )));
  7. Console.WriteLine(Cache.Get("key1"));
  8.  
  9. //设置相对过期时间
  10. Cache.Set("key2", "value", new TimeSpan(, , ));
  11. Console.WriteLine(Cache.Get("key2"));
  12.  
  13. //设置滑动过期时间
  14. Cache.Set("key3", "value", new MemoryCacheEntryOptions()
  15. {
  16. //设置滑动过期
  17. SlidingExpiration = new TimeSpan(, , ),
  18. //设置缓存的优先级,当我们缓存空间不足的时候会移除等级低的缓存,以此类推(清除的时候不会管是否过期)
  19. //Low, 低的意思
  20. //Normal, 正常模式 默认模式
  21. //High, 高
  22. //NeverRemove 绝不回收,这个就算内存满了也不会清除。
  23. Priority = CacheItemPriority.NeverRemove
  24. });
  25. Console.WriteLine(Cache.Get("key3"));

  5、这里我们还要讲一下缓存失效机制,这里它不是主动失效的,只有当我们再次对缓存MemoryCache组件进行增删查改的时候才会扫描里面的内存是否存在过期的,进行垃圾回收。这里我们使用GetOrCreate方法创建我们的缓存证实我们的说法。这个可以设置过期回调函数。图下我们可以看到当我们的缓存过期之后,就没有对缓存进行操作了就不会有回调函数触发。

  1. Cache.GetOrCreate("key4", cacheEntry =>
  2. {
  3. //设置滑动过期
  4. cacheEntry.SlidingExpiration= new TimeSpan(, , );
  5. //设置删除回调函数
  6. cacheEntry.RegisterPostEvictionCallback(CallbackFunction);
  7. //设置内存
  8. return cacheEntry.Value = "滑动过期时间带删除回调";
  9. });
  10. Thread.Sleep();
  11. Console.WriteLine(Cache.Get("key4"));
  12. Thread.Sleep();
  13. Console.WriteLine(Cache.Get("key4"));
  14. Thread.Sleep();

  6、但是我们将最后一句代码解开封印。这里有一个问题,当我们执行等待了5秒钟按道理过期了,但是没有触发删除回调,我截图上的是我将代码块拉到了之前执行的最后一行代码才有,这个我也蒙了。但是当我第二次进来访问就有回调函数了。

  1. /// <summary>
  2. /// 删除回调函数
  3. /// </summary>
  4. /// <param name="key"></param>
  5. /// <param name="value"></param>
  6. /// <param name="reason"></param>
  7. /// <param name="state"></param>
  8. public void CallbackFunction(object key, object value, EvictionReason reason, object state)
  9. {
  10. Console.WriteLine($"你被删除了key:{key},value:{value}回调函数");
  11. }

  7、CreateEntry设置缓存,有一点特殊正常设置缓存的时候,获取的时候是null,只有当我们执行Dispose或者using的时候才成功。带着这个疑问我们决定看看源码。

  1. ////CreateEntry设置缓存
  2. var entity = Cache.CreateEntry("key5");
  3. entity.Value = "";
  4. Console.WriteLine("CreateEntry 获取信息:" + Cache.Get("key5")); //结果null
  1. //方法1
  2. var entity = Cache.CreateEntry("key5");
  3. entity.Value = "";
  4. entity.Dispose();
  5. Console.WriteLine("CreateEntry 获取信息:" + Cache.Get("key5")); //结果5555
  6.  
  7. //方法2
  8. using (var entity = Cache.CreateEntry("key5"))
  9. {
  10. entity.Value = "";
  11. Console.WriteLine("CreateEntry 获取信息:" + Cache.Get("key5")); //结果5555
  12. }

  8、CacheEntry有一个重要的方法Dispose(),因为它继承IDisposable,在Dispost方法中调用了_notifyCacheEntryDisposed委托。

  1. public void Dispose()
  2. {
  3. if (!_added)
  4. {
  5. _added = true;
  6. _scope.Dispose();
  7. _notifyCacheEntryDisposed(this);//在此调用委托,而此委托是被MemoryCache类中的SetEntry赋值。目的是将CacheEntry实体放入MemoryCache类的字典中,也就是放入缓存中。
  8. PropagateOptions(CacheEntryHelper.Current);

三、MemoryCache组件源码

  1、首先看我们在Startup中声明添加的内存缓存的对应的源码。 services.AddMemoryCache();可以看到这里是将缓存组件使用单例模式注入我们程序中。

  1. /// <summary>
  2. /// Adds a non distributed in memory implementation of <see cref="IMemoryCache"/> to the
  3. /// <see cref="IServiceCollection" />.
  4. /// </summary>
  5. /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
  6. /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
  7. public static IServiceCollection AddMemoryCache(this IServiceCollection services)
  8. {
  9. if (services == null)
  10. {
  11. throw new ArgumentNullException(nameof(services));
  12. }
  13.  
  14. services.AddOptions();
  15. services.TryAdd(ServiceDescriptor.Singleton<IMemoryCache, MemoryCache>());
  16.  
  17. return services;
  18. }

  2、MemoryCacheOptions类:主要是配置一些参数

    ExpirationScanFrequency:此字段表明隔一段时间扫描缓存,移除过期缓存,默认频率为一分钟。

    SizeLimit:设置缓存的大小。

    CompactionPercentage:压缩比例 默认为百分之五。

  1. //添加内存缓存
  2. services.AddMemoryCache(x=> {
  3. x.CompactionPercentage = 0.05;//缓存压缩大小
  4. x.ExpirationScanFrequency= new TimeSpan(, , ); //默认自动扫描时间为三分钟
  5. x.SizeLimit = * * ; //大小为500M
  6. });

  3、MemoryCache类

    1:ConcurrentDictionary<object, CacheEntry> _entries:一个多线程安全的字典类型,其实缓存的本质就是这个字典,将所有缓存都放入这个字典中,然后通过字典的key(字典的key其实和缓存实体CacheEntry的key值一样)获取CacheEntry实体(CacheEntry实体包含key和value,也就是我们代码中设置的key和value)。

    2:_expirationScanFrequency:Span类型,表示扫描过期缓存的频率时间,此字段的值来自MemoryCacheOptions类的ExpirationScanFrequency。需要注意的是:假如设置为一分钟扫描一次缓存集合,这个扫描不是主动扫描的,只有当对缓存集合操作时才会扫描。比如增删改查缓存集合的时候,会判断上一次扫描的时间离现在过去多久了,如果超过扫描设置的时间,才会扫描。

  1. private void StartScanForExpiredItems()//扫描函数。在添加、查找、删除、修改缓存的时候,会调用此方法扫描过滤过期缓存,并不是主动隔一段时间执行
  2. {
  3. var now = _clock.UtcNow;
  4. if (_expirationScanFrequency < now - _lastExpirationScan)//判断现在的时间和最后一次扫描时间,是不是大于设置的时间段
  5. {
  6. _lastExpirationScan = now;
  7. Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  8. }
  9. }
  10. private static void ScanForExpiredItems(MemoryCache cache)
  11. {
  12. var now = cache._clock.UtcNow;
  13. foreach (var entry in cache._entries.Values)//然后遍历字典集合,移除过期缓存
  14. {
  15. if (entry.CheckExpired(now))
  16. {
  17. cache.RemoveEntry(entry);
  18. }
  19. }
  20. }

.Net Core缓存组件(MemoryCache)【缓存篇(二)】的更多相关文章

  1. 拥抱.NET Core系列:MemoryCache 缓存选项

    在上一篇 "拥抱.NET Core系列:MemoryCache 缓存过期" 中我们详细的了解了缓存过期相关的内容,今天我们来介绍一下 MSCache 中的 Options,由此来介 ...

  2. 拥抱.NET Core系列:MemoryCache 缓存域

    在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MSCache 中的缓存域. MSCache项目 M ...

  3. 拥抱.NET Core系列:MemoryCache 缓存域(转载)

    阅读目录 MSCache项目 缓存域 写在最后 在上一篇“<拥抱.NET Core系列:MemoryCache 缓存选项>”我们介绍了一些 MSCache 的机制,今天我们来介绍一下 MS ...

  4. 拥抱.NET Core系列:MemoryCache 缓存选项 (转载)

    阅读目录 MSCache项目 MemoryCacheOptions ExpirationScanFrequency SizeLimit CompactionPercentage 写在最后 在上一篇 ” ...

  5. .Net Core缓存组件(MemoryCache)源码解析

    一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...

  6. 拥抱.NET Core系列:MemoryCache 缓存过期

    在上一篇"拥抱.NET Core系列:MemoryCache 初识"中我们基本了解了缓存的添加.删除.获取,那么今天我们来看看缓存的过期机制.这里和上篇一样将把"Micr ...

  7. 拥抱.NET Core系列:MemoryCache 缓存过期 (转载)

    阅读目录 MSCache项目 MSCache提供的过期方式 绝对时间到期 滑动时间到期 自定义过期策略 过期策略组合拳 缓存过期回调 写在最后 在上一篇”拥抱.NET Core系列:MemoryCac ...

  8. .Net Core缓存组件(Redis)源码解析

    上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...

  9. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

随机推荐

  1. 版本控制工具 GIT入门教程

    GIT 在团队中的中作流程 1.每个程序员在自己的分支上进行开发 2.主程序猿/Leader合并程序员程序 3.程序员之间也可以对一下提交冲突进行合并 下载和安装 GIT官方网址:http:// gi ...

  2. Python3-cx_Oracle模块-数据库操作之Oracle

    模块安装 1.安装cx_Oracle模块之前必须要安装Oracle客户端,否则无法使用 2.系统上需要装有对应版本的c++编译套件(Linux下:g++ Windows下:VC++) 参考文档 htt ...

  3. Java常用的文档地址

    https://docs.oracle.com/en/ https://docs.oracle.com/en/java/javase/13/   specifications--->langua ...

  4. SpringBoot--swagger搭建、配置及使用

    一. 作用: 1. 接口的文档在线自动生成. 2. 接口测试. 二.模块介绍 Swagger是一组开源项目,其中主要要项目及功能如下: 1.Swagger Codegen: 通过Codegen 可以将 ...

  5. keras训练函数fit和fit_generator对比,图像生成器ImageDataGenerator数据增强

    1. [深度学习] Keras 如何使用fit和fit_generator https://blog.csdn.net/zwqjoy/article/details/88356094 ps:解决样本数 ...

  6. caffe训练数据流程

    cifar10训练实例 1. 下载数据 # sudo sh data/cifar10/get_cifar10.sh 2. 转换数据格式为lmdb # sudo sh examples/cifar10/ ...

  7. plsql截取字符串字段中的某个字符段

    截取字符串 字符串s=" hello world  ! (name) " 如果要截取括号中的字符串可以采取如下方法. select substr(s,instr(s,'(')+1, ...

  8. 三分钟在任何电脑上使用 chrome os

    准备 什么是 chrome os? Chrome OS是一款Google开发的基于PC的操作系统. Google Chrome OS是一款基于Linux的开源操作系统. Chrome OS 系统和 C ...

  9. 用.NET做B/S结构的系统,您是用几种结构来开发,每一层之间的关系以及为什么要这样分层?

    表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得. 业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理. 数据访问层(DAL):直 ...

  10. 《MapReduce: Simplified Data Processing on Large Clusters》论文研读

    MapReduce 论文研读 说明:本文为论文 <MapReduce: Simplified Data Processing on Large Clusters> 的个人理解,难免有理解不 ...