SOA Framework 开发使用手册
2 Cache
2.1 模块概述
在公共组件库中的MCS.Library. Caching命名空间下包含了框架中实现以内存作为缓存介质的功能模块,该模块有如下特点: ? ? ?
支持将缓存分类。
缓存会随着依赖项的状态改变而失效 缓存的统一管理,以及定期处理过期缓存。
使用Cache模块需要4大类对象 ? ? ? ?
自定义的缓存项,封装需要缓存的数据。 缓存依赖项,定义缓存项的过期依赖。 缓存容器,存放自定义的缓存项。
缓存管理器,注册缓存容器,并统一的管理所有缓存容器。
2.2 使用Cache
在一般应用程序中,我们总会使用Cache这种用空间换时间的方式来提高性能。Cache的实现方案多种多样,其中,通过在内存中的字典来实现Cache,是一种常见的方案。
ObjectCacheQueue.Instance.Add(\, \); 看看下面的代码示例:
在这段代码示例中,ObjectCacheQueue是公共组件库的一个Cache类,使用起来就象一个字典,和CLR的Dictionary
ObjectCacheQueue从CacheQueue
2.3 Cache的失效
Cache中数据的更新和失效是所有解决方案中都会面临的难点。它意味着字典中的每一项的生存期,都会和一个特定的业务规则进行关联。当满足某些条件时,Cache字典中的值就会进行变更。在公共组件库中,这种机制是通过Cache依赖项来实现的。
如果我们的Cache仅仅是一个字典,那么它就没有存在的必要。真正的核心特性将在下一节中进行介绍。
2.4 Cache依赖项
Cache中数据的更新和失效是所有解决方案中都会面临的难点。它意味着字典中的每一项的生存期,都会和一个特定的业务规则进行关联。当满足某些条件时,Cache字典中的值就会进行
?《SOA Framework》 6
SOA Framework 开发使用手册
变更。
在公共组件库中,这种机制是通过Cache依赖项来实现的。 下面的代码,会指定某一个Cache项的过期时间。
AbsoluteTimeDependency dependency = new AbsoluteTimeDependency(DateTime.Now.AddSeconds(10)); ObjectCacheQueue.Instance.Add(\, \, dependency); 代码中的dependency变量,表示了一个依赖项,它和通过字典的Add方法和一个字典项关联在一起,决定了字典项有效期。
在上面的代码中,AbsoluteTimeDependency是一个绝对时间过期的依赖项,例子中指定了10秒钟之后为过期时间。
公共组件库提供了几种常见依赖项,如下表:
类名 AbsoluteTimeDependency SlidingTimeDependency FileCacheDependency CookieCacheDependency 说明 Cache项到了某一时间点失效 Cache项一段时间不使用后失效 文件改变后,Cache项失效 Web应用特有的,某个cookie失效后,Cache项失效 UdpCacheNotifierDependency 当程序收到一个特定Udp包时,Cache项失效 这些依赖项的具体介绍,会在附录中详细说明的。
有一种特殊的依赖项叫做MixedDependency,它会将几种不同的依赖项组合在一起,其中有一个满足条件,就会使Cache项失效。如下所示:
AbsoluteTimeDependency timeDependency = new AbsoluteTimeDependency(DateTime.Now.AddSeconds(600)); FileCacheDependency fileDependency = new FileCacheDependency( @\, @\); MixedDependency mixedDependency = new MixedDependency(timeDependency, fileDependency); ObjectCacheQueue.Instance.Add(\, \, mixedDependency); 代码中的MixedDependency可以将其它依赖项组合起来使用(包括MixedDependency自己),在上述例子中,文件变更或超时都会导致Cache项的失效。
2.5 Cache数据的清理
依赖项(Dependency)的使用提供了Cache数据失效的机制。在依赖项起作用的时候,用户访问Cache的时,会发现数据已经不见了。但是,这并不意味着数据从内存中完全消失。例如,如果使用AbsoluteTimeDependency和SlidingTimeDependency这种时间相关的依赖项,如果没有一个后台清理程序,是不可能自动地随着时间的推移清除内存中失效的Cache项的。
至于FileCacheDependency和UdpCacheNotifierDependency,它们会在文件变更或接受到基于Udp协议的数据包后,从内存中删除数据。实际上,这两种依赖项也是有后台线程在进行监控状态变化。
CookieCacheDependency比较特别,只有当前应用是Web应用,且有Http请求到达时,才
?《SOA Framework》 7
SOA Framework 开发使用手册
能够知道当前请求中的Cookie是否还有效,因此,如果某个客户端一直没有发送Http请求,那么和CookieCacheDependency关联的Cache项就会一致保存在内存中。因此,建议使用CookieCacheDependency的Cache项最好还与AbsoluteTimeDependency、SlidingTimeDependency等其它依赖项进行关联。
用户一般不用关心Cache项在内存中的生存期,但是内存的清理确实是需要考虑的一个问题。
为了保证已经失效的Cache项不会长时间存在于内存中,公共组件库会启动一个后台线程,它会按照一个时间间隔来检查每一个Cache项是否失效。我们可以在配置文件中设置轮询的时间间隔。
Web应用需要在config(web.config)文件中配置一个HttpModule来实现此功能。
所有的Cache依赖项都是从DependencyBase派生而来的,因此我们要使编写一个自己的Cache依赖项,也是从这个类派生下来即可。DependencyBase类的大致定义如下:
?《SOA Framework》 8
SOA Framework 开发使用手册
public abstract class DependencyBase : IDisposable { public CacheItemBase CacheItem { get { return this.cacheItem; } } // 属性,获取或设置Cache项最后修改时间的UTC时间值 public virtual DateTime UtcLastModified { get { return this.utcLastModified; } set { this.utcLastModified = value; } } // 属性,获取或设置Cache项的最后访问时间的UTC时间值 public virtual DateTime UtcLastAccessTime { get { return this.utlLastAccessTime; } set { this.utlLastAccessTime = value; } } // 属性,获取此Dependency是否过期 public virtual bool HasChanged { get { return false; } } /// 当Dependency对象绑定到CacheItem时,会调用此方法。此方法被调用时, /// 保证Dependency的CacheItem属性已经有值 internal protected virtual void CacheItemBinded() { } public virtual void Dispose() { } } 如果写一个自定义的Cache依赖项,最简单的实现就是从DependencyBase派生,并且重载HasChanged。
?《SOA Framework》 9
SOA Framework 开发使用手册
public class MyFooDependency : DependencyBase { public MyFooDependency() { } public override bool HasChanged { get { } } } return expiredFlag; private bool expiredFlag = false; 只要重载了HasChanged属性,访问Cache或清理Cache的时候,就可以根据这个属性来进行失效判断。象AbsoluteTimeDependency之类的依赖项,就是在HasChanged属性实现中对当前时间进行了判断。
上面这个例子,关键是要看expiredFlag什么时候被置为true。
由于后台清理程序或Cache字典会经常读取这个属性,因此设计时需要使读取这个属性的操作开销极小。有些Cache依赖项的实现就是非常简单,如AbsoluteTimeDependency,有些复杂一些了。
例如,我们需要定期检查数据库中某个数据的时间戳来决定Cache的失效,这就是一个开销较大的操作。
这种操作通常是启动一个后台线程来实现的,它定期检查数据库中的数据,然后更新expiredFlag。
首先需要有一个合适的入口点来启动这个线程。
由于DependencyBase的CacheItemBinded方法发生在Cache数据和依赖项被添加到Cache字典后,因此可以通过重载CacheItemBinded方法来实现此功能。
请看下面的例子:
?《SOA Framework》 10