一般緩存采用redis緩存,當(dāng)然也存在沒有redis的情況,所以備用方案是memory緩存。
但是使用的時(shí)候要進(jìn)行統(tǒng)一的注入,起碼要保證兩者繼承的接口是一致的
需要用到的包
Microsoft.Extensions.Caching.StackExchangeRedis
1. 建立緩存的 接口和實(shí)現(xiàn)。CacheService和ICacheService,用于注入的時(shí)候使用
接口比較簡(jiǎn)單,包括常用的 添加,刪除,清空,獲取,判斷是否存在,采用異步方式,異步好一些
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 實(shí)現(xiàn)接口ICacheService
redis 實(shí)現(xiàn)如下
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) { //默認(rèn)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 實(shí)現(xiàn)如下: 需要注意下,memory沒有清空的操作,可能有些大佬封裝的好一些,目前我這里沒有添加,但是我項(xiàng)目里是通過hashset來存儲(chǔ)鍵的,拆出來的時(shí)候圖省事直接不寫了,不是很影響,強(qiáng)迫癥例外
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) { //默認(rèn)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. 這一步比較重要,只是添加一種緩存那確實(shí)沒啥問題,我要的效果是,哪天redis掛了,項(xiàng)目會(huì)默認(rèn)使用memory,但是這對(duì)于開發(fā)來說,他無需在乎你掛沒掛,使用習(xí)慣仍然是一樣的,而不是要注入兩者不同的接口。就是體現(xiàn)在CacheService里面。類似于工廠類,注入之后會(huì)判斷下redis有沒有連接,連接了用redis,沒連接用memory。原理較為簡(jiǎn)單
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配置主要是通過配置文件讀取,格式如下,未啟用或連接有問題時(shí),則開啟memory緩存。 redis瞬時(shí)注入,因?yàn)閿?shù)據(jù)在redis里共享,memory是整個(gè)程序的內(nèi)存,單例好些
"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緩存
浙公網(wǎng)安備 33010602011771號(hào)