前言

    几乎在所有的应用程序中,缓存都是一个永恒的话题,恰当的使用缓存可以有效提高应用程序的性能;在某些业务场景下,使用缓存依赖会有很好的体验;在 Asp.Net Core 中,支持了多种缓存组件,这其中最基础也最易用的当属 IMemoryCache,该接口表示其存储依赖于托管程序服务器的内存,下面要介绍的内容就是基于 IMemoryCache 的缓存依赖。

1. IMemoryCache 的实现

Asp.Net Core 内部实现了一个继承自 IMemoryCache 接口的类 MemoryCache

这几乎已成惯例,一旦某个接口被列入 SDK 中,其必然包含了一个默认实现

1.1 使用 IMemoryCache

在 Asp.Net Core 中要使用 IMemoryCache 非常简单,只需要在 Startup 的 ConfigureServices 方法加入一句代码 services.AddMemoryCache() 即可

    public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
...
}
1.2 在控制器中使用 IMemoryCache
    [Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
private IMemoryCache cache;
public HomeController(IMemoryCache cache)
{
this.cache = cache;
} [HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
cache.Set("userId", "0001");
return new string[] { "value1", "value2" };
} [HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return cache.Get<string>("userId");
}
}

上面的代码表示在 HomeController 控制器的构造方法中使用注入的方式获得了一个 IMemoryCache 对象,在 Get() 方法中增加了一条缓存记录 "userId=0001",然后在 Get(int id) 接口中提取该缓存记录

运行程序,分别调用 Get() 和 Get(int id) 接口,获得下面的输出信息

  • 调用 Get() 接口

  • 调用 Get(int id) 接口

这看起来非常容易,几乎不用什么思考,你就学会了在 Asp.Net Core 中使用缓存,容易使用,这非常重要,这也是一门语言广泛推广的根本态度

2. 应用缓存策略

IMemoryCache 还包含了一个带参数的构造方法,让我们可以对缓存进行灵活的配置,该配置由类 MemoryCacheOptions 决定

2.1 MemoryCacheOptions 配置,MemoryCacheOptions的配置项目不多,看下面的代码
    public class MemoryCacheOptions : IOptions<MemoryCacheOptions>
{
public MemoryCacheOptions(); public ISystemClock Clock { get; set; } [Obsolete("This is obsolete and will be removed in a future version.")]
public bool CompactOnMemoryPressure { get; set; } public TimeSpan ExpirationScanFrequency { get; set; } public long? SizeLimit { get; set; } public double CompactionPercentage { get; set; }
}
  • ISystemClock:系统时钟,默认值为 null,官方文档对此属性没有说明,我也不知道是干什么用的,哪位大神求告知其作用和原理
  • ExpirationScanFrequency:对过期缓存的扫描间隔时间
  • SizeLimit:缓存区可存储记录条目数量
  • CompactionPercentage:在缓存过期策略生效的时候,对缓存进行压缩的百分比

