前言
几乎在所有的应用程序中, 缓存都是一个永恒的话题, 恰当的使用缓存可以有效提高应用程序的性能; 在某些业务场景下, 使用缓存依赖会有很好的体验; 在 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://files.cnblogs.com/files/viter/Ron.MemoryCacheDemo.zip
来源: https://www.cnblogs.com/viter/p/10146312.html