一、.MemoryCache介绍

MemoryCache是.Net Framework 4.0开始提供的内存缓存类,使用该类型可以方便的在程序内部缓存数据并对于数据的有效性进行方便的管理, 它通过在内存中缓存数据和对象来减少读取数据库的次数,从而减轻数据库负载,加快数据读取速度,提升系统的性能。

二、Redis介绍

Redis是一个开源的key-value存储系统,它支持的数据类型包括string(字符串)、 list(链表)、set(集合)、zset(sorted set --有序集合)和hashs(哈希)数据类型的相关操作

三、MemoryCache与Redis的区别

1、性能方面:Redis 只能使用单核(如果确实需要充分使用多核cpu的能力,那么需要在单台服务器上运行多个redis实例(主从部署/集群化部署),并将每个redis实例和cpu内核进行绑定),而 MemoryCache可以使用多核,所以每一个核上Redis在存储小数据时比Memcached性能更高。而存储大数据时,Memcached性能要高于Redis。

2、内存管理方面: MemoryCache使用预分配的内存池的方式,使用slab和大小不同的chunk来管理内存,Item根据大小选择合适的chunk存储,内存池的方式可以省去申请/释放内存的开销,并且能 减小内存碎片产生,但这种方式也会带来一定程度上的空间浪费,并且在内存仍然有很大空间时,新的数据也可能会被剔除; Redis使用现场申请内存的方式来存储数据,并且很少使用free-list等方式来优化内存分配,会在一定程度上存在内存碎片,在Redis中,并不是所有的数据都一直存储在内存中的,当物理内存用完时,Redis可以将一些很久没用到的value交换到磁盘。

3、数据持久化支持:Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB快照和AOF日志。而MemoryCache是不支持数据持久化操作的。

四、本系统中使用MemoryCache和Redis

目标:   1、MemoryCache和Redis使用无缝切换,统一接口,通过配置选择使用MemoryCache还是Redis

2、使用Redis时,减少对Redis的读取(HttpContextAccessor配合Redis使用)

实现:

