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的更多相关文章

  1. ASP.NET Core Authentication系列(四)基于Cookie实现多应用间单点登录(SSO)

    前言 本系列前三篇文章分别从ASP.NET Core认证的三个重要概念,到如何实现最简单的登录.注销和认证,再到如何配置Cookie 选项,来介绍如何使用ASP.NET Core认证.感兴趣的可以了解 ...

  2. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  3. ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis

    ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...

  4. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之三 —— 配置

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  5. 《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!

    写在开始 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击, 现在可以致敬伟大的.NET斗士甲骨文了 (JDK8以上都需要收费, ...

  6. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  7. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— 准备

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  8. 《闲聊 ASP.NET Core》系列直播清单

    [闲聊 ASP.NET Core]第一期:项目与应用结构 [闲聊 ASP.NET Core]第二期:Web Host 初始化与生命周期事件 [闲聊ASP.NET Core]第三期:应用程序配置 [闲聊 ...

  9. [铁人赛] ASP.NET Core 2 系列- 从头开始

    来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽说名称沿用ASP.NET,但相较于ASP.NET确有许多架构上的差异,可说是除了名 ...

  10. ASP.NET CORE CACHE的使用(含MemoryCache,Redis)

    原文:ASP.NET CORE CACHE的使用(含MemoryCache,Redis) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接 ...

随机推荐

  1. python 读取yaml文件

    简介 在实际开发过程中,我们可能需要读取一些配置文件的配置信息,例如ini.yaml.property等格式,本文将讲述怎么去获取和设置yaml文件的相关参数. 示例yaml文件 test1: tes ...

  2. mysql Insert强化

    INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [(col_name,...)] VALUES ({e ...

  3. while 循环、do- while 循环 和 for 循环之间的那点事

    C语言自学之三种循环比较 使用循环计算1-2+3-4+5-6+--100的值?    在编辑器中给出了三种循环体结构的部分代码,请选择合适的循环结构补全代码实现此功能.    运行结果为: sum=- ...

  4. 《Python高手之路 第3版》这不是一本常规意义上Python的入门书!!

    <Python高手之路 第3版>|免费下载地址 作者简介  · · · · · · Julien Danjou 具有12年从业经验的自由软件黑客.拥有多个开源社区的不同身份:Debian开 ...

  5. Spring 03 切面编程

    简介 AOP(Aspect Oriented Programming),即面向切面编程 这是对面向对象思想的一种补充. 面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能. ...

  6. 面试突击74:properties和yml有什么区别?

    properties 和 yml 都是 Spring Boot 支持的两种配置文件,它们可以看作是 Spring Boot 在不同时期的两款"产品".在 Spring Boot 时 ...

  7. 趋势科技 redirfs模块的一个小问题

    最近看的一个问题,消息队列可以创建,但是不能获取属性,也不能发消息,返回错误为:EBADF Bad file descriptor 经过打点,确认走入了这个流程: SYSCALL_DEFINE3(mq ...

  8. 从C过渡到C++——换一个视角深入数组[真的存在高效吗?](2)

    从C过渡到C++--换一个视角深入数组[真的存在高效吗?](2) C风格高效的数组遍历 在过渡到C++之前我还是想谈一谈如何书写高效的C的代码,这里的高效指的是C代码的高效,也就是在不开启编译器优化下 ...

  9. Ubuntu20.04配置 ES7.17.0集群

    Ubuntu20.04配置 ES7.17.0集群 ES能做什么? elasticsearch简写es,es是一个高扩展.开源的全文检索和分析引擎,它可以准实时地快速存储.搜索.分析海量的数据. Ubu ...

  10. 持久化-DLL劫持

    持久化-DLL劫持 原理 通过篡改.替换和代理原dll文件来达到劫持. 原理演示 假定演练流程 假定我们要劫持的目标是 c:\temp\legit.dll 获取 c:\temp\legit.dll 所 ...