当前位置: 首页 > news >正文

.Net Core中的内存缓存实现——Redis及MemoryCache(2个可选)方案的实现

ASP.NET Core 支持多个不同的缓存。 最简单的缓存基于 IMemoryCache。 IMemoryCache 表示存储在 Web 服务器内存中的缓存。 在服务器场(多个服务器)中运行的应用应确保在使用内存中缓存时会话是粘滞的。 粘滞会话可确保来自客户端的请求都转到同一服务器。 例如,Azure Web 应用使用应用程序请求路由 (ARR) 将所有请求路由到同一服务器。Web 场中的非粘滞会话需要分布式缓存(如 Redis)来避免缓存一致性问题。

.NET 中有两个MemoryCache类:

  • System.Runtime.Caching.MemoryCache   
    已非推荐, .NET Standard 2.0 或更高版本。使用场景比如:将代码从 ASP.NET 4.x 移植到 ASP.NET Core 时,使用 System.Runtime.Caching/MemoryCache 作为兼容性桥。
  • Microsoft.Extensions.Caching.Memory.MemoryCache   
    推荐使用,因为它更好地集成到 ASP.NET Core 中。 MemoryCache实现IMemoryCache

引自官方说明:ASP.NET Core 中的内存中缓存 | Microsoft Learn

.NET Core中使用MemoryCache

调用自带的AddMemoryCache扩展方法,将IMemoryCache注入容器中,后面便可通过容器获取IMemoryCache的实现MemoryCache对本机内存缓存进行操纵

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();   //会注入IMemoryCache的实现类MemoryCache
using IHost host = builder.Build();

封装内存缓存工具类 

以下给出代码,通过对 ICacheTool接口 的两种实现,分别封装“本地MemoryCache”和“分布式Redis”两种方案的内存缓存实现,项目中根据需要选择一种作为 ICacheTool 的实现注入依赖容器即可。

1. MemoryCacheTool:

using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ZhonTai.Common.Extensions;namespace ZhonTai.Admin.Tools.Cache;/// <summary>
/// 内存缓存
/// </summary>
public partial class MemoryCacheTool : ICacheTool
{private static readonly string PatternRegex = @"\{.*\}";private readonly IMemoryCache _memoryCache;public MemoryCacheTool(IMemoryCache memoryCache){_memoryCache = memoryCache;}public List<string> Keys => GetAllKeys();public long Del(params string[] key){foreach (var k in key){_memoryCache.Remove(k);}return key.Length;}public Task<long> DelAsync(params string[] key){foreach (var k in key){_memoryCache.Remove(k);}return Task.FromResult(key.Length.ToLong());}public async Task<long> DelByPatternAsync(string pattern){if (pattern.IsNull())return default;pattern = Regex.Replace(pattern, PatternRegex, "(.*)");var keys = GetAllKeys().Where(k => Regex.IsMatch(k, pattern));if (keys != null && keys.Count() > 0){return await DelAsync(keys.ToArray());}return default;}public bool Exists(string key){return _memoryCache.TryGetValue(key, out _);}public Task<bool> ExistsAsync(string key){return Task.FromResult(_memoryCache.TryGetValue(key, out _));}public string Get(string key){return _memoryCache.Get(key)?.ToString();}public T Get<T>(string key){return _memoryCache.Get<T>(key);}public Task<string> GetAsync(string key){return Task.FromResult(Get(key));}public Task<T> GetAsync<T>(string key){return Task.FromResult(Get<T>(key));}public void Set(string key, object value){_memoryCache.Set(key, value);}public void Set(string key, object value, TimeSpan expire){_memoryCache.Set(key, value, expire);}public Task SetAsync(string key, object value, TimeSpan? expire = null){if(expire.HasValue){Set(key, value, expire.Value);}else{Set(key, value);}return Task.CompletedTask;}public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> func, TimeSpan? expire = null){if (await ExistsAsync(key)){try{return await GetAsync<T>(key);}catch{await DelAsync(key);}}var result = await func.Invoke();if (expire.HasValue){await SetAsync(key, result, expire.Value);}else{await SetAsync(key, result);}return result;}private List<string> GetAllKeys(){const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;var coherentState = _memoryCache.GetType().GetField("_coherentState", flags).GetValue(_memoryCache);var entries = coherentState.GetType().GetField("_entries", flags).GetValue(coherentState);var cacheItems = entries as IDictionary;var keys = new List<string>();if (cacheItems == null) return keys;foreach (DictionaryEntry cacheItem in cacheItems){keys.Add(cacheItem.Key.ToString());}return keys;}public List<string> GetKeysByPattern(string pattern){return GetAllKeys().Where(k => Regex.IsMatch(k, pattern)).ToList();}
}

2. RedisCacheTool: 