MemoryCache我们采用EasyCaching(可以从git上获取:https://github.com/dotnetcore/EasyCaching),由于本身提供的接口不满足需求,所以我们直接下载到本地,将EasyCaching.Core和EasyCaching.InMemory添加到项目中,如图所示:

在IEasyCachingProvider添加接口

/// <summary>
/// Removes cached item by cachekey's contain.
/// </summary>
/// <param name="contain"></param>
void RemoveByContain(string contain);

在EasyCachingAbstractProvider.cs添加代码

public abstract void BaseRemoveByContain(string contain);

        public void RemoveByContain(string contain)
{
var operationId = s_diagnosticListener.WriteRemoveCacheBefore(new BeforeRemoveRequestEventData(CachingProviderType.ToString(), Name, nameof(RemoveByPrefix), new[] { contain }));
Exception e = null;
try
{
BaseRemoveByContain(contain);
}
catch (Exception ex)
{
e = ex;
throw;
}
finally
{
if (e != null)
{
s_diagnosticListener.WriteRemoveCacheError(operationId, e);
}
else
{
s_diagnosticListener.WriteRemoveCacheAfter(operationId);
}
}
}

在DefaultInMemoryCachingProvider.Async.cs中添加代码

        public override void BaseRemoveByContain(string contain)
{
ArgumentCheck.NotNullOrWhiteSpace(contain, nameof(contain)); var count = _cache.RemoveByContain(contain); if (_options.EnableLogging)
_logger?.LogInformation($"RemoveByContain : contain = {contain} , count = {count}");
}

在IInMemoryCaching.cs中添加接口

int RemoveByContain(string contain);

在InMemoryCaching.cs中实现接口

   public int RemoveByContain(string contain)
{
var keysToRemove = _memory.Keys.Where(x => x.Contains(contain)).ToList();
return RemoveAll(keysToRemove);
}

MemoryCache接口实现:MemoryCacheManager

using EasyCaching.Core;
using System;
using System.Threading.Tasks; namespace Tools.Cache
{ /// <summary>
///内存管理
/// </summary>
public partial class MemoryCacheManager : ILocker, IStaticCacheManager
{
#region Fields private readonly IEasyCachingProvider _provider; #endregion #region Ctor public MemoryCacheManager(IEasyCachingProvider provider)
{ _provider = provider;
} #endregion #region Methods /// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
public T Get<T>(string key, Func<T> acquire, int? cacheTime = null)
{
if (cacheTime <= )
return acquire(); return _provider.Get(key, acquire, TimeSpan.FromMinutes(cacheTime ?? CachingDefaults.CacheTime)).Value; }
/// <summary>
/// 通过指定Key获取缓存数据,不存在则返回Null
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <returns></returns>
public T Get<T>(string key)
{
return _provider.Get<T>(key).Value;
}
/// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
public async Task<T> GetAsync<T>(string key, Func<Task<T>> acquire, int? cacheTime = null)
{
if (cacheTime <= )
return await acquire(); var t = await _provider.GetAsync(key, acquire, TimeSpan.FromMinutes(cacheTime ?? CachingDefaults.CacheTime));
return t.Value;
} /// <summary>
/// 设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <param name="data">Value</param>
/// <param name="cacheTime">缓存时间(分钟)</param>
public void Set(string key, object data, int cacheTime)
{
if (cacheTime <= )
return; _provider.Set(key, data, TimeSpan.FromMinutes(cacheTime));
} /// <summary>
/// 判断Key是否设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <returns>True表示存在;false则不存在</returns>
public bool IsSet(string key)
{
return _provider.Exists(key);
} /// <summary>
/// 执行某些操作使用独占锁
/// </summary>
/// <param name="resource">独占锁的Key</param>
/// <param name="expirationTime">锁自动过期的时间</param>
/// <param name="action">执行的操作</param>
/// <returns>如果获取了锁并执行了操作,则为true;否则为false</returns>
public bool PerformActionWithLock(string key, TimeSpan expirationTime, Action action)
{
if (_provider.Exists(key))
return false; try
{
_provider.Set(key, key, expirationTime); action(); return true;
}
finally
{ Remove(key);
}
} /// <summary>
///通过Key删除缓存数据
/// </summary>
/// <param name="key">Key</param>
public void Remove(string key)
{
_provider.Remove(key);
} /// <summary>
/// 删除以prefix开头的缓存数据
/// </summary>
/// <param name="prefix">prefix开头</param>
public void RemoveByPrefix(string prefix)
{
_provider.RemoveByPrefix(prefix);
}
/// <summary>
/// 删除所有包含字符串的缓存
/// </summary>
/// <param name="contain">包含的字符串</param>
public void RemoveByContain(string contain)
{
_provider.RemoveByContain(contain);
}
/// <summary>
/// 删除所有的缓存
/// </summary>
public void Clear()
{
_provider.Flush();
} public virtual void Dispose()
{
} #endregion
}
}

Redis实现:

CachingDefaults

using System;
using System.Collections.Generic;
using System.Text; namespace Tools.Cache
{ public static partial class CachingDefaults
{
/// <summary>
/// 缓存默认过期时间
/// </summary>
public static int CacheTime => ; /// <summary>
/// 获取用于保护Key列表存储到redis的Key(与启用persistDataProtectionKeysRedis选项一起使用)
/// </summary>
public static string RedisDataProtectionKey => "API.DataProtectionKeys";
}
}

ILocker

using System;
using System.Collections.Generic;
using System.Text; namespace Tools.Cache
{
public interface ILocker
{
/// <summary>
/// 执行某些操作使用独占锁
/// </summary>
/// <param name="resource">独占锁的Key</param>
/// <param name="expirationTime">锁自动过期的时间</param>
/// <param name="action">执行的操作</param>
/// <returns>如果获取了锁并执行了操作,则为true;否则为false</returns>
bool PerformActionWithLock(string resource, TimeSpan expirationTime, Action action);
}
}

ICacheManager

using System;
using System.Collections.Generic;
using System.Text; namespace Tools.Cache
{
/// <summary>
///缓存接口
/// </summary>
public interface ICacheManager : IDisposable
{
/// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
T Get<T>(string key, Func<T> acquire, int? cacheTime = null);
/// <summary>
/// 通过Key获取指定缓存,如果不存在则返回null
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <returns></returns>
T Get<T>(string key); /// <summary>
/// 设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <param name="data">Value</param>
/// <param name="cacheTime">缓存时间(分钟)</param>
void Set(string key, object data, int cacheTime); /// <summary>
/// 判断Key是否设置缓存
/// </summary>
/// <param name="key">Keym</param>
/// <returns>True表示存在;false则不存在</returns>
bool IsSet(string key); /// <summary>
///通过Key删除缓存数据
/// </summary>
/// <param name="key">Key</param>
void Remove(string key); /// <summary>
/// 删除以prefix开头的缓存数据
/// </summary>
/// <param name="prefix">prefix开头的字符串</param>
void RemoveByPrefix(string prefix); /// <summary>
/// 删除包含字符串的缓存
/// </summary>
/// <param name="contain">包含的字符串</param>
void RemoveByContain(string contain); /// <summary>
/// 删除所有的缓存
/// </summary>
void Clear();
}
}

PerRequestCacheManager

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Tools.ComponentModel; namespace Tools.Cache
{
/// <summary>
/// HTTP请求期间用于缓存的管理器(短期缓存)
/// </summary>
public partial class PerRequestCacheManager : ICacheManager
{
#region Ctor public PerRequestCacheManager(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor; _locker = new ReaderWriterLockSlim();
} #endregion #region Utilities /// <summary>
///获取请求范围内共享数据的key/value集合
/// </summary>
protected virtual IDictionary<object, object> GetItems()
{
return _httpContextAccessor.HttpContext?.Items;
} #endregion #region Fields private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ReaderWriterLockSlim _locker; #endregion #region Methods /// <summary>
/// 通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
public virtual T Get<T>(string key, Func<T> acquire, int? cacheTime = null)
{
IDictionary<object, object> items; using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
{
items = GetItems();
if (items == null)
return acquire(); //i如果缓存存在,返回缓存数据
if (items[key] != null)
return (T)items[key];
} //或者通过方法创建
var result = acquire(); if (result == null || (cacheTime ?? CachingDefaults.CacheTime) <= )
return result; //设置缓存(如果定义了缓存时间)
using (new ReaderWriteLockDisposable(_locker))
{
items[key] = result;
} return result;
}
public T Get<T>(string key)
{
IDictionary<object, object> items; using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
{
items = GetItems(); //i如果缓存存在,返回缓存数据
if (items[key] != null)
return (T)items[key];
}
return default(T);//没有则返回默认值Null
} /// <summary>
/// 设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <param name="data">Value</param>
/// <param name="cacheTime">缓存时间(分钟)</param>
public virtual void Set(string key, object data, int cacheTime)
{
if (data == null)
return; using (new ReaderWriteLockDisposable(_locker))
{
var items = GetItems();
if (items == null)
return; items[key] = data;
}
} /// <summary>
/// 判断Key是否设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <returns>True表示存在;false则不存在</returns>
public virtual bool IsSet(string key)
{
using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.Read))
{
var items = GetItems();
return items?[key] != null;
}
} /// <summary>
/// 通过Key删除缓存数据
/// </summary>
/// <param name="key">Key</param>
public virtual void Remove(string key)
{
using (new ReaderWriteLockDisposable(_locker))
{
var items = GetItems();
items?.Remove(key);
}
} /// <summary>
/// 删除以prefix开头的缓存数据
/// </summary>
/// <param name="prefix">prefix开头</param>
public virtual void RemoveByPrefix(string prefix)
{
using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.UpgradeableRead))
{
var items = GetItems();
if (items == null)
return; //匹配prefix
var regex = new Regex(prefix,
RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
var matchesKeys = items.Keys.Select(p => p.ToString()).Where(key => regex.IsMatch(key)).ToList(); if (!matchesKeys.Any())
return; using (new ReaderWriteLockDisposable(_locker))
{
//删除缓存
foreach (var key in matchesKeys)
{
items.Remove(key);
}
}
}
}
/// <summary>
/// 删除所有包含字符串的缓存
/// </summary>
/// <param name="contain">包含的字符串</param>
public void RemoveByContain(string contain)
{
using (new ReaderWriteLockDisposable(_locker, ReaderWriteLockType.UpgradeableRead))
{
var items = GetItems();
if (items == null)
return; List<string> matchesKeys = new List<string>();
var data = items.Keys.Select(p => p.ToString()).ToList(); foreach(var item in data)
{
if(item.Contains(contain))
{
matchesKeys.Add(item);
}
} if (!matchesKeys.Any())
return; using (new ReaderWriteLockDisposable(_locker))
{
//删除缓存
foreach (var key in matchesKeys)
{
items.Remove(key);
}
}
}
}
/// <summary>
/// 清除所有缓存
/// </summary>
public virtual void Clear()
{
using (new ReaderWriteLockDisposable(_locker))
{
var items = GetItems();
items?.Clear();
}
} public virtual void Dispose()
{ } #endregion
}
}

