Orchard 刨析:Caching

关于Orchard中的Caching组件已经有一些文章做了介绍,为了系列的完整性会再次对Caching组件进行一次介绍。

缓存的使用


在Orchard看到如下一段代码:

可以看到使用缓存的方法Get而看不到以前常见的缓存方法:Add\Set\Remove,是不是很神奇。

其实也不是那么的神奇,Get方法中根据传入的Key(culture)在缓存数据存储中搜索,如果存在则直接返回缓存结果,如果不存在则执行传入的委托,并把委托返回的结果放入到缓存中。

那么怎么确保缓存是不是失效呢?难道不支持吗?

Orchard的缓存中比较难理解的就是这一块了,Orchard的缓存失效机制非常的赞。

ctx.Monitor(_signals.When("culturesChanged"));
这一块就是缓存失效的精髓,这边暂把它叫做添加一个可监控的挥发令牌。这个When方法其实返回了一个实现了IVolatileToken接口的对象实例。下面会有详细的说明。

接口关系


下面是缓存的基本接口的依赖关系


ICache<TKey,TResult>

ICache<TKey,TResult>是一个泛型接口,根据泛型名称可以看出通过该接口可以定义缓存Key的类型和缓存结果的类型,也就是说Orchard中缓存的Key不只是大家所常见String类型

ICache接口是缓存组件中最终的接口,可以这么理解ICacheManagerICacheHolder都是缓存组件为了可扩展性和易用性而设立的抽象,ICache才是缓存的实现(存储缓存数据的地方)

下面我们来看一看它的实现类型:Cache<TKey,TResult>。

IVolatileToken

一个抽象的挥发令牌是缓存状态的一个关键接口,里面的IsCurrent含义是是否是当前的对象,如果为false则代表缓存失效,如果为true则代表缓存有效。这边大家先了解下概念后面会详细说明。

字段

ICacheContextAccessor
_cacheContextAccessor:缓存上下文访问器。

ConcurrentDictionary<TKey,
CacheEntry>
_entries:线程安全的字典表,用于存储缓存的数据。

CacheEntry 缓存条目,对缓存结果进行了封装,主要对缓存结果添加了令牌机制(IVolatileToken)。

TResult Result:缓存的数据。

IEnumerable<IVolatileToken>
Tokens:这个缓存条目所对应的挥发令牌(一个缓存可由多种情况导致缓存失效,如:5分钟之后失效、数据被更改时候失效等多种失效方式)。

AddToken(IVolatileToken
volatileToken):添加一个新的令牌至Tokens。

CompactTokens():主要用于去除Tokens中重复的令牌。(因为令牌是提供给外部添加的所有可能会出现重复的令牌,为提高性能(令牌内的执行执行时间不得而知)需要剔除重复的令牌)

接口方法

可以看到简单的Cache类中包含了添加缓存、更新缓存的方法,而Get方法就是对这两个方法的封装,我们来看看Get方法的实现

代码很简单,使用了ConcurrentDictionary字典的特性添加或者更新(当Key不存在时执行第一个委托内的方法:AddEntrty,当key存在时执行第二个委托内的方法UpdateEntry)

私有方法

PropagateTokens

可以看作探测缓存是否失效。

CreateEntry

AddEntry

UpdateEntry


注意看(currentEntry.Tokens.Any(t => t != null
&& !t.IsCurrent))缓存失效的核心就在这里了,实现了IVolatileToken接口的对象是一个引用类型,只要这个实例被添加至对应的缓存条目,并且通过一些手段将IsCurrent设为False那么这个缓存就失效了。

ICacheHolder(生命周期:租户单例)

顾名思义这个接口主要是用来维护一个ICache接口集合的。

ICacheHolder中包含了一个方法

对应的实现

为什么需要ICacheHolder?

因为缓存Key是很容易冲突的,比如一名开发人员在开发管理员模块的时将所有的管理员账户信息缓存起来那么缓存Key很可能为Users,那么另外一名开发人员在开发会员模块时候也很有可能会使用Users这个人见人爱的Key,这时候缓存中的数据就冲突了。不过在Orchard中很好的避免了这个问题,就是分区。

在ICacheHolder中维护了一个ConcurrentDictionary<CacheKey, object>字典表,CacheKey为一个三元组,类型全是Type,分别为:使用缓存的组件类型,缓存Key类型,缓存结果类型。

以使用缓存的服务类、Key类型、结果类型作为一个Key,也就是说开发人员只要保证在同一个类型中不使用相同类型相同值的Key和相同的结果类型,该缓存就不会冲突(笔者觉得这个创意非常的赞)。

ICacheManager(生命周期:瞬态)

缓存管理者接口,也是大家最经常使用的接口。(ICacheManager的注入在上一篇“Orchard
刨析:前奏曲
”中有解释。)

字段

Type
_component:使用缓存的服务组件类型。

ICacheHolder
_cacheHolder:缓存持有者。

方法

ICache<TKey, TResult> GetCache<TKey,
TResult>():从缓存持有者中获取一个缓存。

TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult>
acquire):调用GetCache<TKey,TResult>方法,并且调用ICache的Get方法。

也包含了两个方法,主要的方法是GetCache这一个,Get方法只是对这个方法进行了封装,笔者觉得这种还是使用扩展方法来实现会更好。

下面我们来看看实现。

非常的简单。

拿ISignals(生命周期:全局单例)开刀

ISignals是一个以信号量方式提供的一个简单的挥发提供者。

ISignals接口提供了两个方法一个用来生成Token,一个用来使Token失效(设置IsCurrent为false)。