using FreeRedis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ZhonTai.Common.Extensions;namespace ZhonTai.Admin.Tools.Cache;/// <summary>
/// Redis缓存
/// </summary>
public partial class RedisCacheTool : ICacheTool
{private static readonly string PatternRegex = @"\{.*\}";private readonly RedisClient _redisClient;public List<string> Keys => _redisClient.Keys("*").ToList();public RedisCacheTool(RedisClient redisClient){_redisClient = redisClient;}public long Del(params string[] key){return _redisClient.Del(key);}public Task<long> DelAsync(params string[] key){return _redisClient.DelAsync(key);}public async Task<long> DelByPatternAsync(string pattern){if (pattern.IsNull())return default;pattern = Regex.Replace(pattern, PatternRegex, "*");var keys = await _redisClient.KeysAsync(pattern);if (keys != null && keys.Length > 0){return await _redisClient.DelAsync(keys);}return default;}public bool Exists(string key){return _redisClient.Exists(key);}public Task<bool> ExistsAsync(string key){return _redisClient.ExistsAsync(key);}public string Get(string key){return _redisClient.Get(key);}public T Get<T>(string key){return _redisClient.Get<T>(key);}public Task<string> GetAsync(string key){return _redisClient.GetAsync(key);}public Task<T> GetAsync<T>(string key){return _redisClient.GetAsync<T>(key);}public void Set(string key, object value){_redisClient.Set(key, value);}public void Set(string key, object value, TimeSpan expire){_redisClient.Set(key, value, expire);}public Task SetAsync(string key, object value, TimeSpan? expire = null){return _redisClient.SetAsync(key, value, expire.HasValue ? expire.Value.TotalSeconds.ToInt() : 0);}public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> func, TimeSpan? expire = null){if (await _redisClient.ExistsAsync(key)){try{return await _redisClient.GetAsync<T>(key);}catch{await _redisClient.DelAsync(key);}}var result = await func.Invoke();await _redisClient.SetAsync(key, result, expire.HasValue ? expire.Value.TotalSeconds.ToInt() : 0);return result;}public List<string> GetKeysByPattern(string pattern){return _redisClient.Keys(pattern).ToList();}
}

3. 根据配置选择其中一种内存缓存方案,作为ICacheTool接口的实现注入容器

...//缓存操作类相关注册
var cacheConfig = AppInfo.GetOptions<CacheConfig>(); //获取缓存相关配置//调用自带的AddMemoryCache扩展方法,将IMemoryCache注入容器(用于MemoryCache作为内存缓存的方案)
services.AddMemoryCache();if (cacheConfig.Type == CacheType.Redis)
{//【要使用Redis作为内存缓存的实现方案】//FreeRedis客户端var redis = new RedisClient(cacheConfig.Redis.ConnectionString){Serialize = JsonConvert.SerializeObject,Deserialize = JsonConvert.DeserializeObject};services.AddSingleton(redis);services.AddSingleton<IRedisClient>(redis);//Redis缓存services.AddSingleton<ICacheTool, RedisCacheTool>();//分布式Redis缓存services.AddSingleton<IDistributedCache>(new DistributedCache(redis));if(_hostAppOptions?.ConfigureIdGenerator != null){_hostAppOptions?.ConfigureIdGenerator?.Invoke(appConfig.IdGenerator);YitIdHelper.SetIdGenerator(appConfig.IdGenerator);}else{//分布式Id生成器services.AddIdGenerator();}
}
else
{//【要使用MemoryCache作为内存缓存的实现方案】//内存缓存services.AddSingleton<ICacheTool, MemoryCacheTool>();//分布式内存缓存services.AddDistributedMemoryCache();//Id生成器_hostAppOptions?.ConfigureIdGenerator?.Invoke(appConfig.IdGenerator);YitIdHelper.SetIdGenerator(appConfig.IdGenerator);
}

4. CacheConfig参考配置信息:

{"CacheConfig": {//缓存类型 Memory = 0,Redis = 1"type": "Memory",//限流缓存类型 Memory = 0,Redis = 1"typeRateLimit": "Memory",//Redis配置"redis": {//连接字符串"connectionString": "127.0.0.1:6379,password=,defaultDatabase=1",//限流连接字符串"connectionStringRateLimit": "127.0.0.1:6379,password=,defaultDatabase=1"}}
}

相关知识记录:

封装System.Runtime.Caching.MemoryCache实现服务端缓存,以及在依赖注入中使用时要注意的坑-CSDN博客

相关文章:

  • 请你谈谈:spring bean的生命周期 - 阶段2:Bean实例化阶段
  • 【PostgreSQL】PostgreSQL 教程
  • 【python虚拟环境管理】【mac m3】 使用pipx安装poetry
  • ASP.NET第七章 --案例1
  • 关闭Ubuntu烦人的apport
  • rust编译安卓各个平台so库
  • 艺术与技术的交响曲:CSS绘图的艺术与实践
  • linux搭建mysql主从复制(一主一从)
  • Autosar RTE配置-Assembly和Delegation的使用-基于ETAS软件
  • Collections.unmodifiableList
  • 【Vue】Vue3 安装 Tailwind CSS 入门
  • Golang | Leetcode Golang题解之第240题搜索二维矩阵II
  • vue2导入elementui组件库
  • LabVIEW电路产品功能自动检测系统
  • 目标检测入门:4.目标检测中的一阶段模型和两阶段模型
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • EOS是什么
  • ES6简单总结(搭配简单的讲解和小案例)
  • React系列之 Redux 架构模式
  • sublime配置文件
  • underscore源码剖析之整体架构
  • vue-router 实现分析
  • 回顾2016
  • 基于axios的vue插件,让http请求更简单
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • Linux权限管理(week1_day5)--技术流ken
  • 数据可视化之下发图实践
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • # C++之functional库用法整理
  • #162 (Div. 2)
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (12)Hive调优——count distinct去重优化
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (Note)C++中的继承方式
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (轉貼) UML中文FAQ (OO) (UML)
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • **PHP分步表单提交思路(分页表单提交)
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET Core引入性能分析引导优化
  • .NET Core中的去虚
  • .net 提取注释生成API文档 帮助文档
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @JsonSerialize注解的使用
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解