Redis接口实现:RedisCacheManager

using EasyCaching.Core.Serialization;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using Tools.Configuration;
using Tools.Redis; namespace Tools.Cache
{
/// <summary>
/// Redis缓存管理
/// </summary>
public partial class RedisCacheManager : IStaticCacheManager
{
#region Fields
private readonly ICacheManager _perRequestCacheManager;
private readonly IRedisConnectionWrapper _connectionWrapper;
private readonly IDatabase _db; #endregion #region Ctor public RedisCacheManager(ICacheManager perRequestCacheManager,
IRedisConnectionWrapper connectionWrapper,
StartupConfig config)
{
if (string.IsNullOrEmpty(config.RedisConnectionString))
throw new Exception("Redis 连接字符串为空!"); _perRequestCacheManager = perRequestCacheManager;
_connectionWrapper = connectionWrapper;
_db = _connectionWrapper.GetDatabase(config.RedisDatabaseId ?? (int)RedisDatabaseNumber.Cache);
} #endregion #region Utilities protected byte[] Serialize<T>(T value)
{
using (var ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, value);
return ms.ToArray();
}
} protected virtual IEnumerable<RedisKey> GetKeys(EndPoint endPoint, string prefix = null)
{
var server = _connectionWrapper.GetServer(endPoint); var keys = server.Keys(_db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*"); keys = keys.Where(key => !key.ToString().Equals(CachingDefaults.RedisDataProtectionKey, StringComparison.OrdinalIgnoreCase)); return keys;
} protected virtual IEnumerable<RedisKey> GetContainKeys(EndPoint endPoint, string contain = null)
{
var server = _connectionWrapper.GetServer(endPoint); var keys = server.Keys(_db.Database, string.IsNullOrEmpty(contain) ? null : $"*{contain}*"); keys = keys.Where(key => !key.ToString().Equals(CachingDefaults.RedisDataProtectionKey, StringComparison.OrdinalIgnoreCase)); return keys;
} protected virtual async Task<T> GetAsync<T>(string key)
{
if (_perRequestCacheManager.IsSet(key))
return _perRequestCacheManager.Get(key, () => default(T), );
var serializedItem = await _db.StringGetAsync(key);
if (!serializedItem.HasValue)
return default(T); var item = JsonConvert.DeserializeObject<T>(serializedItem);
if (item == null)
return default(T);
_perRequestCacheManager.Set(key, item, );
return item;
} protected virtual async Task SetAsync(string key, object data, int cacheTime)
{
if (data == null)
return; var expiresIn = TimeSpan.FromMinutes(cacheTime); var serializedItem = JsonConvert.SerializeObject(data); await _db.StringSetAsync(key, serializedItem, expiresIn);
} protected virtual async Task<bool> IsSetAsync(string key)
{
if (_perRequestCacheManager.IsSet(key))
return true;
return await _db.KeyExistsAsync(key);
} #endregion #region Methods /// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
public async Task<T> GetAsync<T>(string key, Func<Task<T>> acquire, int? cacheTime = null)
{
if (await IsSetAsync(key))
return await GetAsync<T>(key); var result = await acquire(); if ((cacheTime ?? CachingDefaults.CacheTime) > )
await SetAsync(key, result, cacheTime ?? CachingDefaults.CacheTime); return result;
} /// <summary>
/// 通过Key获取缓存
/// </summary>
/// <typeparam name="T">>缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <returns>通过Key获取到的特定的数据</returns>
public virtual T Get<T>(string key)
{
if (_perRequestCacheManager.IsSet(key))
return _perRequestCacheManager.Get(key, () => default(T), );
var serializedItem = _db.StringGet(key); if (!serializedItem.HasValue)
return default(T); var item = JsonConvert.DeserializeObject<T>(serializedItem);
if (item == null)
return default(T); _perRequestCacheManager.Set(key, item, ); return item; } /// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
public virtual T Get<T>(string key, Func<T> acquire, int? cacheTime = null)
{ if (IsSet(key))
return Get<T>(key); var result = acquire(); if ((cacheTime ?? CachingDefaults.CacheTime) > )
Set(key, result, cacheTime ?? CachingDefaults.CacheTime); return result;
} /// <summary>
/// 设置缓存
/// </summary>
/// <param name="key">Key</param>
/// <param name="data">Value</param>
/// <param name="cacheTime">缓存时间(分钟)</param>
public virtual void Set(string key, object data, int cacheTime)
{
if (data == null)
return; var expiresIn = TimeSpan.FromMinutes(cacheTime); var serializedItem = JsonConvert.SerializeObject(data); _db.StringSet(key, serializedItem, expiresIn);
} /// <summary>
/// 判断Key是否设置缓存
/// </summary>
/// <param name="key">Keym</param>
/// <returns>True表示存在;false则不存在</returns>s>
public virtual bool IsSet(string key)
{
if (_perRequestCacheManager.IsSet(key))
return true;
return _db.KeyExists(key);
} /// <summary>
///通过Key删除缓存数据
/// </summary>
/// <param name="key">Key</param>
public virtual void Remove(string key)
{
if (key.Equals(CachingDefaults.RedisDataProtectionKey, StringComparison.OrdinalIgnoreCase))
return; _db.KeyDelete(key);
_perRequestCacheManager.Remove(key);
}
/// <summary>
/// 删除所有包含字符串的缓存
/// </summary>
/// <param name="contain">包含的字符串</param>
public void RemoveByContain(string contain)
{
_perRequestCacheManager.RemoveByContain(contain); foreach (var endPoint in _connectionWrapper.GetEndPoints())
{
var keys = GetContainKeys(endPoint, contain); _db.KeyDelete(keys.ToArray());
}
} /// <summary>
/// 删除以prefix开头的缓存数据
/// </summary>
/// <param name="prefix">prefix开头</param>
public virtual void RemoveByPrefix(string prefix)
{
_perRequestCacheManager.RemoveByPrefix(prefix); foreach (var endPoint in _connectionWrapper.GetEndPoints())
{
var keys = GetKeys(endPoint, prefix); _db.KeyDelete(keys.ToArray());
}
} /// <summary>
/// 删除所有的缓存
/// </summary>
public virtual void Clear()
{
foreach (var endPoint in _connectionWrapper.GetEndPoints())
{
var keys = GetKeys(endPoint).ToArray(); foreach (var redisKey in keys)
{
_perRequestCacheManager.Remove(redisKey.ToString());
} _db.KeyDelete(keys);
}
} public virtual void Dispose()
{
} #endregion
}
}