上面的这个配置非常简单,在系统中应用类似下面的代码这样

 public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache(options =>
{
options.CompactionPercentage = 0.02d;
options.ExpirationScanFrequency = TimeSpan.FromMinutes(5);
options.SizeLimit = 1024;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

上面的缓存策略设置为缓存压缩比为 2%,每 5 分钟进行一次过期缓存的扫描,最大缓存空间大小限制为 1024

使用方法不变

2.1 单个键缓存策略

由于缓存的所有键其缓存过期优先级都是默认的 Normal,可能我们需要在某些业务场景下,让某些缓存值设置一个较高的优先级,比如设置永远都不过期,这样即使缓存达到最大限制条数以后也不会对其进行清理

  • 缓存优先级,该值为一个枚举类型,分别是 低、普通、高、永不移除,开发者可以根据不同的业务场景灵活设置
    public enum CacheItemPriority
{
Low = 0,
Normal = 1,
High = 2,
NeverRemove = 3
}
  • 设置策略,下面就使用 MemoryCacheEntryOptions 对单个键值进行应用策略
        [HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
MemoryCacheEntryOptions entry = new MemoryCacheEntryOptions
{
Priority = CacheItemPriority.NeverRemove
};
cache.Set("userId", "0001", entry); return new string[] { "value1", "value2" };
}

上面的代码表示,我们对缓存键 "userId" 应用了一个 “永不移除” 的策略,当然,还可以对单个值做非常多的策略,比如现在 "userId" 的值大小等等,有兴趣的同学可以深入了解 MemoryCacheEntryOptions 类

3. 使用缓存依赖策略

缓存依赖的意思是表示,一个或者多个缓存依赖于某个缓存,当某个缓存过期的时候,对其有依赖条件的其它缓存也会过期,在某些应用场景下,缓存依赖非常有用

3.1 创建 TokenController 并登录后注册依赖、获取缓存、移除缓存接口

以下示例使用一个模拟用户登录/登出的业务场景

    [Route("api/[controller]")]
[ApiController]
public class TokenController : ControllerBase
{
private IMemoryCache cache;
public TokenController(IMemoryCache cache)
{
this.cache = cache;
} // 创建注册依赖
[HttpGet("login")]
public ActionResult<string> Login()
{
var cts = new CancellationTokenSource();
cache.Set(CacheKeys.DependentCTS, cts);
using (var entry = cache.CreateEntry(CacheKeys.UserSession))
{
entry.Value = "_x0123456789";
entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);
cache.Set(CacheKeys.UserShareData, "这里是共享的数据", new CancellationChangeToken(cts.Token));
cache.Set(CacheKeys.UserCart, "这里是购物车", new CancellationChangeToken(cts.Token));
}
return "设置依赖完成";
} // 获取缓存
[HttpPost("getkeys")]
public IActionResult GetKeys()
{
var userInfo = new
{
UserSession = cache.Get<string>(CacheKeys.UserSession),
UserShareData = cache.Get<string>(CacheKeys.UserShareData),
UserCart = cache.Get<string>(CacheKeys.UserCart)
}; return new JsonResult(userInfo);
} // 移除缓存
[HttpPost("logout")]
public ActionResult<string> LogOut()
{
cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel(); var userInfo = new
{
UserSession = cache.Get<string>(CacheKeys.UserSession),
UserShareData = cache.Get<string>(CacheKeys.UserShareData),
UserCart = cache.Get<string>(CacheKeys.UserCart)
}; return new JsonResult(userInfo);
} // 过期通知
private static void DependentEvictionCallback(object key, object value, EvictionReason reason, object state)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Key:{0} 已过期,依赖于该 Key 的所有缓存都将过期而处于不可用状态", key);
Console.ForegroundColor = ConsoleColor.Gray;
}
}

上面的代码使用 CancellationTokenSource 用作事件通知源,当移除 CacheKeys.DependentCTS 并触发 CancellationTokenSource.Cancel() 方法后,将异步触发 DependentEvictionCallback(object key, object value, EvictionReason reason, object state)委托;此时,托管程序收到一个通知,用户已登出,已移除用户相关缓存,任何移除接口尝试再次读取 CacheKeys 项,此时,返回值为空

3.2 运行程序,分别调用 login/getkeys/logout 接口,分别得到以下输出结果
  • login 登录后注册依赖

  • getkeys 获取缓存

  • logout 移除缓存,尝试再次读取 CacheKeys 项,此时,返回值为空

  • 控制台输出移除通知(黄色字体部分信息)

可以看到,在用户登录登出这个业务场景下,使用缓存依赖项对其相关缓存进行管理,还是非常方便的,当用户退出登录后,即清空其所有相关缓存

结束语

  • 本文通过实例介绍了 IMemoryCache 的简单使用方法
  • 针对单个缓存键,也可以对其进行应用策略
  • 通过使用缓存依赖策略,可以在某些业务场景中有非常好的应用体验
  • 注意:当使用全局缓存策略 SizeLimit 时,每个键都需要设置一个大小
  • IMemoryCache 依赖于托管服务器等内存,一旦重启,缓存数据将立即被释放

示例代码下载

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.MemoryCacheDemo

