关于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的更多相关文章

  1. Orchard 刨析:导航篇

    之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...

  2. Orchard 刨析:Logging

    最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...

  3. Orchard 刨析:前奏曲

    Orchard中大量使用了依赖注入,而实现依赖注入的组件就是Autofac,它在Orchard中扮演者非常重要的角色,多租户如是,模块如是,工作区也如是.今天就来讲讲Autofac在Orchard中的 ...

  4. 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 ...

  5. MapReduce源码刨析

    MapReduce编程刨析: Map map函数是对一些独立元素组成的概念列表(如单词计数中每行数据形成的列表)的每一个元素进行指定的操作(如把每行数据拆分成不同单词,并把每个单词计数为1),用户可以 ...

  6. Apollo 刨析:Localization

    九月 30 2014 11:27 上午     admin 0 Comments 今天我们来看一看Apollo中的Localization Component. 本地化在Apollo中的使用 像这样的 ...

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

    前言 由浅入深.逐个击破 30SecondsOfCode 中函数系列所有源码片段,带你领略源码之美. 本系列是对名库 30SecondsOfCode 的深入刨析. 本篇是其中的函数篇,可以在极短的时间 ...

  8. Golang 性能测试 (3) 跟踪刨析 golang trace

    简介 对于绝大部分服务,跟踪刨析是用不到的.但是如果遇到了下面问题,可以不妨一试: 怀疑哪个协程慢了 系统调用有问题 协程调度问题 (chan 交互.互斥锁.信号量等) 怀疑是 gc (Garbage ...

  9. 温故知新-多线程-深入刨析volatile关键词

    文章目录 摘要 volatile的作用 volatile如何解决线程可见? CPU Cache CPU Cache & 主内存 缓存一致性协议 volatile如何解决指令重排序? volat ...

随机推荐

  1. 在Jena框架下基于MySQL数据库实现本体的存取操作

    在Jena框架下基于MySQL数据库实现本体的存取操作 转自:http://blog.csdn.net/jtz_mpp/article/details/6224311 最近在做一个基于本体的管理系统. ...

  2. 迅为顶级四核开发板 Exynos4412开发板,仅售560元

    业内公认最强四核 藐视一切挑战 ●四核CPU  ●四核GPU  ●双通道内存  ●HKMG技术  ●超级性能  ●至佳稳定  ●供货周期长 Exynos 4412处理器成功应用于三星GALAXY S3 ...

  3. 动手学习TCP: 环境搭建

    前一段时间通过Wireshark抓包,定位了一个客户端和服务器之间数据传输的问题.最近就抽空看了看<TCP/IP详解 卷1>中关于TCP的部分,书中用了很多例子展示了TCP/IP协议中的一 ...

  4. HBase性能调优

    因官方Book Performance Tuning部分章节没有按配置项进行索引,不能达到快速查阅的效果.所以我以配置项驱动,重新整理了原文,并补充一些自己的理解,如有错误,欢迎指正. 配置优化 zo ...

  5. 二分+动态规划 POJ 1973 Software Company

    Software Company Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 1112   Accepted: 482 D ...

  6. Jetson TK1刷机+配置Mini PCI-e无线网卡

    最近买了台4K电视,觉得可以当显示器用,但没主机,不知怎的想到了Jetson TK1,于是一冲动买了.因为没网线,而Jetson TK1没有无线网卡,所以也折腾了一番,记录一下,给万一也有像我一样没有 ...

  7. 一道题看bitset应用 --ZOJ 3642

    题意:给n个文件,包括文件名和文件大小,然后给出k个关键词,查询包含该关键词的文件的大小总和.文件名为一些中括号括起的关键词的合集. 解法:可用bitset记录每一个关键词在哪些文件中出现,然后查询即 ...

  8. Bellman-Ford算法判负环

    算法思想:如果没有负权回路,dis数组应该会在n-1次松弛之后结束. 算法复杂度:O(n*m).比Dijkstra算法复杂度要高. 代码: bool Bellman_Ford(int s) { int ...

  9. DoTween(HOTween V2) 教程

    DoTween资料 官方网站:http://dotween.demigiant.com/ 下载地址:http://dotween.demigiant.com/download.php 快速开始:htt ...

  10. JMeter学习(五)检查点

    JMeter也有像LR中的检查点,本篇就来介绍下JMeter的检查点如何去实现. JMeter里面的检查点通过添加断言来完成. 检查点:上一章讲到,我们对用户名和密码进行了参数化,那么怎样来判断jme ...