IStaticCacheManager Redis和MemoryCache统一暴露的接口

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; namespace Tools.Cache
{
/// <summary>
///用于在HTTP请求之间进行缓存的管理器(长期缓存)
/// </summary>
public interface IStaticCacheManager : ICacheManager
{
/// <summary>
///通过Key获取缓存,如果没有该缓存,则创建该缓存,并返回数据
/// </summary>
/// <typeparam name="T">缓存项Type</typeparam>
/// <param name="key">缓存 key</param>
/// <param name="acquire">,如果该Key没有缓存则通过方法加载数据</param>
/// <param name="cacheTime">缓存分钟数; 0表示不缓存; null则使用默认缓存时间</param>
/// <returns>通过Key获取到的特定的数据</returns>
Task<T> GetAsync<T>(string key, Func<Task<T>> acquire, int? cacheTime = null);
}
}

配置是否使用Redis作为缓存,默认使用MemoryCache

  "Cache": {
"RedisEnabled": true,
"RedisDatabaseId": "",
"RedisConnectionString": "127.0.0.1:6379,ssl=False",
"UseRedisToStoreDataProtectionKeys": true,
"UseRedisForCaching": true,
"UseRedisToStorePluginsInfo": true
}

读取配置扩展ServiceCollectionExtensions

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text; namespace Infrastructure.Common.Extensions
{
public static class ServiceCollectionExtensions
{
public static TConfig ConfigureStartupConfig<TConfig>(this IServiceCollection services, IConfiguration configuration) where TConfig : class, new()
{
if (services == null)
throw new ArgumentNullException(nameof(services)); if (configuration == null)
throw new ArgumentNullException(nameof(configuration)); var config = new TConfig(); configuration.Bind(config); services.AddSingleton(config); return config;
}
}
}

