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版权协议,转载请附上原文出处链接和本声明. 本文链接 ...
随机推荐
- python 读取yaml文件
简介 在实际开发过程中,我们可能需要读取一些配置文件的配置信息,例如ini.yaml.property等格式,本文将讲述怎么去获取和设置yaml文件的相关参数. 示例yaml文件 test1: tes ...
- mysql Insert强化
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [(col_name,...)] VALUES ({e ...
- while 循环、do- while 循环 和 for 循环之间的那点事
C语言自学之三种循环比较 使用循环计算1-2+3-4+5-6+--100的值? 在编辑器中给出了三种循环体结构的部分代码,请选择合适的循环结构补全代码实现此功能. 运行结果为: sum=- ...
- 《Python高手之路 第3版》这不是一本常规意义上Python的入门书!!
<Python高手之路 第3版>|免费下载地址 作者简介 · · · · · · Julien Danjou 具有12年从业经验的自由软件黑客.拥有多个开源社区的不同身份:Debian开 ...
- Spring 03 切面编程
简介 AOP(Aspect Oriented Programming),即面向切面编程 这是对面向对象思想的一种补充. 面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能. ...
- 面试突击74:properties和yml有什么区别?
properties 和 yml 都是 Spring Boot 支持的两种配置文件,它们可以看作是 Spring Boot 在不同时期的两款"产品".在 Spring Boot 时 ...
- 趋势科技 redirfs模块的一个小问题
最近看的一个问题,消息队列可以创建,但是不能获取属性,也不能发消息,返回错误为:EBADF Bad file descriptor 经过打点,确认走入了这个流程: SYSCALL_DEFINE3(mq ...
- 从C过渡到C++——换一个视角深入数组[真的存在高效吗?](2)
从C过渡到C++--换一个视角深入数组[真的存在高效吗?](2) C风格高效的数组遍历 在过渡到C++之前我还是想谈一谈如何书写高效的C的代码,这里的高效指的是C代码的高效,也就是在不开启编译器优化下 ...
- Ubuntu20.04配置 ES7.17.0集群
Ubuntu20.04配置 ES7.17.0集群 ES能做什么? elasticsearch简写es,es是一个高扩展.开源的全文检索和分析引擎,它可以准实时地快速存储.搜索.分析海量的数据. Ubu ...
- 持久化-DLL劫持
持久化-DLL劫持 原理 通过篡改.替换和代理原dll文件来达到劫持. 原理演示 假定演练流程 假定我们要劫持的目标是 c:\temp\legit.dll 获取 c:\temp\legit.dll 所 ...