1:前言
园子里有很多关于开发库的介绍,但是大多都是关于如何使用开发库的。我想其实开发库中蕴含的思想才是重要的,这里我就撰文补充。本文主要阐述其中的缓冲模块。
2:概述
该缓冲模块是个Key-Value型的系统,也就是时下流行的NoSql。当然微软的Demo中提供的只是本地缓冲不过你可以通过简单的扩展就可以实现分布式的缓冲。在这个Key-Value系统中,它可以分为两大功能块。一个功能块是过期或者称之为缓冲而另一个功能块是存储,其中存储以插件或是接口的形式与过期耦合起来共同组成缓冲模块。过期模块主要处理Key,当然这里的Key是经过扩展的,它里面包含了很多其他元素比如过期时间,过期类型(相对以及绝对,我倒是没有看到相对过期的逻辑)等。在过期模块中核心的逻辑就是判断过期,这里有个很巧妙的地方就是他通过周期轮训Key的克隆来判断过期而不是轮训Key集合本身。另一个逻辑块就是存储,在这里微软已经为我们扩展了两种存储就是文件和数据库,可惜没有内存这不能不说是一种遗憾。在这个模块中我认为值得一提的是存储信息的格式,给予通用性考虑,他把所有的对象包含值类型和引用类型都存储成Byte数组。两大功能块通过接口进行协作,过期块持有存储块的接口,在过期快判断到过期或是其他操作的时候通过存储块的接口来对存储的信息进行管理。
3:整体代码结构
微软一贯的作风就是代码一大坨,这里我用图的形式予以介绍,类图如下
Cache类,它的对象持有以HashTable存储的Key和存储层的接口,它实现了对Key的增删改查,当然在Key的增删改查的同时也通过持有的存储层接口对Value进行了增删改查。
CacheManager类,它的对象是持有Cache类的实例和过期判断类的实例。这里值得提一点的就是在该类实例化成对象的时候后台线程就启动了,因此这个对象在扩展成分布式的时候应该是个单件,要在服务启动的时候创建。这个对象是缓冲和存储耦合的地方,其实这句话不是很恰当大家就慢慢体会吧。
ExpirationPollTimer类,它的对象是个计时器负责设置监测过期的频率
BackgroundScheduler类,它的对象在判断是否过期。CacheManager通过ExpirationPollTimer周期的执行BackgroundScheduler。
BaseBackingStore类,抽象类实现了IBackingStore接口
IsolatedStorageBackingStore类 ,存储层扩展中的文件存储,实现了IBackingStore接口
DataBackingStore类,存储层扩展中的数据库存储
接下来详细描述两个问题
首先解释如何判断过期的,其次就是如何实现存储的,特别是数据库存储的。
我们先说说第一个问题,在CacheManager中通过ExpirationPollTimer周期性的启动BackgroundScheduler来判断过期。因此这里的逻辑核心就是BackgroundScheduler。
在CacheManager实例化的时候通过
pollTimer.StartPolling(backgroundScheduler.ExpirationTimeoutExpired);
ThreadPool.QueueUserWorkItem(unused => BackgroundWork(Expire));
expirationTask.DoExpirations();
来周期性的删除过期的对象,在 DoExpirations方法中
1:获取CacheItem集合,Hashtable liveCacheRepresentation = cacheOperations.CurrentCacheState;(Hashtable)inMemoryCache.Clone();
2:标示过期MarkAsExpired(liveCacheRepresentation);
3:删除键int expiredItemsCount = SweepExpiredItemsFromCache(liveCacheRepresentation);
4:删除值if(expiredItemsCount > 0) instrumentationProvider.FireCacheExpired(expiredItemsCount);
另外比较巧妙的是,微软在添加的时候也是可能激活过期判断流程的。但是这个过期判断不是判断所有的而是判断一部分。{在我设计的缓冲模块中没有该逻辑但是我实现了过期检测超时以及缓存
最大上限,缓存过期检测超时是指在本次缓存过期检测中如果超过这段时间则放弃本次过期检测,另一个是缓存最大上限这个是指如果缓存项超过这个数量则忽略过期时间,过期将按照先进先出策略
执行,微软的这个激活过期检测流程实际上也是在避免本次检测时间过长影响前端响应,这个逻辑是正确的,以后我将修正我的这部分缓存逻辑}
接下来在说说存储层
对于文件存储体它使用的是IsolatedStorageFile对象,这个大家找找MSDN自己看看就行了,这里不再累述。我们还是来看看数据库存储体,它是存储在表中的表结构如下
GO
/****** 对象: Table [dbo].[CacheData] 脚本日期: 06/24/2010 18:35:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[CacheData](
[StorageKey] [int] NOT NULL,
[PartitionName] [varchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Key] [varchar](128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Value] [image] NULL,
[RefreshAction] [image] NULL,
[Expirations] [image] NULL,
[ScavengingPriority] [int] NOT NULL,
[LastAccessedTime] [datetime] NOT NULL,
CONSTRAINT [PK_CacheData] PRIMARY KEY CLUSTERED
(
[StorageKey] ASC,
[PartitionName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
操作是通过六个存储过程来实现的,他在底层封装了一些DB的操作。这里大家可以看到Value是以Byte数组存储的。这里我想提的是,它的存储有区的概念,一个区一个表而不是一个缓存对象一个表或是所有缓冲对象一个表。
4:后记
这将是一个系列,我将逐个介绍开发库的其他模块,当然以后我也会介绍其他的开源项目。