依赖注入:我们这里采用autofac来实现依赖注入,在Startup.cs中的ConfigureServices方法中添加:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var build =
new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())//SetBasePath设置配置文件所在路径
.AddJsonFile("appsettings.json");
var configRoot = build.Build();
var config = services.ConfigureStartupConfig<StartupConfig>(configRoot.GetSection("Cache"));
var builder = new ContainerBuilder();
#region 自动判断是否使用Redis,TURE则使用Redis,否则使用本机内存缓存
if (config.RedisEnabled)
{
//services.
builder.RegisterType<RedisConnectionWrapper>()
.As<ILocker>()
.As<IRedisConnectionWrapper>()
.SingleInstance();
}
//static cache manager
if (config.RedisEnabled && config.UseRedisForCaching)
{
builder.RegisterType<RedisCacheManager>().As<IStaticCacheManager>()
.InstancePerLifetimeScope();
}
else
{
builder.RegisterType<MemoryCacheManager>()
.As<ILocker>()
.As<IStaticCacheManager>()
.SingleInstance();
} #endregion
services.AddEasyCaching(option =>
{
//use memory cache
option.UseInMemory("default");
});
var container = builder.Build();
return new AutofacServiceProvider(container);//autofac 接管.netCore默认DI

在Configure方法中添加:

