一、.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. 1.Eclipse下载、常用配置、快捷键

    Eclipse官网下载:https://www.eclipse.org/downloads/packages/ 自动补全 位置:Eclipse——Window——Perferences——Java—— ...

  2. 02-11 RANSAC算法线性回归(波斯顿房价预测)

    目录 RANSAC算法线性回归(波斯顿房价预测) 一.RANSAC算法流程 二.导入模块 三.获取数据 四.训练模型 五.可视化 更新.更全的<机器学习>的更新网站,更有python.go ...

  3. js中的toString和valueOf

    数据的转换 基本上,所有JS数据类型都拥有valueOf和toString这两个方法,null除外.它们俩解决javascript值运算与显示的问题 所有对象继承了两个转换方法: 第一个是toStri ...

  4. margin和text-align实现水平居中的区别

    1.首先text-align只应用于内联块和内联元素 text-align影响的是元素中的文本内容的对其方式(默认是left,设置为center时水平居中) 所以,将text-align设置为cent ...

  5. OFD电子文档阅读器功能说明(采用WPF开发,永久免费)

    特别说明 ofd阅读器开发语言为c#,具有完全自主产权,没有使用第三方ofd开发包.可以根据你的需求快速定制开发.本阅读器还在开发完善阶段,如有任何问题,可以联系我QQ:13712486.博客:htt ...

  6. 对象模型(Object-Model):关于vptr、vtbl

    当一个类本身定义了虚函数,或其父类有虚函数时,为了支持多态机制,编译器将为该类添加一个虚函数指针(vptr).虚函数指针一般都放在对象内存布局的第一个位置上,这是为了保证在多层继承或多重继承的情况下能 ...

  7. 算法学习之剑指offer(二)

    题目1 题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. import java.util.Stack; public class Solution { ...

  8. cobalt strike批量发送钓鱼邮件

    0×01 利用Cobalt strike生成木马 这里我们生成木马可以用cs带的HTA.OFFICE宏.word宏来使目标上线cs,这里以word宏病毒为例子. 首先我们需要制作一个word宏病毒来进 ...

  9. python编程系列---进程池的优越性体验

    1.通过multiprocessing.Process()类创建子进程 import multiprocessing, time, os, random def work(index): " ...

  10. HTTP Catcher

    HTTP Catcher HTTP Catcher 是一个 Web 调试工具.它可以拦截.查看.修改和重放来自 iOS 系统的 HTTP 请求. 你不需要连接电脑,HTTP Catcher 可以在后台 ...