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. Python3的原生协程(Async/Await)和Tornado异步非阻塞

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_113 我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决 ...

  2. Odoo14 一些好用的开源的模块

    # odoo14中一些好用的开源的模块 1.intero_reload_form 刷新按钮(页面数据刷新,而不是按F5刷新整个页面) 2.ms_magic_button 弹框下拉选项 3.sessio ...

  3. 使用.NET简单实现一个Redis的高性能克隆版(一)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  4. 不会提交 PR 的小伙伴看过来,超详细的视频教程!

    点击上方 蓝字关注我们 作者 | 严天奇 ✎ 编 者 按 最近有一些新加入社区的朋友反馈不太了解 Apache DolphinScheduler 提交 PR 的步骤和规则.这不,人帅心美的严天奇同学就 ...

  5. 来看看这位年轻的 eBay 小伙是如何成为 Committer

    介绍一下我自己 目前就职于eBay中国,专注于微服务中间件,分布式架构等领域,同时也是狂热的开源爱好者. 如何成为一个commiter 过去几个月,我一直持续在为 Apache DolphinSche ...

  6. 如何在win下安装dlib的whl文件(Anaconda方式)

    问题描述 由于作业需要用到dlib的人脸检测函数,所以尝试安装了一下dlib.顺便贴上dlib的下载网址dlib下载. 但当我直接输入pip install dlib-19.7.0-cp36-cp36 ...

  7. Rust 从入门到精通05-数据类型

    Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型. 在 Rust 中,每一个值都属于某一个 数据类型(data type),分为两大类: ①.标 ...

  8. JavaScript的入门

    书写的三种方式 1. 书写在script标签里面(一般会用到的) 2.书写在js文件里面(推荐)定义一个js文件(xxx.js) 3. 书写对应的事件属性里面(比较少用) 初体验 1. 目前js的代码 ...

  9. JavaScript 基础知识(二):闭包

    首先来思考一下下面的案例: function unclosure() { let count = 0 return count++ } for (let index = 0; index < 1 ...

  10. Vue3中插槽(slot)用法汇总

    Vue中的插槽相信使用过Vue的小伙伴或多或少的都用过,但是你是否了解它全部用法呢?本篇文章就为大家带来Vue3中插槽的全部用法来帮助大家查漏补缺. 什么是插槽 简单来说就是子组件中的提供给父组件使用 ...