app.UseEasyCaching();

至此,MemoryCache与Redis的使用到此结束。
最后说一下添加RemoveByContain接口的目的:看方法就是删除包含字符串的缓存对象,目的:在底层操作数据时,防止数据更新了,缓存还存在,不能访问最新的数据,如何做到实时同步,看上篇博客底层的方法,在对单表操作时缓存Key都包含了实体的名称,因为后期我们可能还会涉及到一些多表的查询,此时,只需要在设置多表查询时缓存Key都包含每个实体的名称,后面再更新或删除数据时通过包含字符串就可以移除掉所有相关的缓存,保证了数据及时的一致性。

缓存管理之MemoryCache与Redis的使用的更多相关文章

  1. SpringBoot缓存管理(二) 整合Redis缓存实现

    SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...

  2. 【无私分享:ASP.NET CORE 项目实战(第十一章)】Asp.net Core 缓存 MemoryCache 和 Redis

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 ...

  3. 缓存管理Memorycache 的使用

      前言:什么是memoryCache? 一种缓存管理技术,某些只读数据频繁操作数据库,会对系统的性能有很大的开销,所以我们使用缓存技术,当数据库内容更新,我们在更更新缓存的数据值.目前缓存讲技术的产 ...

  4. Asp.net Core 缓存 MemoryCache 和 Redis

    Asp.net Core 缓存 MemoryCache 和 Redis 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitH ...

  5. Asp.net Core2.0 缓存 MemoryCache 和 Redis

    自从使用Asp.net Core2.0 以来,不停摸索,查阅资料,这方面的资料是真的少,因此,在前人的基础上,摸索出了Asp.net Core2.0 缓存 MemoryCache 和 Redis的用法 ...

  6. 微服务-Springboot+Redis缓存管理接口代码实现

    废话少说,上代码,结合代码讲解: 一.创建maven工程:导入依赖: <packaging>war</packaging><!--修改jdk的版本--><pr ...

  7. 七:SpringBoot-集成Redis数据库,实现缓存管理

    SpringBoot-集成Redis数据库,实现缓存管理 1.SpringBoot集成Redis 1.1 核心依赖 1.2 配置文件 1.3 简单测试案例 1.4 自定义序列化配置 1.5 序列化测试 ...

  8. SpringBoot缓存管理(三) 自定义Redis缓存序列化机制

    前言 在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理. 接下来 ...

  9. [Abp 源码分析]八、缓存管理

    0.简介 缓存在一个业务系统中十分重要,常用的场景就是用来储存调用频率较高的数据.Abp 也提供了一套缓存机制供用户使用,在使用 Abp 框架的时候可以通过注入 ICacheManager 来新建/设 ...

