entlib2.0研究(五)--缓存组件的设计
3缓存组件的设计
缓存组件被设计实现下面的目标:
l 提供一定的APIs
l 是开发者在不了解内部工作原理的情况下,使用组件。
l 使用配制工具进行简单的配制
l 提供性能的优化
l 线程安全,
l 如果异常发生,确保后端数据的完整
l 确保内存数据和后端数据的同步
3.1设计亮点
当你使用CacheFactory初始化CacheManager实例时,他在内部创建CacheManagerFactory对象,接着创建Cache对象。Cache对象被创建后,所有在后端存储里的数据被加载到内存中,包含在Cache对象中。应用程序就可以请求CacheManager对象来获取、添加、移除数据,
当应用使用GetData方法发请求到CacheManager对象来获取缓存项时,CacheManager对象将请求推给Cache对象。如果请求的项在缓存中,返回缓存中的内存中的版本到应用程序。如果不在缓存中,返回null。如果项过期,也返回null。
当应用使用Add方法请求CacheManager对象往缓存中添加项时,也将请求推给Cache对象。如果已经存在同样key的项,先移除再添加到内存和后端存储中。如果后端存储是NullBackingStore类型,仅仅加到内存中。如果缓存中的项超过预定的限制的时候,BackgroundScheduler对象开始清除。添加的时候可以使用重载的Add方法设定过期策略列表,清除策略,和继承ICacheItenRefreshAction接口的对象。这个对象被用来刷新过期的项。
当添加项时,如果项不在内存中的hash table中,Cache对象首先创建一个虚的缓存对象,并加入内存的hash table中。之后给in-memory hash table中的这项上锁,并添加到后端存储中,最后用新的项替代这个已存在的项。如果当写后端存储的时候出现了异常,则移除这个虚拟的项。缓存组件加强了异常处理的保证。这意味着如果一个Add操作失败了,缓存可以回滚到之前的状态。这点对Remove和Flush方法相同。
BackgroundScheduler对象周期的监控缓存中项的生存周期。当一项过期了,BackgroundScheduler对象首先移除它,可选的通知应用程序。应用程序负责刷新缓存。
设计细节
CacheManager类是缓存组件的门户。它提供了所有添加,获取,移除的方法,并且是线程安全的。
当你使用CacheFactory初始化CacheManager实例时,他在内部创建CacheManagerFactory对象。CacheManagerFactory类创建其他用来实现CacheManager对象的其他内部类。
使用相同的名字来创建CacheManager对象,会返回相同的实例。因为对于一个特定的名字,所有的caches共享同一个内存中数据。为了创建多个缓存,使用多个名字。注意不同的缓存意味着有不同名字的缓存,不能共享相同的后端存储器。每一个CacheManager对象只能有唯一的一个后端存储。
Cache对象从CacheManager对象那收到请求,并实现所有的后端存储和内存中的操作。Cache对象包含hash table来保存in-memeory形式的数据。数据被封装成CacheItem对象,包含数据,以及key,优先级,RefreshAction对象,和过期策略。Cache对象还使用锁来控制对在缓存中的项的访问,包括应用程序和BackgroundScheduler对象的访问。Cache对象提供线程安全。
BackgroundScheduler对象负责过期缓存项和清除低优先级项。PollTimer对象触发过期循环和数字上的限制触发清除过程。在配置文件中有设定。
BackgroundScheduler对象是活动对象模型的实现。这意味着其他对象告诉BackgroundScheduler是否存在调用对象的线程。当发生调用,BackgroundScheduler封装请求作为一个消息,并放入消息队列,而不是直接执行请求。这个队列是生产者-消费者模式的例子。当BackgroundScheduler准备处理消息时,一个内部的线程从队列上拉一个消息过来。事实上,BackgroundScheduler序列化所有的清除和过期的请求。
BackgroundScheduler对象顺序的从队列上移除消息并处理请求。对于过期处理过程,他调用ExpirationTask类的Run方法。对于清除过程,调用ScavengingTask类的run方法。在单一线程上序列的执行操作的好处是,确保代码运行在单线程的环境中,这使得代码和他的作用易于理解。
缓存组件中的缓存存储类是DataBackingStore,IsolatedStorageBackingStore,NullBackingStore。如果你想开发自己的后端存储,你的类必须继承抽象类BaseBackingStore,它实现IbackingStore接口。这个类包含通用策略的实现。
DataBackingStore类是当后端存储使用数据访问组件时使用的。可以配置使用的数据库的名字。IsolatedStorageBackingStore在指定域的隔离存储器中存储缓存项。可以配置使用隔离存储器的名字。缓存组件使用IBackingStore接口来同所有的后端存储交流。
DataBackingStore和IsolatedStorageBackingStore可以对缓存数据进行加密。可以配置使用对称密钥算法提供器。这个提供器在解密的时候也需要。
3.2过期过程的设计
缓存组件的过期过程是由BackgroudScheduler执行的。它周期的检查hash table中的CacheItems对象来看是否已经过期。你可以配置CacheManager实例来控制过期循环检查的频率。
缓存组件提供了四种过期策略:
l 绝对时间。这意味着缓存项在特定的时间过期。
l 偏移时间。指每次过期相隔特定的时间。默认是2分钟。
l 扩展格式。允许你非常详细的指定过期时间。
l 文件依赖。指定文件被修改了,则缓存项过期。
前三种策略是时间相关的。对于不稳定的缓存项,你可以使用基于时间的过期策略,如规则的数据刷新和只在指定的时间内有效。基于时间的过期策略使你设定这种策略—仅在当前数据流通的情况下保存数据。例如,你想写一个应用通过获取Web站点更新的频率的数据来跟踪流通的交换率,你可以在流通率保持常数的时候缓存流通率。你可以根据Web站点更新的频率来设定过期的策略。
第四种,是基于通知的过期策略。是根据特定的文件来定义缓存项的可用性。当缓存项改变了,缓存项就是无用的,被移除出缓存。
Add方法有两个重载。一个是默认过期策略,NeverExpired。另一个可以让你来设定过期策略(包括自己定义的)。这个策略可以是一个数组,当任何一个策略生效时,过期发生。
标记和清除
过期有两部分过程。标记和清除。分步执行是避免如果应用使用了BackgroundScheduler试图过期的缓存项而导致的冲突。
在标记过程中,BackgroundScheduler备份当前的hash table并检查每一项是否过期。锁定正在被检查的项。如果一项是符合被过期的条件,则给这个缓存项设定标记。
在清除过程中,BackgroundScheduler重新检测每一个被标记的缓存项是否正在被访问。如果正在被访问,则继续留在缓存中,否则清除,并产生WMI事件。
回报信号
可选的,开发者可以使用Add方法的重载指定当某个缓存项过期并被移除后向应用程序发一个回报信号。如果必要,应用可以刷新这项。
应用退出再重启,很多项可能已经过期。在这种情况下,这些项留在缓存中,并发出回报信息。然而,如果应用请求在第一次过期循环检查之前终止这些项,缓存会执行一个回报,并返回一个null给应用。这确保了在每一个项终止前发送一个回报,从而防止应用收到一个过期的项。
3.3清除过程的设计
缓存组件中的清除过程是由BackGroundScheduler对象执行的。它每次添加缓存项的时候检查缓存中的数量是否已经达到了预定的限制。用配置来设定这个限制。你也可以设定清除时移出的项的数量。
当向缓存添加项的时候,可以给它四种优先级:低,正常,高,不被移除。BackGroundScheduler对象通过优先级和最后访问时间来决定哪些项应该被移出。例如,刚刚被访问的低优先级的项可能先于三年没有被访问的高优先级的项。
NotRemoveable优先级是你想使某项被保持到他过期。