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接口是缓存组件中最终的接口,可以这么理解ICacheManager、ICacheHolder都是缓存组件为了可扩展性和易用性而设立的抽象,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的更多相关文章
- Orchard 刨析:导航篇
之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...
- Orchard 刨析:Logging
最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...
- Orchard 刨析:前奏曲
Orchard中大量使用了依赖注入,而实现依赖注入的组件就是Autofac,它在Orchard中扮演者非常重要的角色,多租户如是,模块如是,工作区也如是.今天就来讲讲Autofac在Orchard中的 ...
- Learning Cocos2d-x for WP8(2)——深入刨析Hello World
原文:Learning Cocos2d-x for WP8(2)--深入刨析Hello World cocos2d-x框架 在兄弟篇Learning Cocos2d-x for XNA(1)——小窥c ...
- MapReduce源码刨析
MapReduce编程刨析: Map map函数是对一些独立元素组成的概念列表(如单词计数中每行数据形成的列表)的每一个元素进行指定的操作(如把每行数据拆分成不同单词,并把每个单词计数为1),用户可以 ...
- Apollo 刨析:Localization
九月 30 2014 11:27 上午 admin 0 Comments 今天我们来看一看Apollo中的Localization Component. 本地化在Apollo中的使用 像这样的 ...
- 30s源码刨析系列之函数篇
前言 由浅入深.逐个击破 30SecondsOfCode 中函数系列所有源码片段,带你领略源码之美. 本系列是对名库 30SecondsOfCode 的深入刨析. 本篇是其中的函数篇,可以在极短的时间 ...
- Golang 性能测试 (3) 跟踪刨析 golang trace
简介 对于绝大部分服务,跟踪刨析是用不到的.但是如果遇到了下面问题,可以不妨一试: 怀疑哪个协程慢了 系统调用有问题 协程调度问题 (chan 交互.互斥锁.信号量等) 怀疑是 gc (Garbage ...
- 温故知新-多线程-深入刨析volatile关键词
文章目录 摘要 volatile的作用 volatile如何解决线程可见? CPU Cache CPU Cache & 主内存 缓存一致性协议 volatile如何解决指令重排序? volat ...
随机推荐
- 第四篇 :微信公众平台开发实战Java版之完成消息接受与相应以及消息的处理
温馨提示: 这篇文章是依赖前几篇的文章的. 第一篇:微信公众平台开发实战之了解微信公众平台基础知识以及资料准备 第二篇 :微信公众平台开发实战之开启开发者模式,接入微信公众平台开发 第三篇 :微信公众 ...
- apt-cache, apt-get
apt是debian系的软件包的管理工具,他们可以通过搜索在/var/lib/apt/list里的索引文件搜做根据/etc/apt/sources.list里的软件源来在线安装软件,安装的过程还可以自 ...
- sass+compass+bootstrap三剑合璧高效开发记录
1. 先搭建环境,下载node.js,rubyinstaller,安装, 安装rubyinstaller时,要选上include system path,这样就会自动将node.js执行添加到wind ...
- Android Listener侦听的N种写法
Android中,View的Listener方法,在是否使用匿名类匿名对象时,有各种不同的写法. OnClickListener和其他Listener方法一样,都是View类的接口,重载实现后就能使用 ...
- Mac OS X 快捷键
启动快捷键 按下按键或组合键,直到所需的功能出现(例如,在启动过程中按住 Option 直到出现“启动管理程序”,或按住 Shift 直到出现“安全启动”).提示:如果启动功能未起作用,而您使用的是第 ...
- Android 内存分析
1.使用adb命令查看app内存使用情况 adb shell dumpsys meminfo <package_name> adb shell top | grep <package ...
- WordCount Analysis
1.Create a new java project, then copy examples folder from /home/hadoop/hadoop-1.0.4/src; Create a ...
- Linux 系统常用命令汇总(二) vi 文本编辑
文本编辑 vi 命令 作用 +文件名 编辑文本文件,若文件不存在同时创建该文件 Ctrl+f 向后翻一页 Ctrl+b 向前翻一页 Ctrl+d 向后翻半页 Ctrl+u 向前翻半页 + 光标移动到下 ...
- URAL 2014 Zhenya moves from parents --线段树
题意:儿子身无分文出去玩,只带了一张他爸的信用卡,当他自己现金不足的时候就会用信用卡支付,然后儿子还会挣钱,挣到的钱都是现金,也就是说他如果有现金就会先花现金,但是有了现金他不会还信用卡的钱.他每花一 ...
- bootstrap学习总结-css样式设计(一)
由于项目需要,所以打算好好学习下bootstrap框架,之前了解一点,框架总体不难,但涉及到的东西还是很多,想要熟练掌握它,还是要多练练. 一:bootstrap是什么? bs是什么? 即前端页面搭 ...