随机推荐

  1. jsp JavaBean el表达式

    JSP三大指令 一个jsp页面中,可以有0~N个指令的定义! 1. page --> 最复杂:<%@page language="java" info="xx ...

  2. Kubernetes+Docker+Istio 容器云实践

    随着社会的进步与技术的发展,人们对资源的高效利用有了更为迫切的需求.近年来,互联网.移动互联网的高速发展与成熟,大应用的微服务化也引起了企业的热情关注,而基于Kubernetes+Docker的容器云 ...

  3. Road Construction

    King Mercer is the king of ACM kingdom. There are one capital and some cities in his kingdom. Amazin ...

  4. Docker的安装及加速器配置

    简介 Docker是一个开源项目 ,其主要目标是实现轻量级的操作系统虚拟化解决方案.Docker的基础是Linux容器(LXC)等技术.在LXC的基础上Docker进行了进一步的封装,让用户不需关心容 ...

  5. 因果推理的春天-实用HTE(Heterogeneous Treatment Effects)论文github收藏

    一直以来机器学习希望解决的一个问题就是'what if',也就是决策指导: 如果我给用户发优惠券用户会留下来么? 如果患者服了这个药血压会降低么? 如果APP增加这个功能会增加用户的使用时长么? 如果 ...

  6. Vue3都使用Proxy了,你更应该了解Proxy

    vue3.0的pre-alpha版代码已经开源了,就像作者之前放出的消息一样,其数据响应这一部分已经由ES6的Proxy来代替Object.defineProperty实现,感兴趣的同学可以看其实现源 ...

  7. Java学习笔记之基础语法(顺序,条件,循环语句)

    顺序结构:自上而下 条件分支选择结构: if条件语句   1,一旦某一个分支确定执行以后,其他分支就不会执行.if后面的条件必须是boolean类型   2,if  后面如果不加大括号,默认相邻的下一 ...

  8. 安装Go语言及搭建Go语言开发环境

    一步一步,从零搭建Go语言开发环境. 安装Go语言及搭建Go语言开发环境 下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://gol ...

  9. java学习-IDEA相关使用

    1.配置git与github(用于将代码提交到GitHub) 添加自己的github账号 2.提交代码到github 登录https://github.com,即可看到刚刚提交到github的代码仓库 ...

  10. 学习笔记:flutter项目搭建(mac版)

    什么是flutter Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的 ...