简介

缓存是做什么的?

简单的可以认为是一个键值对的数据存于内存中,高速读取。作用为了减少和数据库的交互

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可以独自扩展

ABP缓存的更多相关文章

  1. 基于DDD的.NET开发框架 - ABP缓存Caching实现

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  2. ABP缓存示例

    private readonly ICacheManager _cacheManager; public ProgrammeManage(ICacheManager cacheManager) { _ ...

  3. ABP框架 - 缓存

    文档目录 本节内容: 简介 ICacheManager ICache ITypedCache 配置 实体缓存 EntityCache 是如何工作 Redis 缓存集成 简介 ABP提供了一个缓存接口, ...

  4. ABP入门系列(13)——Redis缓存用起来

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 引言 创建任务时我们需要指定分配给谁,Demo中我们使用一个下拉列表用来显示当前系统的所有用 ...

  5. ABP官方文档翻译 2.3 缓存

    缓存 介绍 ICacheManager 警告:GetCache方法 ICache ITypedCache 配置 实体缓存 实体缓存如何工作 Redis缓存集成 介绍 ABP为缓存提供了一个抽象接口,它 ...

  6. ABP框架 - 缓存( 转)

    出处:http://www.cnblogs.com/kid1412/p/5987083.html 文档目录 本节内容: 简介 ICacheManager ICache ITypedCache 配置 实 ...

  7. ABP开发框架前后端开发系列---(15)ABP框架的服务端和客户端缓存的使用

    缓存在一个大型一点的系统里面是必然会涉及到的,合理的使用缓存能够给我们的系统带来更高的响应速度.由于数据提供服务涉及到数据库的相关操作,如果客户端的并发数量超过一定的数量,那么数据库的请求处理则以爆发 ...

  8. 一步一步学习ABP项目系列文章目录

    1.概述 基于DDD的.NET开发框架 - ABP初探 基于DDD的.NET开发框架 - ABP分层设计 基于DDD的.NET开发框架 - ABP模块设计 基于DDD的.NET开发框架 - ABP启动 ...

  9. Redis缓存用起来

    Redis缓存用起来 1. 引言 创建任务时我们需要指定分配给谁,Demo中我们使用一个下拉列表用来显示当前系统的所有用户,以供用户选择.我们每创建一个任务时都要去数据库取一次用户列表,然后绑定到用户 ...

随机推荐

  1. python3与anaconda2共存

    我是先下载了python3,后来因为需要2为了省事就去下载了anaconda2,但发现这里有个很严重的共存问题. 找了一下网上的各种共存问题,发现网上的基本上都是基于一个anaconda然后通过虚拟环 ...

  2. AODH: ALARM EVENTS IN OPENSTACK

    AODH是从Ceilometer分离出来的一个子项目,开始于OpenStack Liberty,用来提供alarm机制. 除了之前Ceilometer有的基于sample的警报机制,AODH还添加了基 ...

  3. 魔术师发牌问题--java实现

    package com.wyl.linklist; /** **问题名称:魔术师发牌问题 *问题描述:魔术师手里一共有13张牌,全是黑桃,1~13. *********魔术师需要实现一个魔术:这是十三 ...

  4. review15

    不同区域的星期格式 不同国家的星期的简称或全称有很大的不同.如果想用特定地区的星期格式来表示日期中的星期,可以用format的重载方法: format(Locale locale, 格式化模式,日期列 ...

  5. 2017-01-15 微信小程序胡诌一

    2017年1月9日,正值iphone发布10周年,在2007年的1月9日,乔布斯发布了震惊世界的iphone,10年后张小龙正式推出了他的小程序,究竟何意也没有具体深究.最初的时候小程序并不叫小程序, ...

  6. web测试中的测试点和测试方法总结

    测试是一种思维,包括情感思维和智力思维,情感思维主要体现在一句俗语:思想决定行动上(要怀疑一切),智力思维主要体现在测试用例的设计上.具有了这样的思想,就会找出更多的bug.   一.输入框 1.字符 ...

  7. 22 Python 模块与包

    一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编 ...

  8. 剑指offer--8.调整数组顺序使奇数位于偶数前面

    习惯了简单 ------------------------------------------------- 时间限制:1秒 空间限制:32768K 热度指数:422906 本题知识点: 数组 题目 ...

  9. 网络基础之网络协议篇---CS架构--网络通信--osi 协议---套接字socket--粘包

    1 C\S 客户端/服务器架构: .硬件 C/S架构 (打印机) .软件 C/S 架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...

  10. inux中,关于多路复用的使用,有三种不同的API,select、poll和epoll

    inux中,关于多路复用的使用,有三种不同的API,select.poll和epoll https://www.cnblogs.com/yearsj/p/9647135.html 在上一篇博文中提到了 ...