Discuz!NT 缓存设计简析[转]

原文地址:http://nt.discuz.net/showtopic-56907.html
        作为一个社区类型软件,大并发支持和高效稳定运行永远是“硬道理”,而有效安全的使用_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
缓存恰恰能起到事倍功半的效果。而.NET本身所提供的缓存机制又显得过于“单薄”,比如说订_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
制不太灵活方便, 缓存对象之间层次感不强, 使用时缺乏统一的管理等等。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        Discuz!NT缓存产生背景:_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        在去年五月份我加入Discuz!NT项目组时,发现这个项目当时还未使用缓存机制。主要原因_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
是项目还处于起步阶段,很多东西还只是有想法,但未付诸实施,或还没找到合适的方案, 而_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
缓存就是其中一个到底该不该使用,如果使用的该到底能多大程度缓解数据库压力以及开发成本_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
的东西。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        我当时正好有一个比较好的“原型”(从一本书上看到的源码),也就是今天Discuz!NT所_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
使用的缓存机制的雏形,但当时它在功能上还很不健全且存在一些“致命的” BUG, 但实现简_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
单的缓存数据对象还是绰绰有余的,于是我通过一个简单的测试用例(缓存数据表和StringBuilder_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
对象)和雪人一起讨论并分析后得到一些数据,基本上肯定了使用缓存解决对数据库象中经常访_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
问但又不经常更新的数据进行缓存的使用方案,同时也要求这个缓存机制要使用起来尽可能的简_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
单,同时功能扩展要非常方便。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        因此本人就在这个“原型”的基本上进行了一段时间的功能扩展和BUG修正才得到今天大家_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
所看到的这部分代码。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        现在将Discuz!NT的缓存架构说明如下,先请大家看一下Discuz!NT架构图:
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        其实这个构架说白了就是一个标准的“策略”模式,为了对比方便,我把策略模式的结构_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
图放在下面:
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        看到了吧,里面的DNTCache就是“策略”模式的应用场景,而DefaultCache , ForumCache_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
,RssCache等等就是相应的具体策略,每一种策略都会对.net所提供的缓存机制进行一番“订制”_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
,以实现不同的用途。比如系统DefaultCache在对象到期时提供数据再次加载机制,而ForumCache_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
而不使用这种机制,另外还有缓存的到期时间几种策略也各不相同,这都是根据具体的应用场景_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
"量身订制"的。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        说到这里,您所要做的就是下载一份源码按上图索骥就可以把整个缓存机制搞清楚。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        下面对缓存设计所采用的几种技术做一下简要说明。包括XML,XPATH ,"单件模式" 以及跨_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
web园共享数据。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        首先请看一下代码:(xml xpath)
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
1 //要存取的xpath格式路径_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
2 //要缓存的对象_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
3 public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
virtual_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
void AddObject(string xpath, object o ,string[] files)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
4 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
5 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
6 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//整理XPATH表达式信息_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
7 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string newXpath = PrepareXpath(xpath);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
8 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
int separator = newXpath.LastIndexOf("/");_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
9 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//找到相关的组名_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
10 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string group = newXpath.Substring(0,separator  );_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
11 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//找到相关的对象_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
12 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string element = newXpath.Substring(separator +_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
1);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
13   _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
14 XmlNode groupNode = objectXmlMap.SelectSingleNode(group);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
15 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//建立对象的唯一键值, 用以映射XML和缓存对象的键_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
16 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string objectId="";_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
17 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
18 XmlNode node = objectXmlMap.SelectSingleNode(PrepareXpath(xpath));_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
19 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if ( node !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
20 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
21   objectId = node.Attributes["objectId"].Value;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
22 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
23 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if(objectId=="")_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
24 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
25   groupNode = CreateNode(group);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
26   objectId= Guid.NewGuid().ToString();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
27 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//建立新元素和一个属性 for this perticular object_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
28   XmlElement objectElement = objectXmlMap.OwnerDocument.CreateElement(element);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
29   XmlAttribute objectAttribute =objectXmlMap.OwnerDocument.CreateAttribute("objectId");_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
30   objectAttribute.Value = objectId;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
31   objectElement.Attributes.Append(objectAttribute);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
32 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//为XML文档建立新元素_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
33   groupNode.AppendChild(objectElement);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
34 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
35 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
else_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
36 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
37 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//建立新元素和一个属性 for this perticular object_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
38   XmlElement objectElement = objectXmlMap.OwnerDocument.CreateElement(element);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
39   XmlAttribute objectAttribute =objectXmlMap.OwnerDocument.CreateAttribute("objectId");_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
40   objectAttribute.Value = objectId;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
41   objectElement.Attributes.Append(objectAttribute);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
42 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//为XML文档建立新元素_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
43   groupNode.ReplaceChild(objectElement,node);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
44 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
45 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//向缓存加入新的对象_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
46 cs.AddObjectWithFileChange(objectId,o,files);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
47 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
48 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
49
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        为什么要用XML, 主要是为了使用XML中的层次化功能以及相关的结点添加,替换,移除,_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
还有就是当希望对缓存的结构信息进行“持久化”操作时会很方便等。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        XPATH 便于能过层次表达式(hierarchical expression) 对XML文件进行查找搜索。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        通过上面或其它的类似代码,就可以构建起一个xml树来管理已加入到系统的缓存对象了。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        使用"单件模式"模式生成全局唯一的“应用场景”,因为缓存这种东西通常在存储共享_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
数据时它的效果最好,编码也最容易实现和管理,同时项目本身基本上就是对经常访问但不_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
经常改变的数据库数据(可看成是共享数据)进行缓存,所以使用单件模式就顺理成章了。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        请看如下代码:_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
static DNTCache GetCacheService()_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
{_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if (instance ==_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
{_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
lock (lockHelper)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
if (instance ==_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    instance
=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
new DNTCache();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//检查并移除相应的缓存项_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//注:此处代码为即将发布的2.0版本中的代码类,如果您想了解其中_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
       
//的代码可参见开源版本中的Discuz.Forum.cachefactory.cs文件中_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//相应函数_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
instance=CachesFileMonitor.CheckAndRemoveCache(instance);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
return instance;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        小插曲:_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        1.项目到了beta版时出现了无法跨web园共享数据的问题。它的表现是这样的,当你在IIS_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
服务的应用程序池中设置2个或以上的WEB园时,这时你在后台更新缓存时,就是出现缓存_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
“隔三差五”数据不更新或轮换更新的情况。说白了,就是只有一个应用进程中的数据缓存_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
被更新,而其余的进程中所有数据还没事人似的保留原有的面貌。这个问题主要是因为static_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
的数据实例(也就是上面所有的单体代码中的对象)虽然而当前进程中“唯一”,但在其它进程_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
中却各自都有一个造成的。一开始我也很惊讶,为什么微软不能像提供“全局”钩子那样的技术_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
一样提供一种跨WEB园来共享数据的技术或关键字呢,不过一转念也猜出了一二分,必定多WEB园_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
是一种让程序(WEB)跑起来更加安全,稳定快速的“解决方案”。 因为谁都不好说自己的程序_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
一点BUG没有,即有真有这样的代码,但当遇上运行环境这个因素后,也会表现得有些难以控制。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
但微软通过web园这个技术就会把运行在几个不同进程下的程序相互隔离,使其谁也不影响到谁,_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
即使其中一个进程down了,而其它进程依就会继续正常 "工作" 。因此程序中的对象实例和所有_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
资源每个进程中都会保存一份,完全相同。而如果引用共享机制就有可能出现当进程共享的数据_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
或程序对象出现问题时,所有进程就可能都玩完了, 因此就需要进程隔离。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        说是这么说,但总也要想个办法解决当时面临的问题吧。记得在豪杰工作期间,一次老梁_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
给我们开会,其中的一段话我至今还记忆犹新,他说CPU访问内存的速度和访问硬盘的速度在某些_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
情况下是相近的,如果我没理解的话比如说“虚拟缓存”或最新频繁访问的硬盘区段,这些地方_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
的代码或文件会有比较高的运行和访问效率。因此,我想到了使用文件标志关联的方法来解决这_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
个多进程问题。接着就顺理成章的使用了文件修改日期这个属性进行在多进程下缓存是否更新的_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
依据了,大家可以到开源下载包中的config文件夹下把一个cache.config的文件,对应最新的数_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
据项再回过头来看如下代码就会一清二楚了:
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
static DNTCache CheckAndRemoveCache(DNTCache instance)//_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
{_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
     
//当程序运行中cache.config发生变化时则对缓存对象做删除的操作_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
      cachefilenewchange = System.IO.File.GetLastWriteTime(path);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
     
if (cachefileoldchange != cachefilenewchange)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
      {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
               
lock (cachelockHelper)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                   
if (cachefileoldchange != cachefilenewchange)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                    {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                       
//当有要清除的项时_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        DataSet dsSrc =_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
new DataSet();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        dsSrc.ReadXml(path);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                       
foreach (DataRow dr in dsSrc.Tables[0].Rows)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                           
if (dr["xpath"].ToString().Trim() !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
"")_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                            {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                DateTime removedatetime
= DateTime.Now;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                               
try_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                    removedatetime
= Convert.ToDateTime(dr["removedatetime"].ToString().Trim());_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                               
catch {;}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                               
if (removedatetime > cachefilenewchange.AddSeconds(-2))_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                   
string xpath = dr["xpath"].ToString().Trim();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                    instance.RemoveObject(xpath,
false);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                                }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                            }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        cachefileoldchange
= cachefilenewchange;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                        dsSrc.Dispose();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                    }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
                }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
      }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
     
return instance;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        2.另外需要说明的是在4月份时缓存机制出现了一些问题,比如缓存数据丢失以及在.net2下_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
的死循环的问题,后来在雪人的建议下采用每个缓存都有缓存标志来解决数据丢失的问题。也就_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
是如下的代码段:_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
1 //添加时_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
2 public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
virtual_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
void AddObject(string xpath, DataTable dt)  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
3 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
4 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
lock(lockHelper)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
5     {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
6 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if(dt.Rows.Count>0)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
7 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
8   AddObject(xpath+"flag", CacheFlag.CacheHaveData);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
9 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
10 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
else_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
11 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
12   AddObject(xpath+"flag", CacheFlag.CacheNoData);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
13 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
14 AddObject(xpath, (object) dt);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
15     }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
16 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
17 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
18 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
19 //获取时_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
20 public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
virtual_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
object RetrieveObject(string xpath)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
21 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
22 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
try_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
23 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
24 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
object cacheObject = RetrieveOriginObject(xpath);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
25   CacheFlag cf = (CacheFlag) RetrieveOriginObject(xpath+"flag");_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
26   _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
27 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//当标志位中有数据时_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
28 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if(cf ==CacheFlag.CacheHaveData)  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
29   {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
30 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string otype = cacheObject.GetType().Name.ToString();_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
31 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
32 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//当缓存类型是数据表类型时_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
33 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if(otype.IndexOf("Table")>0_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
34             {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
35   System.Data.DataTable dt = cacheObject as DataTable;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
36 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if ((dt ==_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null) || (dt.Rows.Count ==_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
0))_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
37                         {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
38 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
return_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
39                         }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
40 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
else _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
41                         {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
42 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
return cacheObject;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
43                         }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
44     }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
45         _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
46 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
47
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        而死循环的问题主要是因为.net2下的缓存回调加载机制和程序本身的一个BUG造成的,目前_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
已修正, 大家请放心使用。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        目前已开发但还未使用的功能:_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        1.一键多值:请看DNTCache代码段中的AddMultiObjects(string xpath,object[] objValue)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
,获取时使用object[] RetrieveObjectList(string xpath)方法返回即可,这样就可以用一个xpath_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
来存取一组对象了。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        它的实现代码也相对简单,这里就不多说了,只把代码贴在此处。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
virtual_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
bool AddMultiObjects(string xpath,object[] objValue)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
lock(lockHelper)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
{_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
//RemoveMultiObjects(xpath);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if (xpath !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
&& xpath !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
""_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
&& xpath.Length !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
0_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
&& objValue !=_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
null)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
for (int i =_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
0; i < objValue.Length; i++)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    AddObject(xpath
+_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
"/Multi/_"_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
+ i.ToString(),objValue); _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
return_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
true;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
  }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
 
return_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
false;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
}_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        2.批量移除缓存_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
          它主要是利用XML有按路径层次存储的特点才这样做的,主要是去掉位于当前路径下的所有_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
子结点的缓存数据。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        它的函数声明如下:RemoveObject(string xpath, bool writeconfig)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        它的实现代码也相对简单,这里就不多说了, 只把代码贴在此处。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
1 public_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
virtual_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
void RemoveObject(string xpath, bool writeconfig)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
2 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
3 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
lock(lockHelper)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
4 {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
5 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
try_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
6   {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
7 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if(writeconfig)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
8   {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
9                         CachesFileMonitor.UpdateCacheItem(xpath);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
10   }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
11 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
12   XmlNode result = objectXmlMap.SelectSingleNode(PrepareXpath(xpath));_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
13 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//检查路径是否指向一个组或一个被缓存的实例元素_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
14 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
if (result.HasChildNodes)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
15   {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
16 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//删除所有对象和子结点的信息_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
17     XmlNodeList objects = result.SelectNodes("*[@objectId]");_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
18 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string objectId =_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
"";_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
19 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
foreach (XmlNode node in objects)_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
20     {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
21     objectId = node.Attributes["objectId"].Value;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
22     node.ParentNode.RemoveChild(node);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
23 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//删除对象_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
24     cs.RemoveObject(objectId);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
25     }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
26   }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
27 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
else_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
28   {_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
29 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//删除元素结点和相关的对象_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
30 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
string objectId = result.Attributes["objectId"].Value;_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
31     result.ParentNode.RemoveChild(result);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
32     cs.RemoveObject(objectId);_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
33   }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
34 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
35 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
//检查并移除相应的缓存项_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
36   }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
37 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
catch_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
38   {    //如出错误表明当前路径不存在_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
39   }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
40 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
41 }_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
42 _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
43
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
    _¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        已开发出来,但却去掉了的功能。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        在正式版出现之前,后台管理中有记录缓存日志的功能,它的实现方式是基于"访问者"模式实现的_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
(大家应该可以在项目中找到这个类LogVisitor)。但因为后来不少站长反映日志表操作的过于频繁导_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
致日志记录急剧增加,而把这部分功能拿下了。我在这里说出来就是想给大家提个醒,对于新功能或新_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
技术的追求要非常谨慎,要不就会出现您费尽千辛万苦开发的功能,最后却没人买帐就郁闷了。_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
        最后需要说明的就是,为什么要先把这块功能先发到园子里来。因为我们产品的Discuz!NT2.0产品_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
即将发布,而整个产品的架构也出现了不少变化,而由于缓存结构相对稳定,所以变化的不大。这才在_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
今天发个BLOG讲给大家的,下一篇关于DISCUZ!NT架构的文章要等到正式版发布之后了。到时大家下_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
载代码之后再对照新代码给大家聊聊这个产品的其它设计思路(按我的理解)。
_¸¬´{ßWnt.discuz.netµ¯›žý½îÐ61
posted @ 2009-07-24 11:47  肚肚  阅读(238)  评论(0编辑  收藏  举报