原创  动网论坛缓存技术研究 收藏

     动网论坛算是国内比较著名的一款论坛.从DVBBS7.0版本开始,动网重构了代码核心,采用缓存机制,程序和模板完全分离,在代码质量,权限等级,用户体验方面算是一个里程碑的作品了.虽然从7.0到现在的8.2,每一个阶段动网论坛都爆出了较大的漏洞(主要是上传和注入漏洞,7.0的欺骗上传漏洞使动网一度瘫痪),抛开这些,动网确实是一个不错的软件作品,我们可以从中学习到很多编程处理技巧.纵观现在市场上的一些web软件产品,都或多或少带有动网代码的影子.
     由于工作上的需要,我于2005年开始研究动网7.0的代码,长时间的接触使我一度对代码熟悉起来,可以肯定的说,现在我对动网7.0的代码熟悉程度在95%以上.动网7.0最大的特点就是采用独有的缓存机制,这种缓存机制到现在还在被很多web产品在使用.下面我谈谈动网的缓存机制的理解.
     缓存就是将用户频繁访问的数据放到内存中,二次访问时从内存中读取,而不需要频繁的访问硬盘,加快了数据处理速度,同时也减轻了服务器的访问压力. 那么什么样的情况下使用缓存呢?使用缓存首要的原则就是数据对所有用户来说具有共享性,说得具体点,就是每个用户浏览的页面结果都是一样的,不会是用户甲浏览的是一个页面结果,其他用户浏览又是一个结果.相同的效果才能使数据对所有用户具有较好的共享.当然,不同用户浏览出现不同效果也可以用缓存,但是这种缓存会使服务器具有内存崩溃掉,不推荐使用.举个简单的例子.网站首页的新闻头条,这些新闻总是从数据库中提取最新的几条,对所用用户,其显示结果都是一样的,所有用户浏览都是相同的结果,因此可以使用缓存.如果查询新闻信息,不同的用户查询可以查询到不同的信息结果,这里就不推荐使用缓存.
     动网采用如下几种缓存:
     1.数组缓存,采用getrows函数,将数据表字段置入一个二维数组中,通过数组下标遍历调用.如下面实例代码:
  Set Rs=Dvbbs.Execute("Select
        TopicID,boardid,title,postusername,postuserid,dateandtime,child,hits,votetotal,lastpost,lastposttime,istop,isvote,is
best,locktopic,Expression,TopicMode,Mode from dv_topic Where istop>0 and TopicID in ("&Forum_AllTopNum&") Order By istop desc, Lastposttime Desc")'执行SQL语句
   If Rs.Eof And Rs.Bof Then'执行不成功时,Forum_AllTopNum = 0
      Forum_AllTopNum = 0
   Else
    SQL=Rs.GetRows(-1)'就是这里,这里最关键,将以上数据表字段置入二维数组中
    For i=0 To Ubound(SQL,2)'数组下标遍历
      Showtitle=SQL(2,i)'赋值            
    Next
    End If
    2.调用缓存类,这个缓存类代码非常精简,非常不错.
    具体解释如下
'下面是dvbbs缓存模块,从dvbbs7.0开始,动网开始使用缓存机制,到SQL8.2版本,动网缓存机制使用更上一层楼,结合Application,采
用xml缓存.个人认为,动网的缓存机制值得学习
Public Property Let Name(ByVal vNewValue)'定义Name属性,此处Name属性主要是缓存Name,不同的缓存有不同的Name,此
Name是自定义的.个人认为,Name主要起一个标记作用,因为缓存数量过多的话,主要是靠Name来识别.Name就像房间号一样,对号入座.
  LocalCacheName = LCase(vNewValue)'将传递参数转换成小写
End Property

Public Property Let Value(ByVal vNewValue)'定义Value属性,此处Value属性是指写入缓存的Value,后面着重介绍
  If LocalCacheName<>"" Then'LocalCacheName不为空
   Application.Lock'锁定,其他人无法写入
   Application(CacheName & "_" & LocalCacheName &"_-time")=Now()'将now函数写入Application中
   Application(CacheName & "_" & LocalCacheName) = vNewValue'将参数vNewValue写入Application中
   Application.unLock'解锁,共享缓存
  End If
End Property

Public Property Get Value()'定义一个类的属性,该属性返回Value的值
  If LocalCacheName<>"" Then  '判断条件,也就是缓存名不为空,每一个缓存有一个Name值,才能执行下面语句
    Value=Application(CacheName & "_" & LocalCacheName)'将缓存的内容赋给Value
  End If
End Property
Public Function ObjIsEmpty()'此函数是判断缓存对象是否过期,有一个返回值ObjIsEmpty,初始值为False
  'Response.Write DateDiff("s",CDate(Application(CacheName & "_" & LocalCacheName &"_-time")),Now())&"
秒"
  ObjIsEmpty=False'返回值初始化
  If  IsDate(Application(CacheName & "_" & LocalCacheName &"_-time")) Then'用IsDate函数判断缓存中的
now()是否转换成日期
   If DateDiff("s",CDate(Application(CacheName & "_" & LocalCacheName &"_-time")),Now()) >
(60*Reloadtime) Then ObjIsEmpty=True'缓存是否过期判断,主要是和Now进行比较,得到一个返回值
  Else
   ObjIsEmpty=True
  End If
  If ObjIsEmpty Then RemoveCache()'过期了,是否缓存
End Function

Public Sub RemoveCache()'是否缓存对象
  Application.Lock'锁定
  Application.Contents.Remove(CacheName & "_" & LocalCacheName)'释放缓存数据
  Application.Contents.Remove(CacheName & "_" & LocalCacheName &"_-time")'释放缓存的时间
  Application.unLock'解锁
End Sub
3.xml缓存机制
这种xml机制在7.1以后版本中用到,通过建立xml节点,并将这些节点共享.具体要参考xpath语法.
首先是建立一个xml根节点,并通过缓存类将这个根节点缓存起来,根节点建好后再建立根结点的子节点,建立好字节点后,通过执行SQL语句对子节点赋值(赋值还是数据表的字段内容),那么就可以在其他页面重复使用数据表中字段值了.举个例子:
Set UserSession = RecordsetToxml(rs,"userinfo","xml")'建立根结点,这里用函数封装了
  UserSession.documentElement.selectSingleNode("userinfo/@cometime").text=Now()'赋值
  UserSession.documentElement.selectSingleNode("userinfo/@activetime").text=DateAdd("s",-3600,Now())'时间赋值
  UserSession.documentElement.selectSingleNode("userinfo/@boardid").text=boardid
说明一下,为什么要将用户所有字段内容缓存起来呢?因为缓存起来后将用户资料置入内存中,下次需要取出用户资料时直接从内存中读取,不需要读取数据库,减轻了数据库的压力. 因为涉及倒用户资料的页面太多了,如果每个页面的用户资料都从数据库中读取,频繁的反问会造成IIS假死.
下面说说RecordsetToxml函数的作用,该函数建立后,整个论坛都在调用,重用性很强.
Public Function RecordsetToxml(Recordset,row,xmlroot)'row是行节点,xmlroot是根节点
  Dim i,node,rs,j,DataArray'定义变量
  If xmlroot="" Then xmlroot="xml"'xml为空时,xmlroot设置默认值,这里为xml
  If row="" Then row="row"'同上
  Set RecordsetToxml=Dvbbs.CreateXmlDoc("msxml2.FreeThreadedDOMDocument"& MsxmlVersion)'创建自由线程XMlDOM对象,这种格式是固定的,不用怕,呵呵
  RecordsetToxml.appendChild(RecordsetToxml.createElement(xmlroot))'创建根节点了
  If Not Recordset.EOF Then'布尔值判断,此处是为真,能执行通过的话,执行如下语句
   DataArray=Recordset.GetRows(-1)'哈哈,这里又用到缓存数组
   For i=0 To UBound(DataArray,2)'缓存数组下标遍历
    Set Node=RecordsetToxml.createNode(1,row,"")'建立行节点,什么是行节点,这要看看xml格式,呵呵,一看就知道了
    j=0
    For Each rs in Recordset.Fields
       node.attributes.setNamedItem(RecordsetToxml.createNode(2,LCase(rs.name),"")).text= DataArray(j,i)& ""'这里是建立根节点的子节点
       j=j+1'循环
    Next
    RecordsetToxml.documentElement.appendChild(Node)'子节点赋值
   Next
  End If
  DataArray=Null
End Function
当然,缓存要合理利用,因为它是牺牲服务器的内存为代价,过多的使用缓存会造成内存大量消耗. 缓存每隔一段时间要释放出来,一般有自动释放和手动释放.自动释放是到了一定时间,程序自动释放被占用的内存.手动释放需要在后台进行管理.

发表于 @ 2009年04月22日 21:40:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:利用COM组件对于B/S中分页的实现 | 新一篇:MVC设计模式在asp.net用户登陆中设计和运用

  • 发表评论
  • 评论内容:
  •  
Copyright © ice241018
Powered by CSDN Blog