我们来看内部实现

字段

IDictionary<object,Token> _tokens:用来存储信号量令牌的字典表。

方法

void Trigger<T>(T signal);
触发一个信号量(设置IsCurrent为false导致缓存失效)。
IVolatileToken
When<T>(T signal);(生成一个令牌)

可以看到这么一个简单的类就可以实现一个缓存失效机制,可见Orchard的缓存失效机制是易于扩展的,Orchard中还内置了一些其他的失效机制实现,如:时间、文件监控等。

总结

以上是对Orchard缓存的一个简单说明,一个简简单单的缓存都被Orchard设计的这么华丽,不得不敬佩Microsoft的工程师们,下面我们在简单的过一遍缓存的流程。

异步令牌提供者

IAsyncTokenProvider

在上面也说了为什么需要压缩缓存条目中的令牌,原因就是会影响性能,所以Orchard也提供了异步的令牌机制,主要是以异步的方式传播令牌,实现方式也非常简单,用到了线程池。

并行缓存

并行缓存暂时不提供说明,因为还有一些争议:并行缓存是不是属于缓存。

写在最后

刚开始写这个系列没多久,可能开头的文章逻辑并不是很清楚,期待后续文章的改善,如果在读文中有遇到问题请移步QQ群:299744835,专为本系列提供的一个交流探讨的地方。

Orchard 刨析:Caching,布布扣,bubuko.com

时间: 2024-10-21 11:35:09

Orchard 刨析:Caching的相关文章

Orchard 刨析:导航篇

之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard Framework为Title,其实只是因为本人没有用Orchard开发过实际的东西,只是研究了它Fraemwork层面的东西和极少部分的Module,Framework中也有一些没有研究或极少的研究,比如:Content,Display,Indexing,Messaging等,如果大家想了解这方面

Orchard 刨析:Logging

最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西跟大家一起分享(架构.思想上的分享). 今天我们来看一看Orchard中的Logging Component. 日志在Orchard中的使用 像这样的代码大家在Orchard中肯定经常见到,那么NullLogger.Instance是什么?大量使用依赖注入的Orchard会把真正的日志记录器放在一个

Orchard 刨析:前奏曲

Orchard中大量使用了依赖注入,而实现依赖注入的组件就是Autofac,它在Orchard中扮演者非常重要的角色,多租户如是,模块如是,工作区也如是.今天就来讲讲Autofac在Orchard中的应用. 从OrchardStarter认识Autofac OrchardStarter中向Ioc容器中注册了大量的服务,在此中的服务也是"root"域,根域. Module注册 builder.RegisterModule(new CacheModule()); 这段话代表了注册一个Mod

wlan技术问题刨析

标题索引 追朔原因 核心技术 问题刨析 项目总结 本文出自 "一步一印,有印为证" 博客,谢绝转载!

30s源码刨析系列之函数篇

前言 由浅入深.逐个击破 30SecondsOfCode 中函数系列所有源码片段,带你领略源码之美. 本系列是对名库 30SecondsOfCode 的深入刨析. 本篇是其中的函数篇,可以在极短的时间内培养你的函数式思维. 内容根据源码的难易等级进行排版,目录如下: 新手级 普通级 专家级 正文 新手级 checkProp const checkProp = (predicate, prop) => obj => !!predicate(obj[prop]); const lengthIs4

Apollo 刨析:简介

  Apollo是配置在IIS服务器上的一个Web站点,它使用了.NET4.0和ASP.Net的技术. 代码是C#编写的.是基于ASP.NET MVC3的Web开发框架上编写的一个应用. 它使用到了Nhibernate(OR映射工具).Autofac(依赖注入容器).Castle(也是一个开源框架,好像说是用到了动态代理之类的东西,目前还没明白,等清楚了再说)等成熟的框架和类库. 其实还有一些其他常用的类库,如:sharpziplib(压缩.解压).log4net(日志)等. 估计是太常用了所以

char *与char []深度刨析

编译不能通过 #include "iostream" using namespace std; int _tmain(int argc, _TCHAR* argv[]) { char *p= "hello" ; //不是把一个字符串赋给了一个字符型的指针,而是把一个字符型的指针指向了字符串的首地址. strcpy(p,"hel"); cout << p << endl; return 0; }//编译通不过 解析:char

深度刨析-reduxTodo

昨天我们讲到了怎么去简单的构建一套完整的redux体系,今天我以一个简单的实例去讲解reduxTodo用ES6+webpack+redux怎么去实现数据流动,还有需要注意的地方.好的,言归正传,让我们开始吧! 首先我们看一下完整的文件目录,一个简单的todo我们只做删除和添加两个功能 再贴一下完整的示例图 首先说一下如果我们不使用redux怎么去实现简单的删除功能呢,我们把上图分为两个组件部分,分别是Header和section部分,header部分包括的是输入框和h1部分,section部分我

深入刨析三层交换机工作过程

路由器的三层转发主要依靠CPU进行,而三层交换机的三层转发依靠ASIC芯片完成,这就决定了两者在转发性能上的巨大差别.当然,三层交换机并不能完全替代路由器,路由器所具备的丰富的接口类型.良好的流量服务等级控制.强大的路有能力等仍然是三层交换机的薄弱环节.目前的三层交换机一般是通过VLAN来划分二层网络并实现二层交换的,同时能够实现不同VLAN间的三层IP互访.在讨论三层交换机的转发原理之前有必要交代一下不同网络的主机之间互访时的行为: (1)源主机在发起通信之前,将主机的IP与目的主机的IP进行