一般缓存采用redis缓存,当然也存在没有redis的情况,所以备用方案是memory缓存。
但是使用的时候要进行统一的注入,起码要保证两者继承的接口是一致的
需要用到的包
Microsoft.Extensions.Caching.StackExchangeRedis
1. 建立缓存的 接口和实现。CacheService和ICacheService,用于注入的时候使用
接口比较简单,包括常用的 添加,删除,清空,获取,判断是否存在,采用异步方式,异步好一些
public interface ICacheService { Task SetAsync(string key, object value, TimeSpan? expirationTime = null); Task GetAsync(string key); Task RemoveAsync(string key); Task<bool> ExistAsync(string key); Task ClearAsync(); }
2.分别建立redis和memory 实现接口ICacheService
redis 实现如下
public class RedisCacheService(IConnectionMultiplexer connection) : ICacheService { private readonly IConnectionMultiplexer connection = connection; private readonly IDatabase redis = connection.GetDatabase(); public async Task ClearAsync() { var endpoints = connection.GetEndPoints(); var tasks = endpoints.Select(async x => { var server = connection.GetServer(x); foreach (var key in server.Keys()) { await redis.KeyDeleteAsync(key); } }); await Task.WhenAll(tasks); } public async Task<bool> ExistAsync(string key) { return await redis.KeyExistsAsync(key); } public async Task<string> GetAsync(string key) { return await redis.StringGetAsync(key); } public async Task RemoveAsync(string key) { await redis.KeyDeleteAsync(key); } public async Task SetAsync(string key, object value, TimeSpan? expirationTime = null) { if (value != null) { //默认2H expirationTime ??= TimeSpan.FromHours(2); if (value is string cacheStr) { await redis.StringSetAsync(key, cacheStr, expirationTime); } else { await redis.StringSetAsync(key, JsonSerializer.Serialize(value), expirationTime); } } } }
memory 实现如下: 需要注意下,memory没有清空的操作,可能有些大佬封装的好一些,目前我这里没有添加,但是我项目里是通过hashset来存储键的,拆出来的时候图省事直接不写了,不是很影响,强迫症例外
public class MemoryCacheService(IMemoryCache memory) : ICacheService { private readonly IMemoryCache memory = memory; public Task ClearAsync() {
return Task.CompletedTask;
} public Task<bool> ExistAsync(string key) { var exists = memory.TryGetValue(key, out _); return Task.FromResult(exists); } public Task<string> GetAsync(string key) { if (memory.TryGetValue(key, out string value)) { return Task.FromResult(value); } return Task.FromResult<string>(null); } public Task RemoveAsync(string key) { memory.Remove(key); return Task.CompletedTask; } public Task SetAsync(string key, object value, TimeSpan? expirationTime = null) { if (value != null) { //默认2H expirationTime ??= TimeSpan.FromHours(2); if (value is string cacheStr) { memory.Set(key, cacheStr, expirationTime.Value); } else { memory.Set(key, JsonSerializer.Serialize(value), expirationTime.Value); } } return Task.CompletedTask; } }
3. 这一步比较重要,只是添加一种缓存那确实没啥问题,我要的效果是,哪天redis挂了,项目会默认使用memory,但是这对于开发来说,他无需在乎你挂没挂,使用习惯仍然是一样的,而不是要注入两者不同的接口。就是体现在CacheService里面。类似于工厂类,注入之后会判断下redis有没有连接,连接了用redis,没连接用memory。原理较为简单
public class CacheService : ICacheService { private readonly ICacheService cache; public CacheService(IConnectionMultiplexer connection, IMemoryCache memory) { if (connection.IsConnected) { cache = new RedisCacheService(connection); } else { cache = new MemoryCacheService(memory); } } public async Task ClearAsync() { await cache.ClearAsync(); } public async Task<bool> ExistAsync(string key) { return await cache.ExistAsync(key); } public async Task<string> GetAsync(string key) { return await cache.GetAsync(key); } public async Task RemoveAsync(string key) { await cache.RemoveAsync(key); } public async Task SetAsync(string key, object value, TimeSpan? expirationTime = null) { await cache.SetAsync(key, value, expirationTime); } }
4.如何注入:redis配置主要是通过配置文件读取,格式如下,未启用或连接有问题时,则开启memory缓存。 redis瞬时注入,因为数据在redis里共享,memory是整个程序的内存,单例好些
"Redis": { "Enable": false, "UseCluster": false, "ConnectionSingle": "127.0.0.1:6379", "ConnectionCluster": "node1:6379,node2:6379,node3:6379", "InstanceName": "" }
public static void AddCacheSetup(this IServiceCollection services) { if (AppSettings.AppString("Redis:Enable").ToBool()) { var redisConfig = string.Empty; if (AppSettings.AppString("Redis:UseCluster").ToBool()) { redisConfig = AppSettings.AppString("Redis:ConnectionCluster"); } else { redisConfig = AppSettings.AppString("Redis:ConnectionSingle"); } var configurationOptions = ConfigurationOptions.Parse(redisConfig); configurationOptions.ResolveDns = true; try { var connectionMultiplexer = ConnectionMultiplexer.Connect(configurationOptions); services.AddStackExchangeRedisCache(x => { x.ConnectionMultiplexerFactory = () => Task.FromResult(connectionMultiplexer as IConnectionMultiplexer); var instanceName = AppSettings.AppString("Redis:InstanceName"); if (!string.IsNullOrEmpty(instanceName)) { x.InstanceName = instanceName; } }); services.AddTransient<ICacheService, RedisCacheService>(); } catch (Exception) { services.AddMemoryCache(); services.AddSingleton<ICacheService, MemoryCacheService>(); } } else { services.AddMemoryCache(); services.AddSingleton<ICacheService, MemoryCacheService>(); } }
最后Program里: builder.Services.AddCacheSetup();
只需要在使用的地方注入ICacheService,就可以使用了,而不用在意用的是redis缓存还是memory缓存
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)