Asp.Net Core 轻松学-在.Net Core 使用缓存和配置依赖策略的更多相关文章

  1. Asp.Net Core 轻松学-在.Net Core 中使用钩子

    前言     Host startup hook,是2.2中提供的一项新的功能,通过使用主机启动钩子,允许开发人员在不修改代码的情况下,在服务启动之前注入代码:通过使用钩子,可以对已部署好的服务在服务 ...

  2. Asp.Net Core 轻松学系列-1阅读指引目录

    https://www.cnblogs.com/viter/p/10474091.html 目录 前言 1. 从安装到配置 2. 业务实现 3. 日志 4. 测试 5. 缓存使用 6.网络和通讯 7. ...

  3. Asp.Net Core 轻松学-正确使用分布式缓存

    前言     本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了.上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,那么本篇文章就来了解一下如何使用分 ...

  4. Asp.Net Core 轻松学-使用MariaDB/MySql/PostgreSQL和支持多个上下文对象

    前言 在上一篇文章中(Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库)[https://www.cnblogs.com/viter/p/10243577.html],介 ...

  5. Asp.Net Core 轻松学-多线程之Task(补充)

    前言     在上一章 Asp.Net Core 轻松学-多线程之Task快速上手 文章中,介绍了使用Task的各种常用场景,但是感觉有部分内容还没有完善,在这里补充一下. 1. 任务的等待 在使用 ...

  6. Asp.Net Core 轻松学-利用文件监视进行快速测试开发

    前言     在进行 Asp.Net Core 应用程序开发过程中,通常的做法是先把业务代码开发完成,然后建立单元测试,最后进入本地系统集成测试:在这个过程中,程序员的大部分时间几乎都花费在开发.运行 ...

  7. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  8. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  9. C# 中一些类关系的判定方法 C#中关于增强类功能的几种方式 Asp.Net Core 轻松学-多线程之取消令牌

    1.  IsAssignableFrom实例方法 判断一个类或者接口是否继承自另一个指定的类或者接口. public interface IAnimal { } public interface ID ...

随机推荐

  1. too many open files linux服务器 golang java

    1. 现象 服务的cpu跑满(golang实现), 并大量报too many open files错误.服务使用systemd来运行,部署在阿里ecs上. 2.分析 从日志来看,cpu的上升主要为到达 ...

  2. 解锁 vmware esxi 6.7 并安装 mac os 10.13

    1.安装 esxi 6.7 2.下载 unlocker 2.1.1.zip 3.上传 unlocker 2.1.1.zip esxi的磁盘中 4.开启esxi的ssh登录 5.使用 ssh 登录 es ...

  3. bzoj 2653 middle 二分答案 主席树判定

    判断中位数是否可行需要将当前的解作为分界,大于其的置为1,小于为-1,然后b-c必选,ab,cd可不选,这个用线段树判定就好 但不能每次跑,所以套主席树,按权值排序,构建主席树,更新时将上一个节点改为 ...

  4. BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树

    BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树 题意: 给定n个点,m条双向边的图.其中有k个点是重要的.每条边都有一定的长度. 现在要你选定一些边来构成一个图,要使得k个重要的 ...

  5. Django web框架开发基础-django实现留言板功能

    1.创建项目 cmd  django-admin startpoject cloudms 2.创建APP cmd django-admin startapp msgapp 3.修改settings,T ...

  6. 挑子学习笔记:DBSCAN算法的python实现

    转载请标明出处:https://www.cnblogs.com/tiaozistudy/p/dbscan_algorithm.html DBSCAN(Density-Based Spatial Clu ...

  7. 基于 HTML5 的 WebGL 3D 版俄罗斯方块

    前言 摘要:2D 的俄罗斯方块已经被人玩烂了,突发奇想就做了个 3D 的游戏机,用来玩俄罗斯方块...实现的基本想法是先在 2D 上实现俄罗斯方块小游戏,然后使用 3D 建模功能创建一个 3D 街机模 ...

  8. Android 8.1 源码_启动篇(二) -- 深入研究 zygote(转 Android 9.0 分析)

    前言 在Android中,zygote是整个系统创建新进程的核心进程.zygote进程在内部会先启动Dalvik虚拟机,继而加载一些必要的系统资源和系统类,最后进入一种监听状态.在之后的运作中,当其他 ...

  9. c#批量抓取免费代理并验证有效性

    之前看到某公司的官网的文章的浏览量刷新一次网页就会增加一次,给人的感觉不太好,一个公司的官网给人如此直白的漏洞,我批量发起请求的时候发现页面打开都报错,100多人的公司的官网文章刷新一次你给我看这个, ...

  10. Service Fabric service 根据环境变量读取配置文件

    前言 一个服务或者产品,往往需要三个环境:一个开发环境(Development),一个测试环境(Staging),一个生产环境(Production), 这就不可避免的需要多个配置文件来匹配相应的环境 ...