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 ...
随机推荐
- grub条目示例
https://wiki.archlinux.org/index.php/GRUB#Install_to_disk /boot/grub/menu.lst default=0 timeout=5 ti ...
- HttpClent4.3 的例子
package com.unbank.robotspider.util; import java.io.IOException; import java.net.MalformedURLExcepti ...
- Windows 网络问题
一.问题描述 每次重启后电脑的ip地址或无故没掉,或不能正常连网,要重新设置ip才能上网. 二.解决方法 在DOS窗口执行 netsh winsock reset netsh int ip res ...
- php databases support off fail zabbix
php 安装参数./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --enable- ...
- TFSF边界条件
待续 %1D FDTD simulation with a simple absorbing boundary condition % and a TFSF boundary between hy[] ...
- 矩阵乘法快速幂 codevs 1574 广义斐波那契数列
codevs 1574 广义斐波那契数列 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 广义的斐波那契数列是指形如 ...
- 伪多项式时间算法Pseudo-polynomial Algorithms-----geeksforGeek 翻译
原创翻译加学习笔记,方便国人学习算法知识! 原文链接http://www.geeksforgeeks.org/pseudo-polynomial-in-algorithms/ 什么是伪多项式? 当一个 ...
- UVA 11235 Frequent Values ---RMQ
大白书上的例题,具体讲解见大白书,最好用用一个Log数组直接求k,这样就是纯O(1)了 #include <iostream> #include <cstdio> #inclu ...
- 优化mysql主从下的slave延迟问题
一般而言,slave相对master延迟较大,其根本原因就是slave上的复制线程没办法真正做到并发.简单说,在master上是并发模式(以InnoDB引擎为主)完成事务提交的,而在slave上,复制 ...
- STL之stack栈
栈(statck)这种数据结构在计算机中是相当出名的.栈中的数据是先进后出的(First In Last Out, FILO).栈只有一个出口,允许新增元素(只能在栈顶上增加).移出元素(只能移出栈顶 ...
