.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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/47457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《昇思25天学习打卡营第20天|GAN图像生成》

生成对抗网络&#xff08;GAN&#xff09;是一种深度学习模型&#xff0c;用于生成逼真的图像。在手写数字识别的任务中&#xff0c;GAN 可以用来生成与真实手写数字相似的图像&#xff0c;以增强模型的训练数据集。GAN 主要由两个部分组成&#xff1a;生成器&#xff08;Gener…

服务自旋:Eureka中实现服务的动态注册与注销

标题&#xff1a;服务自旋&#xff1a;Eureka中实现服务的动态注册与注销 在微服务架构中&#xff0c;服务的动态注册和注销是确保服务发现机制灵活性和准确性的关键。Eureka&#xff0c;作为Netflix开源的服务发现框架&#xff0c;提供了强大的支持来实现服务实例的动态注册和…

Spring Data Jpa 原生SQL联表查询返回自定义DTO

Spring Data Jpa 原生SQL联表查询返回自定义DTO 方案一&#xff1a;返回Map 这个就不说了 方案二&#xff1a;实体定义成接口的形式 该方式最直观&#xff01;&#xff01;推荐&#xff01;&#xff01;&#xff01; 注意&#xff1a;XxxDto是interface接口&#xff0c;而…

WPF/C#:在WPF中如何实现依赖注入

前言 本文通过 WPF Gallery 这个项目学习依赖注入的相关概念与如何在WPF中进行依赖注入。 什么是依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种设计模式&#xff0c;用于实现控制反转&#xff08;Inversion of Control&#xff0…

[Redis]典型应用——缓存

什么是缓存 缓存&#xff08;Cache&#xff09;是一种用于临时存储数据的机制&#xff0c;目的是提高数据访问速度和系统性能。 核心思路就是把一些常用的数据放到触手可及(访问速度更快)的地方&#xff0c;方便随时读取 缓存是一个相对的概念&#xff0c;比如说&#xff0c…

EE trade:强平和爆仓的区别

在金融交易市场中&#xff0c;杠杆交易的引入&#xff0c;让投资者可以用少量的资金撬动更大的头寸&#xff0c;获取更大的收益。然而&#xff0c;杠杆交易也带来了更大的风险&#xff0c;一旦市场波动&#xff0c;投资者可能会面临强平或爆仓的风险。了解强平和爆仓的区别&…

选择Maya进行3D动画制作与渲染的理由

如果你对3D动画充满热情并追求成为专业3D动画师的梦想&#xff0c;你一定听说过Maya——近年来3D动画的行业标准。Maya被3D艺术家广泛使用&#xff0c;你是否想知道为什么Maya总是他们的首选&#xff1f;下面一起来了解下。 一、什么是Maya&#xff1f; 由Autodesk开发的Maya是…

2024年土木建筑与结构工程国际会议(IACCASE 2024)

2024年土木建筑与结构工程国际会议 2024 International Conference on Civil and Structural Engineering 【1】会议简介 2024年土木建筑与结构工程国际会议旨在为全球土木建筑与结构工程领域的专家学者、研究人员及从业人员提供一个交流与合作的平台。会议聚焦该领域的最新研究…

Java Spring Boot的三层结构

在Java Spring Boot开发中&#xff0c;三层架构是一种常见的设计模式&#xff0c;它通过将应用程序分为表现层&#xff08;Controller层&#xff09;、业务逻辑层&#xff08;Service层&#xff09;和数据访问层&#xff08;Repository层&#xff09;&#xff0c;来提高代码的可…

nng协议nni_posix_resolv_sysinit()系统初始化

nni_posix_resolv_sysinit(void) 实现了一个初始化函数 nni_posix_resolv_sysinit&#xff0c;用于设置解析系统&#xff08;resolver system&#xff09;。它主要负责初始化解析线程池&#xff0c;用于并发处理域名解析请求。 源码&#xff1a; int nni_posix_resolv_sysini…

影院选座系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;影院信息管理&#xff0c;电影类型管理&#xff0c;放映厅管理&#xff0c;电影信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;影院信息&…

Node.js实现文件下载

1.设置响应头&#xff1a; 使用 res.writeHead 设置适当的响应头&#xff0c;包括 Content-Type 和 Content-Disposition 以触发下载对话框。 2.创建文件读取流&#xff1a; 使用 fs.createReadStream 创建文件读取流&#xff0c;并通过 pipe 方法将其连接到响应对象 res&#…

学习小记-Nacos的服务注册与发现原理

服务注册&#xff1a; 当一个服务实例启动时&#xff0c;它会向 Nacos 服务器注册自己的信息&#xff0c;包括 IP 地址、端口号、元数据&#xff08;如服务版本、区域信息等&#xff09;。服务实例使用 Nacos API 发送注册请求&#xff0c;Nacos 服务器接收请求并存储服务实例信…

oracle数据字典详解

数据字典 1、动态数据字典是以v$xxx开始的数据字典&#xff0c;在数据库中约有150个左右&#xff0c;这些数据字典反映数据库动态运行状况&#xff0c;在不同时间查询会得到不同的结果。 2、DBA数据字典是以DBA_xxx表示&#xff0c;该数据字典存储数据库结构&#xff0c;查询…

[iOS]浅析isa指针

[iOS]浅析isa指针 文章目录 [iOS]浅析isa指针isa指针isa的结构isa的初始化注意事项 上一篇留的悬念不止分类的实现 还有isa指针到底是什么 它是怎么工作的 class方法又是怎么运作的 class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags 这里面的class又是何方…

7、核心:可扩展的共享内存数组结构-分块映射表和数组头

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 这篇是重点&#xff0c;如何构…

关于pip Install与conda install

conda解决依赖的问题很弱&#xff0c;环境包多了以后经常要解决依赖几分钟到十几分钟。我个人感觉比较好的实践是conda创建虚拟环境&#xff0c;装torch/tensorflow等比较难装的包&#xff0c;基础环境配好以后&#xff0c;后面装包一律用pip。 conda&#xff0c;pip&#xff0…

Netty Websocket

一、WebSocket 协议概述 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它允许服务端主动向客户端推送数据&#xff0c;从而实现了实时通信。WebSocket 建立在 HTTP 之上&#xff0c;但与 HTTP 的轮询&#xff08;Polling&#xff09;和长轮询&#xff08;Long Pol…

python的tkinter、socket库开发tcp的客户端和服务端

一、tcp通讯流程和开发步骤 1、tcp客户端和服务端通讯流程图 套接字是通讯的利器&#xff0c;连接时要经过三次握手建立连接&#xff0c;断开连接要经过四次挥手断开连接。 2、客户端开发流程 1&#xff09;创建客户端套接字 2&#xff09;和服务端器端套接字建立连接 3&#x…

Linux·基本指令(下)

1. mv 指令 (move) 语法&#xff1a;mv[选项] 源文件或目录 目标文件或目录 功能&#xff1a;将源文件或目录剪贴到一个新位置&#xff0c;或给源文件或目录改名但不会改变其内容 常用选项&#xff1a; -f &#xff1a;force 强制&#xff0c;如果目标文件已经存在&#xff0c;…