ABP缓存
简介
缓存是做什么的?
简单的可以认为是一个键值对的数据存于内存中,高速读取。作用为了减少和数据库的交互
Abp中缓存的使用
public class InvoiceAppService : ApplicationService
{
// 缓存管理器
private readonly ICacheManager _cacheManager;
// 仓储
private readonly IRepository<Invoice> _rep;
public TestAppService(ICacheManager cacheMgr, IRepository<Invoice> rep)
{
_cacheManager;= cacheMgr;
_rep = rep;
}
public void ChanelInvoice()
{
// 获取缓存
var cache = _cacheManager.GetCache("cache1");
// 转换强类型缓存
var typedCache = cache.AsTyped<int, string>();
// 获取缓存的值,存在则直接从缓存中取,不存在则按照你给定的方式取出值,然后添加进缓存中
// 这里是利用仓储从数据库中取出
var cacheValue = typedCache.Get(10, id => _rep.Get(id).Name);
}
}
Abp中的缓存可以看作一个大衣柜,里面有许多方格, 我们第一步 _cacheManager.GetCache得到的就是一个方格,里面有许多的value,value就是我们缓存的值.
在说的详细点,缓存分类里有user user里面有user1,user2... ,分类里还有invoice, invoice里面有invoice1,invoice2
我们都是从这个“分类里”取得具体的“缓存项”
ICacheManager
Abp框架使用的时候,取缓存,都是通过从ioc容器取出ICacheManager
public interface ICacheManager : IDisposable
{
// 获取所有缓存
IReadOnlyList<ICache> GetAllCaches();
// 根据名称取出缓存
[NotNull] ICache GetCache([NotNull] string name);
}
ICacheManager的默认实现是:CacheManagerBase(抽象类)实现了ISingletonDependency.在框架启动时会以单利的形式注册到ioc容器中.
public abstract class CacheManagerBase : ICacheManager, ISingletonDependency
{
// ioc管理器
protected readonly IIocManager IocManager;
// 缓存配置器
protected readonly ICachingConfiguration Configuration;
// 存放缓存的字典
protected readonly ConcurrentDictionary<string, ICache> Caches;
// Constructor.
protected CacheManagerBase(IIocManager iocManager, ICachingConfiguration configuration)
{
IocManager = iocManager;
Configuration = configuration;
Caches = new ConcurrentDictionary<string, ICache>();
}
// 获取所有缓存
public IReadOnlyList<ICache> GetAllCaches()
{
return Caches.Values.ToImmutableList();// 转换成不可变集合
}
// 根据名称获取缓存 ICache
public virtual ICache GetCache(string name)
{ // 空值检测
Check.NotNull(name, nameof(name));
// 如果已经存在,则直接取出.
// 不存在则创建一个.
return Caches.GetOrAdd(name, (cacheName) =>
{ // 具体创建缓存的方法。该方法由具体的实现类,实现.
var cache = CreateCacheImplementation(cacheName);
// 获取缓存配置项 (c => c.CacheName == null 这是所有缓存的设置,后面会有说到)
var configurators = Configuration.Configurators.Where(c => c.CacheName == null || c.CacheName == cacheName);
// 为缓存设置 配置项中的配置(时间等..)
foreach (var configurator in configurators)
{
configurator.InitAction?.Invoke(cache);
}
return cache;
});
}
// 释放
public virtual void Dispose()
{
DisposeCaches();
Caches.Clear();
}
// 调用ioc管理器依次释放
protected virtual void DisposeCaches()
{
foreach (var cache in Caches)
{
IocManager.Release(cache.Value);
}
}
// 实际创建缓存的方法.由子类实现.(可能是redis,或者memcache等)
protected abstract ICache CreateCacheImplementation(string name);
}
AbpMemoryCacheManager
AbpMemoryCacheManager是内存缓存管理器,是CacheManagerBase的其中一种实现
public class AbpMemoryCacheManager : CacheManagerBase
{ // 日志
public ILogger Logger { get; set; }
// ctor
public AbpMemoryCacheManager(IIocManager iocManager, ICachingConfiguration configuration)
: base(iocManager, configuration)
{
Logger = NullLogger.Instance;
}
// 重写CacheManagerBase的CreateCacheImplementation方法,创建AbpMemoryCache
protected override ICache CreateCacheImplementation(string name)
{
return new AbpMemoryCache(name)
{
Logger = Logger
};
}
// 释放
protected override void DisposeCaches()
{
foreach (var cache in Caches.Values)
{
cache.Dispose();
}
}
}
下面看看AbpMemoryCache是什么.
public class AbpMemoryCache : CacheBase
{
private MemoryCache _memoryCache;
// ctor
public AbpMemoryCache(string name)
: base(name)
{
_memoryCache = new MemoryCache(new OptionsWrapper<MemoryCacheOptions>(new MemoryCacheOptions()));
}
// 根据key获取值
public override object GetOrDefault(string key)
{
return _memoryCache.Get(key);
}
// 设置key和值
public override void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
if (value == null)
{
throw new AbpException("Can not insert null values to the cache!");
}
if (absoluteExpireTime != null)
{
_memoryCache.Set(key, value, DateTimeOffset.Now.Add(absoluteExpireTime.Value));
}
else if (slidingExpireTime != null)
{
_memoryCache.Set(key, value, slidingExpireTime.Value);
}
else if (DefaultAbsoluteExpireTime != null)
{
_memoryCache.Set(key, value, DateTimeOffset.Now.Add(DefaultAbsoluteExpireTime.Value));
}
else
{
_memoryCache.Set(key, value, DefaultSlidingExpireTime);
}
}
// 根据key移除
public override void Remove(string key)
{
_memoryCache.Remove(key);
}
// 清空
public override void Clear()
{
_memoryCache.Dispose();
_memoryCache = new MemoryCache(new OptionsWrapper<MemoryCacheOptions>(new MemoryCacheOptions()));
}
// 释放
public override void Dispose()
{
_memoryCache.Dispose();
base.Dispose();
}
}
就是对memoryCache做了一层封装.
AbpRedisCacheManager
public class AbpRedisCacheManager : CacheManagerBase
{
public AbpRedisCacheManager(IIocManager iocManager, ICachingConfiguration configuration)
: base(iocManager, configuration)
{ // 瞬时注册AbpRedisCache
IocManager.RegisterIfNot<AbpRedisCache>(DependencyLifeStyle.Transient);
}
// 实现基类的CreateCacheImplementation方法 创建缓存
protected override ICache CreateCacheImplementation(string name)
{ // 从ioc容器中获取,这里需要name作为参数(如果你对ioc容器创建对象这个过程了解的话,就知道我说的是什么)
return IocManager.Resolve<AbpRedisCache>(new { name });
}
}
这个就是redis的实现,主要还是AbpRedisCache,这个和AbpMemoryCache一样,都是做了一层封装,其内部就是对redis的使用的封装啦
ICache
AbpRedisCache和AbpMemoryCache都是根据ICache实现的。
public interface ICache : IDisposable
{
// 缓存名字(唯一的)
string Name { get; }
// 滑动过期时间,默认 1h 可以通过configuration设置
TimeSpan DefaultSlidingExpireTime { get; set; }
// 绝对过期时间 默认是null.
TimeSpan? DefaultAbsoluteExpireTime { get; set; }
// 获取缓存数据,不存在则执行 Func 委托
object Get(string key, Func<string, object> factory);
// 上面方法做了批量处理
object[] Get(string[] keys, Func<string, object> factory);
// 异步获取
Task<object> GetAsync(string key, Func<string, Task<object>> factory);
// 同上
Task<object[]> GetAsync(string[] keys, Func<string, Task<object>> factory);
// 获取缓存数据,没有的话为null
object GetOrDefault(string key);
// 批量
object[] GetOrDefault(string[] keys);
// 异步处理
Task<object> GetOrDefaultAsync(string key);
Task<object[]> GetOrDefaultAsync(string[] keys);
// 设置缓存
void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
void Set(KeyValuePair<string, object>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
// 异步处理
Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
Task SetAsync(KeyValuePair<string, object>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
// 根据key移除缓存
void Remove(string key);
void Remove(string[] keys);
Task RemoveAsync(string key);
Task RemoveAsync(string[] keys);
// 清除该缓存下的所有数据
void Clear();
Task ClearAsync();
}
强类型缓存
CacheExtensions是ICache的扩展.
public static class CacheExtensions
{
// 其他代码
// 转换强类型缓存
public static ITypedCache<TKey, TValue> AsTyped<TKey, TValue>(this ICache cache)
{
return new TypedCacheWrapper<TKey, TValue>(cache);
}
// 其他代码
}
通过ICache转换成强类型缓存,那么我们的缓存的值 也是强类型的了,不需要在手动强转了
可以看到上述代码中new了TypedCacheWrapper
public class TypedCacheWrapper<TKey, TValue> : ITypedCache<TKey, TValue>
{
// 具体调用AsTyped这个静态方法的Icache对象
public ICache InternalCache { get; private set; }
public TypedCacheWrapper(ICache internalCache)
{
InternalCache = internalCache;
}
// 缓存名字
public string Name
{
get { return InternalCache.Name; }
}
// 滑动过期时间
public TimeSpan DefaultSlidingExpireTime
{
get { return InternalCache.DefaultSlidingExpireTime; }
set { InternalCache.DefaultSlidingExpireTime = value; }
}
// 绝对过期时间
public TimeSpan? DefaultAbsoluteExpireTime
{
get { return InternalCache.DefaultAbsoluteExpireTime; }
set { InternalCache.DefaultAbsoluteExpireTime = value; }
}
// 释放
public void Dispose()
{
InternalCache.Dispose();
}
// 清空
public void Clear()
{
InternalCache.Clear();
}
public Task ClearAsync()
{
return InternalCache.ClearAsync();
}
// 取 删 。。。。 其实调用的还是ICache的扩展方法
// return (TValue)cache.Get(key.ToString(), (k) => (object)factory(key));
// 最后还是做了强转
public TValue Get(TKey key, Func<TKey, TValue> factory)
{
return InternalCache.Get(key, factory);
}
public TValue[] Get(TKey[] keys, Func<TKey, TValue> factory)
{
return InternalCache.Get(keys, factory);
}
public Task<TValue> GetAsync(TKey key, Func<TKey, Task<TValue>> factory)
{
return InternalCache.GetAsync(key, factory);
}
public Task<TValue[]> GetAsync(TKey[] keys, Func<TKey, Task<TValue>> factory)
{
return InternalCache.GetAsync(keys, factory);
}
public TValue GetOrDefault(TKey key)
{
return InternalCache.GetOrDefault<TKey, TValue>(key);
}
public TValue[] GetOrDefault(TKey[] keys)
{
return InternalCache.GetOrDefault<TKey, TValue>(keys);
}
public Task<TValue> GetOrDefaultAsync(TKey key)
{
return InternalCache.GetOrDefaultAsync<TKey, TValue>(key);
}
public Task<TValue[]> GetOrDefaultAsync(TKey[] keys)
{
return InternalCache.GetOrDefaultAsync<TKey, TValue>(keys);
}
public void Set(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
InternalCache.Set(key.ToString(), value, slidingExpireTime, absoluteExpireTime);
}
public void Set(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
var stringPairs = pairs.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value));
InternalCache.Set(stringPairs.ToArray(), slidingExpireTime, absoluteExpireTime);
}
public Task SetAsync(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
return InternalCache.SetAsync(key.ToString(), value, slidingExpireTime, absoluteExpireTime);
}
public Task SetAsync(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
var stringPairs = pairs.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value));
return InternalCache.SetAsync(stringPairs.ToArray(), slidingExpireTime, absoluteExpireTime);
}
public void Remove(TKey key)
{
InternalCache.Remove(key.ToString());
}
public void Remove(TKey[] keys)
{
InternalCache.Remove(keys.Select(key => key.ToString()).ToArray());
}
public Task RemoveAsync(TKey key)
{
return InternalCache.RemoveAsync(key.ToString());
}
public Task RemoveAsync(TKey[] keys)
{
return InternalCache.RemoveAsync(keys.Select(key => key.ToString()).ToArray());
}
}
ICachingConfiguration
缓存的一些设置。如过期时间等,则是通过ICachingConfiguration进行设置的。而ICachingConfiguration,则是在AbpBootstrap的初始化方法中进行注入的
public virtual void Initialize()
{
IocManager.IocContainer.Install(new AbpCoreInstaller());
}
internal class AbpCoreInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
// 其他配置组建(略)..
Component.For<ICachingConfiguration, CachingConfiguration>().ImplementedBy<CachingConfiguration>().LifestyleSingleton()
);
}
}
最后会变为IAbpStartupConfiguration
的一个属性(Caching),有因为AbpModule中有IAbpStartupConfiguration这个属性,所以我们可以在自己的模块的预初始化方法中对缓存进行一系列的设置
public override void PreInitialize()
{
Configuration.Caching.ConfigureAll(z=>z.DefaultSlidingExpireTime = TimeSpan.FromHours(1));
}
在来看看CachingConfiguration
internal class CachingConfiguration : ICachingConfiguration
{
public IAbpStartupConfiguration AbpConfiguration { get; private set; }
// 缓存配置器集合
private readonly List<ICacheConfigurator> _configurators;
public IReadOnlyList<ICacheConfigurator> Configurators
{
get { return _configurators.ToImmutableList(); }
}
// ctor
public CachingConfiguration(IAbpStartupConfiguration abpConfiguration)
{
AbpConfiguration = abpConfiguration;
_configurators = new List<ICacheConfigurator>();
}
// 为所有缓存设置 一些配置.
public void ConfigureAll(Action<ICache> initAction)
{
_configurators.Add(new CacheConfigurator(initAction));
}
// 为指定名称的缓存 设置一些配置。
public void Configure(string cacheName, Action<ICache> initAction)
{
_configurators.Add(new CacheConfigurator(cacheName, initAction));
}
}
internal class CacheConfigurator : ICacheConfigurator
{
// 缓存名字
public string CacheName { get; private set; }
// 执行的配置操作
public Action<ICache> InitAction { get; private set; }
public CacheConfigurator(Action<ICache> initAction)
{
InitAction = initAction;
}
public CacheConfigurator(string cacheName, Action<ICache> initAction)
{
CacheName = cacheName;
InitAction = initAction;
}
}
在IcacheManager的默认实现CacheManagerBase中,创建缓存后会获取CacheConfigurator并执行InitAction.Invoke方法对缓存进行设置,而InitAction则是我们在模块的预初始化方法中定义的.
根据ConfigureAll和Configure方法可以看出,你在初始化的时候,ConfigureAll是会初始化一个cacheName=null的 CacheConfigurator
而Configure则是一个指定名称的CacheConfigurator
这也是为什么,CacheManagerBase中获取所有缓存配置,是这样过滤的,
获取所有缓存都要的配置 以及 指定缓存 自己的配置
var configurators = Configuration.Configurators.Where(c => c.CacheName == null || c.CacheName == cacheName);
可以借鉴的地方
在缓存这一块,ABP用了两个设计模式:
模板方法模式:父类实现主要算法,定义调用顺序和过程,子类实现具体算法,CacheManagerBase,AbpMemoryCacheManager,AbpRedisCacheManager
桥接模式:ICacheManager,ICache 独立变化Manager和Cache,使Manager和Cache可以独自扩展