ASP.NET Core :缓存系列(四):内存缓存 MemoryCache
System.Runtime.Caching/MemoryCache
ICacheEntry 接口中的属性:具体设置过期时间 可以参考:微软文档ICacheEntry 接口
缓存基本使用 (一) 绝对过期
AbsoluteExpirationRelativeToNow 绝对过期
在实际的使用中肯定是要使用 Id 之类的进行拼接
var items = await memCache.GetOrCreateAsync("Test1", async (e) => {
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
logger.LogInformation("从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});
缓存基本使用 (二) 设置一个滑动过期
SlidingExpiration 滑动过期
注意如果这个缓存一直被访问,一直不会过期,长时间会导致数据不一致问题!
var items = await memCache.GetOrCreateAsync("Test2", async (e) => {
e.SlidingExpiration = TimeSpan.FromSeconds(10);
logger.LogInformation("Demo2从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});
缓存基本使用 (三)混合使用过期策略
比如这个接口访问比较频繁,我们可以使用滑动过期和绝对过期结合使用
注意:绝对过期时间一定要大于滑动过期时间
var items = await memCache.GetOrCreateAsync("Test3", async (e) => {
e.SlidingExpiration = TimeSpan.FromSeconds(10);
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
logger.LogInformation("Demo3从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});
在设置一个滑动过期的时候 我发现了一个问题,就是random 随机数不支持 Double 导致时间无法精确,而且如果int 类型 缓存过多 生成的 随机数可能会导致重复,造成缓存雪崩
这里我们自己扩展一个随机Double传入(minValue,maxValue)
public static class RondowExtensions
{
public static double NextDouble(this Random random, double minValue, double maxValue)
{
if (minValue >= maxValue) throw new ArgumentOutOfRangeException(nameof(minValue), "minValue annot be bigger than maxValue");
double x = random.NextDouble();
return x * maxValue + (1 - x) * minValue;
}
}
interface IMemoryCacheHelper
public interface IMemoryCacheHelper
{
/// <summary>
/// 从缓存中获取数据,如果缓存中没有数据,则调用valueFactory获取数据。
/// 可以用AOP+Attribute的方式来修饰到Service接口中实现缓存,更加优美,但是没有这种方式更灵活。
/// 默认最长的缓存过期时间是expireSeconds秒,当然也可以在领域事件的Handler中调用Update更新缓存,或者调用Remove删除缓存。
/// 因为IMemoryCache会把null当成合法的值,因此不会有缓存穿透的问题,但是还是建议用我这里封装的ICacheHelper,原因如下:
/// 1)可以切换别的实现类,比如可以保存到MemCached、Redis等地方。这样可以隔离变化。
/// 2)IMemoryCache的valueFactory用起来麻烦,还要单独声明一个ICacheEntry参数,大部分时间用不到这个参数。
/// 3)这里把expireSeconds加上了一个随机偏差,这样可以避免短时间内同样的请求集中过期导致“缓存雪崩”的问题
/// 4)这里加入了缓存数据的类型不能是IEnumerable、IQueryable等类型的限制
/// </summary>
/// <typeparam name="TResult">缓存的值的类型</typeparam>
/// <param name="cacheKey">缓存的key</param>
/// <param name="valueFactory">提供数据的委托</param>
/// <param name="expireSeconds">缓存过期秒数的最大值,实际缓存时间是在[expireSeconds,expireSeconds*2)之间,这样可以一定程度上避免大批key集中过期导致的“缓存雪崩”
/// 的问题</param>
/// <returns></returns>
TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int expireSeconds = 60);
Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int expireSeconds = 60);
/// <summary>
/// 删除缓存的值
/// </summary>
/// <param name="cacheKey"></param>
void Remove(string cacheKey);
}
MemoryCacheHelper
/// <summary>
/// 用ASP.NET的IMemoryCache实现的内存缓存
/// </summary>
public class MemoryCacheHelper : IMemoryCacheHelper
{
private readonly IMemoryCache memoryCache;
public MemoryCacheHelper(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
private static void ValidateValueType<TResult>()
{
//因为IEnumerable、IQueryable等有延迟执行的问题,造成麻烦,因此禁止用这些类型
Type typeResult = typeof(TResult);
if (typeResult.IsGenericType)//如果是IEnumerable<String>这样的泛型类型,则把String这样的具体类型信息去掉,再比较
{
typeResult = typeResult.GetGenericTypeDefinition();
}
//注意用相等比较,不要用IsAssignableTo
if (typeResult == typeof(IEnumerable<>) || typeResult == typeof(IEnumerable)
|| typeResult == typeof(IAsyncEnumerable<TResult>)
|| typeResult == typeof(IQueryable<TResult>) || typeResult == typeof(IQueryable))
{
throw new InvalidOperationException($"TResult of {typeResult} is not allowed, please use List<T> or T[] instead.");
}
}
private static void InitCacheEntry(ICacheEntry entry, int baseExpireSeconds)
{
//过期时间.Random.Shared 是.NET6新增的
Random.Shared.Next(1.1, 1.0);
double sec = Random.Shared.Next(baseExpireSeconds, baseExpireSeconds * 2);
TimeSpan expiration = TimeSpan.FromSeconds(sec);
entry.AbsoluteExpirationRelativeToNow = expiration;
}
public TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int baseExpireSeconds = 60)
{
ValidateValueType<TResult>();
//因为IMemoryCache保存的是一个CacheEntry,所以null值也认为是合法的,因此返回null不会有“缓存穿透”的问题
//不调用系统内置的CacheExtensions.GetOrCreate,而是直接用GetOrCreate的代码,这样免得包装一次委托
if (!memoryCache.TryGetValue(cacheKey, out TResult result))
{
using ICacheEntry entry = memoryCache.CreateEntry(cacheKey);
InitCacheEntry(entry, baseExpireSeconds);
result = valueFactory(entry)!;
entry.Value = result;
}
return result;
}
public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int baseExpireSeconds = 60)
{
ValidateValueType<TResult>();
if (!memoryCache.TryGetValue(cacheKey, out TResult result))
{
using ICacheEntry entry = memoryCache.CreateEntry(cacheKey);
InitCacheEntry(entry, baseExpireSeconds);
result = (await valueFactory(entry))!;
entry.Value = result;
}
return result;
}
public void Remove(string cacheKey)
{
memoryCache.Remove(cacheKey);
}
}
ASP.NET Core :缓存系列(四):内存缓存 MemoryCache的更多相关文章
- ASP.NET Core Authentication系列(四)基于Cookie实现多应用间单点登录(SSO)
前言 本系列前三篇文章分别从ASP.NET Core认证的三个重要概念,到如何实现最简单的登录.注销和认证,再到如何配置Cookie 选项,来介绍如何使用ASP.NET Core认证.感兴趣的可以了解 ...
- ASP.NET Core 中间件之压缩、缓存
前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...
- ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis
ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...
- 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之三 —— 配置
==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...
- 《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!
写在开始 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击, 现在可以致敬伟大的.NET斗士甲骨文了 (JDK8以上都需要收费, ...
- 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇
==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...
- 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— 准备
==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...
- 《闲聊 ASP.NET Core》系列直播清单
[闲聊 ASP.NET Core]第一期:项目与应用结构 [闲聊 ASP.NET Core]第二期:Web Host 初始化与生命周期事件 [闲聊ASP.NET Core]第三期:应用程序配置 [闲聊 ...
- [铁人赛] ASP.NET Core 2 系列- 从头开始
来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽说名称沿用ASP.NET,但相较于ASP.NET确有许多架构上的差异,可说是除了名 ...
- ASP.NET CORE CACHE的使用(含MemoryCache,Redis)
原文:ASP.NET CORE CACHE的使用(含MemoryCache,Redis) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接 ...
随机推荐
- 封装Fraction-分数类(C++)
Fraction 分数类 默认假分数,可自行修改 由于concept的原因 template <typename T> concept is_float_v = std::is_float ...
- 最新MongoDB安装,学习笔记
MongoDB 导读 作者还在陆续更新中,如果喜欢作者的笔记,觉得可以学习到有帮助,后面会不断学习新内容,就点个关注吧,如果觉得文章有关注可以点个赞,谢谢: 官网:https://www.mongod ...
- Vnc自动登录器-多国语言绿色版
推荐:介绍一个VNC连接工具:iis7服务器管理工具.IIs7服务器管理工具可以批量连接并管理VNC服务器.作为服务器集成管理器,它最优秀的功能就是批量管理windows与linux系统服务器.vps ...
- day19--Java集合02
Java集合02 6.ArrayList ArrayList的注意事项: Permits all element , including null ,ArrayList 可以加入null ,并且可以加 ...
- Luogu P3273 [SCOI2011]棘手的操作(左偏树)
什么恶心东西啊,又水又烦 两个可并堆维护即可 #include <cstdio> #include <iostream> #include <cstring> #i ...
- ESP32与MicroPython入门-01 搭建开发环境
ESP32简介 ESP32 是上海乐鑫公司开发的一款比较新的32位微控制器,它集成了WiFi及蓝牙等功能,有着性能稳定.功耗低.价格低廉等特点,非常适用于物联网开发,但也可以作为普通的MCU使用. E ...
- Vue 内联样式
前置说明 Vue 绑定HTML 全局属性style,可以动态地改变属性值.这里就不讲内联样式的基础了,具体轻查看官网文档 Class 与 Style 绑定. 主要分为以下两个步骤进行: v-bind ...
- 大数据Hadoop入门教程 | (二)Linux
使用finalShell可以提供文件目录图形化 完整Linux命令整理参考大佬博客:Linux常见文件管理命令 - Mr_Walker - 博客园 Linux文件系统基础知识 Linux文件系统概念 ...
- C#.NET ORM FreeSql 读取使用 US7ASCII 的 Oracle 数据库中文显示乱码问题
前言 关于 Oracle US7ASCII 中文乱码的问题,Ado.Net 和 Odbc 无法解决.包括最新的.Net Core..NET6..NET7 都无法解决这个问题. FreeSql 对 Or ...
- 【NOI P模拟赛】混凝土粉末(整体二分)
题面 样例输入 5 8 1 1 4 2 2 3 1 2 3 3 1 2 5 1 2 3 3 2 5 2 2 1 2 2 1 3 样例输出 1 0 4 0 1 0 样例解释 题解 比这道题简单了不知多少 ...