设计缓存的目的
缓存模块可以用来实现下列功能:
本节讲述一些缓存模块设计的亮点,和设计的细节。还包括一些过期处理的设计和扫描处理的设计。
1、设计亮点
上图显示的是缓存模块中关键类之间的关系。
当你用CacheFactory初始化一个CacheManager对象的时候,在内部创建了一个CacheManagerFactory对象,创建了一个Cache对象。常见Cache对象之后,在后端存储的所有数据被加载到内存中,放在Cache对象中。然后应用可以用CacheManager发出请求,获取数据添加数据,删除数据。
当应用向CacheManager发送请求,使用GetData方法从缓存中获取数据的时候,CacheManager对象会请求Cache对象。如果请求项存在的话,返回缓存的数据,如果不存在返回null,如果对象过期,也返回null。
当应用使用CacheManager的add方法,向缓存中添加项的时候,CacheManager同样将请求发送给Cache对象。如果已经存在一个同样key的项的话,Cache首先会删除原来的项,然后加入新项。如果缓存的后端存储是默认的NullBackingStore,数据只是写入内存。如果添加项的时候,达到了缓存存储数据量的上限的话,BackgroundSchedule对象就会开始扫描,进行过期处理。
如果添加的项在内存缓存中没有的话,Cache首先创建一个复制品,然后添加到内存的hashtable中,然后锁定内存中的项,添加项到后端存储,然后替代内存hashtable中的项。如果在写入后端存储的时候发生异常,就会从内存缓存中删除那个复制品。缓存模块拥有很强的异常安全保护,意味着如果加入项的时候发生异常,缓存的状态可以回滚的添加之前的状态。换句话说,除非添加项成功,否则缓存的状态不会改变。(同样也适用于删除项和清空项)
BackgroundSchedule对象周期性的监测缓存中的项。当一个项过期,BackgroundSchedule首先会移除项,然后有可能会通知应用,项被移除。这时候,应用的责任就是更新缓存。
2、设计细节
CacheManager是一个接口类型,所有的缓存操作都通过接口调用。如果开发者没有修改直接使用缓存模块的话,CacheManager对象提供了访问缓存需要的全部方法,获取、添加、删除、清空项。每个方法都是线程安全的。
使用CacheFactory类创建一个CacheManager对象,还会创建一个CacheManagerFactory类。CacheManagerFactory类会创建实现一个CacheManager所需要的内部类。
不同的缓存不能共享相同的后端储存,每个CacheManager都只能对应一个后端存储。Cache对象接收从CacheManager对象发送过来的请求,实现了所有的在内存缓存和后端储存之间的操作。包含了一个hashtable来存储内存中对应的数据,一个数据项作为一个CacheItem对象。这个对象包括数据本身,还包括了项的key,优先级,RefreshAction对象,和过期策略。这些信息都存放在一个hashtable中,Cache也是线程安全的。Cache用一个同步的hashtable控制应用和BackgroundSchedule对缓存中的项的访问。
BackgroundSchedule对象的责任就是进行过期处理,扫描低级别的项。PollTimer对象激活过期循环,一个数字是一次扫描处理移除的项目。这些都在配置文件中可以设置。
缓存模块中的缓存存储类包括DataBackingStore、IsolateStorageBackingStore、和NullBackingStore。如果你想要实现自定义的后端存储,你需要实现IBackingStore接口,或者是继承BaseBackingStore类,这个类也实现了IBackingStore接口。这个基类包含了实现任何后端存储所需要的策略和工具方法。
当后段存储是使用数据库的时候,可以用DataBackingStore类。在配置工具中,对应于一个数据库实例的名称。
DataBackingStroe和IsolatedBackingStore类可以在持久化之前加密缓存项。加密缓存项可以通过配置来激活。使用配置工具,缓存存储可以使用配置好的加密算法provider。provider的name在从缓存中读取数据,然后在显示之前解密的时候同样需要。
过期策略的设计
缓存模块的过期处理是靠BackgroundSchedule来执行的。它会周期性的检查hashtable中的缓存项,看是否有过期项。你可以在配置缓存模块的时候设置检查周期的时间。
缓存模块提供了四个过期策略:
前三个过期选项都是以时间为基础的,如果数据的更新时间是规则的,或者是在一个特定的时间更新,你可以使用前三个选项。
第四个,文件依赖,是以通知为基础的。它指定缓存项的过期依赖一个特定的文件,如果文件被修改,缓存项就过期,就会删除缓存项。
Add方法有两个重载,一个设置了默认的过期策略,NeverExpired。另外一个你可以设置适合自己的过期策略。你甚至可以设置自定义的策略,如果你为一个缓存项设置了多个策略, 如果由一个策略符合条件,缓存项就会过期。
标记和清除
过期包括两个步骤。一个就是标记,另外一个就是清除。分为两个步骤,就是为了避免BackgroundSchedule清除一个应用正在使用的缓存项可能发生的冲突。
在标记过程中,BackgroundSchedule会拷贝一份hashtable,然后检查里面的每一个缓存项,看看有没有过期的。这时候会锁定缓存项,如果发现过期的,就会打个标记。
在清除过程中,BackgroundSchedule会重新检查缓存项的标记,看看在打上标记之后是否有访问记录。如果有访问记录,就保留缓存项。如果没有,就过期它,删除它。缓存项过期的时候,会引发WMI事件。
回调callbacks
开发者可以选择add方法的另外一个重载,使得在缓存项过期,删除缓存项的时候发通知给应用,如果有必要,应用可以更新缓存。
扫描处理的设计
缓存模块的扫描处理也是由BackgroundSchedule执行的。在每次有缓存项加入的时候都会检查,缓存项的数目是否达到设置的最大值。数量是设置在CacheManager实例上的。
当加入一个缓存项的时候,可以设置优先级,有四个选项:Low,Normal,High,NotRemovable。删除的时候会按照优先级的高低进行处理,优先级低的会先被清除。默认值是Normal。
当你想让一个缓存项直到它过期才被清除的话,可以设置为NotRemovable。最好不要设置为NotRemovable,因为缓存是用来提升性能的,不应该被作为一种持久化手段。
不想过期处理包括两个过程,扫描处理只有一个过程。