随笔 - 49  文章 - 0 评论 - 207 trackbacks - 6
<2007年10月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

严正声明 : 本站文章转载请注明出处.

与我联系

搜索

 

留言簿(11)

我的标签

随笔分类

随笔档案

Atlas相关

积分与排名

  • 积分 - 55750
  • 排名 - 769

阅读排行榜

最近拜读了代振军同学写的关于Discuz!NT的缓存设计的一篇文章Discuz!NT 缓存设计简析 [原创],颇有些想法,姑且写在这里让大家拍砖吧。;)

缓存真是个好东西,在大型的系统中可以有效地提升系统的速度,此乃废话就不多说了,在.Net 平台下面我把缓存从功用大致分为两类,数据对象缓存和页面输出缓存. 对于数据缓存来讲是由System.Web.Caching.Cache这个类来实现,可以从上下文对象Context.Cache 来获取这个对象的引用.而页面/控件输出缓存则是由.Net环境在运行时依据头部的缓存申明来控制缓存策略. 本文主要论证与数据缓存相关的一些应用与问题.

文中代同学提到了"无法跨Web园共享数据的问题",虽然提到解决方案就是使用XML文件来存放缓存的键值,这里有一个疑惑,就是.Net的Web园既然是进程独立的又何来共享之说呢,真要是这样的话即便是通过XML文档写入缓存键值缓存的对象也不能同时在两个进程中共享,而这里获得的好处仅是避免了在其它的进程中读到了已在当前进程中失效的“脏”缓存数据,这样的话开几个Web园就会产生几个缓存的对象对系统资源的利用系就比较低了. 如果是用Web场布署的话浪费就更多了,也许是还少有论坛达到这样的规模故不在设计能力的范围中吧.CommunityServer也是使用了这个系统对象,并对它作了一些包装形成了CommunityServer.Components.CSCache这个类,还是不错的,可以在项目中选择使用.

基于这个类的应注实现还有EnterpriseLibrary的CacheBlock里面的NullBackingStore方式,但是为了满足多进程/服务器共程缓存数据的需求EntLib还提供了将SQL SERVER作为后端存储设备的方案,这样在性能要求不是太严,客户端连接不是太多的情况下也可以使用这种方式.只需要将EntLib 配置为共享数据库分区的工作方式即可,所有的CacheManager实例都有对缓存块的读写权,当然你也可配置为只允许一个实例写,其它的来读.

那么还有没有更好的办法呢,其实是有的. 不过我很奇怪在.Net平台下居然没有“原生态”的分布式缓存解决方案,也许是俺孤陋寡闻吧,有哪位达人知道的请分享。还好我们有Memcached这东西,它在PHP平台上已经取得了巨大的成功,是优秀的分布式缓存解决方案,可以参看这篇文章 , 大型的站点上应该必不可少吧.有举的同学可以去看看, 另外还想好一个思路, 就是在EntLib的基础上作扩展实现IBackingStore 接口从BaseBackingStore派生一个实现出来,再经由Remoting或者ICE这样的分布式中间件技术应该也可以实现的类似的功能吧.

用XML作为缓存键的存储方式倒是一个不错的想法,这样在批量移除缓存项的时候就不需要作扫描而直接得到相应的缓存键值,跟分布式缓存作一个整合应当是一个不错的方案。

好了,让我们再回头看看Discuz!NT在页面缓存上有些什么高招.

总的说来我是不怎么喜欢.Net2.0提供的页面输出缓存功能,主要是不能手动地控制页面缓存的过期,而使有缓存依赖项似乎也有点不爽。事实上使用数据绑定控件相对来说是比较消耗资源的,同样的数据我用StringBuilder直接拼出来输出速度要快不少,测试代码比较简单我这里就不给了,大家可以自己去测去,Discuz!NT在设计中也大量地采用了这样的方法(怪不得速度这么快呢;))。一般来讲模版被保存后后台会在aspx目录生成对应的页面文件, 比如你有一个页面,上面需要显示一个来访者的姓名,它的伪代码看起来可能是这个样子

模版文件内容show.html:
<html>
<body>
Hello, Your name is <% yourname %>
</body>
</html>
生成的文件 show.aspx
templateBuilder.AppendLine("<html>");
templateBuilder.AppendLine("<body>");
templateBuilder.AppendLine("Hello, Your name is " + this.yourname);
templateBuilder.AppendLine("</body>");
templateBuilder.AppendLine("</html>");
 
这里的this.yourname对应着相应页面后台类里面的一个属性,由程序在运行时进行初始化赋值,这样最后得到的页面执行结果就可以从这个templateBuilder对象的ToString()方法得到, templateBuilder也就是一个页面后台类里面的StringBuilder类的实例,最后在页面执行完毕后的OnLoad事件中根据不同的页面类型,如首页,频道首页,内容页等, 使用不同的缓存策略将页面执行结果的HTML代码插入到缓存中,下一个请求进来的时候在进入页面生命周期之前的HttpModule(这里面还包含地址重写功能代码)中判断这个缓存是否有效,直接从内存读取缓存发回客户端.这样速度当然就快了, 页面上看到的执行时间自然是0ms. 不过对于登陆用户来说由于要显示不同的登陆信息所以不能使用匿名的缓存文件版本,所以说一旦你登陆页面才会真正执行一次,但是上面要显示的数据都有独立的缓存项,所以仅仅是重新组装一次页面代码罢了,速度还是比较快的,在官方论坛上看到首页加载时间是15ms, 够快的了. 愚以为连这个时间其实也是可以再节省节省的. 比如用户登陆信息这部分东西可以生成一段JS, 在向浏览器发出了匿名用户的缓存版本时再判断如果用户登陆了就追加这样一段JS代码,在里面去把相应的HTML替换掉就可以了,也可以使用AJAX技术在客户端去取,这样就解决了已登陆用户和未登陆用户在共享缓存版本上的问题,至少在首页这一级是可以的吧,其它的主要页面不好说应该也差不多,俺对论坛程序的流程不是很了解.

从另一个角度讲已登陆用户不应该速度比匿名用户还慢吧~~~;)

HOHO,终于完成了,先写这些吧。聊点别的`~~总体上看俺觉得实用的就是最好的,不要说DNT的设计有多烂多烂,层次结构多不合理,代码多不OO,但是在这样一个特定的应用环境下它提供了一种相对灵活和高速的解决的方案,我觉得就是一个正确的选择。量体裁衣做出来的东西虽然可维护可重用性较差还是有它的好处的,俺自己也比较倾向于这种针对性很强的解决方案思路,希望DNT能做得更好~~~正如BlogEngine这样的程序,它几乎将很多的数据都Load到内存里面,数据存储默认的也是XML的存储方式,你很难去讲它是一个糟糕的程序,因为它有特定的应用场景,合适的才是最好的~~~~
 
本文基于DNT 2.0 RC1, 不当之处请大家斧正, 谢谢.





posted on 2007-10-29 00:27 沙加 阅读(3293) 评论(17)  编辑 收藏 所属分类: .NET相关

FeedBack:
#1楼  2007-10-29 00:43 我是老K [未注册用户]
这玩意还是少用吧,老老实实的把网站最大程度的静态化吧。。。。
  回复  引用    
#2楼  2007-10-29 07:52 怪怪      
网站静态化在很多情况下并不是一个良好的解决方案, 这个shooter.cn的站长曾经有过介绍.

另外DNT不是说它的结构和组装页面的过程一定是烂的, 而是实现本身也不雅观. "在这样一个特定的应用环境下它提供了一种相对灵活和高速的解决的方案"的前提下, 仍然可以实现的好得多..
  回复  引用  查看    
#3楼  2007-10-29 07:59 橘子&猪      
@我是老K
静态化维护是个最大的问题.
只要内存足够还是用缓存来的好,速度也快些,毕竟缓存是直接从内存加载的.
  回复  引用  查看    
缓存可以用更好地解决方案:memcached是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。
http://www.keyusoft.cn/memcached.ashx
  回复  引用  查看    
#5楼  2007-10-29 09:08 wingoo      
lz,我机子上缓存总丢失是怎么个回事呢??
  回复  引用  查看    
#6楼  2007-10-29 09:09 亚历山大同志      
内存数据库是好东西,基本上在程序上就不用管缓存,因为已经够快了:}
  回复  引用  查看    
#7楼  2007-10-29 09:31 Jeffrey Zhao      
memcache是好东西
mysql可以设内存表,在sql server中据说用全局的表变量也可以做到内存表——我没有用过,哪位有实际经验呢?
静态业不是万能的,对于门户应用静态页是常用的也是最有效的手段,不过对于像DNT这种应用,就几乎不可以用静态页。要么也非常麻烦——主要还在内容更新上。
  回复  引用  查看    
#8楼  2007-10-29 09:36 Jeffrey Zhao      
@wingoo
具体情况具体分析,比如看看是不是recycle了?
  回复  引用  查看    
个人基本不认同分布式缓存同步技术
多台服务器之间通过垮服务器去获取缓存,虽然达到了减少缓存在多服务器间的冗余以及数据同步,但却造成了数据之间传输的序列化和反序列化消耗,感觉已经失去了缓存提高性能的根本作用了。
大概只有在需要多服务器之间需要保持同步数据,但又不需要存数据库时,节约下数据库操作的系统损耗。但这种场景我实在想象不到多少。
  回复  引用    
#10楼  2007-10-29 10:54 Jeffrey Zhao      
@风行*深蓝
多台服务器分布式缓存还是很有效的,传输/序列化/反序列化相比于一些操作(例如某些数据库操作)还是很快的。另外,分布式缓存可以视作是二级缓存,比如CPU有也有多级缓存,有些方式方法是差不多的。
  回复  引用  查看    
#11楼  2007-10-29 11:43 大石头      
除非计算消耗非常大,否则我不赞成使用分布式缓存,组织这个缓存的代价可能比CPU再计算一次还要慢。
我们使用缓存,一般都是为了减轻IO、数据库、网络等的损耗,而这个分布式,明显增加了这些东西
  回复  引用  查看    
to Jeffrey Zhao:
我想你比较错了对象,你拿分布式同步缓存去和数据库操作进行比较……
那我拿缓存来干什么?平白增加了系统的复杂性,提高的性能却非常有限的话,我坚决反对这样的架构设计!

应该比较的是本地缓存和分布式的同步缓存两种技术而已,需要缓存数据多服务器一致的情况真的太少了。
  回复  引用    
#13楼  2007-10-29 13:29 Jeffrey Zhao      
@风行*深蓝
谁都知道本地缓存和分布式的同步缓存性能优劣,缓存不就是为了减小IO、数据库能操作的消耗吗?那么考虑是否需要分布式缓存,难道不应该和数据库等操作相比吗?分布式缓存既然比数据库性能好,自然可以使用。
还有,我说分布式缓存有用,并非是建议把InProc的缓存都用分布式来替换,还是要看合适不合适。不过有些情况下分布式缓存是很有用,尤其是在多台Server中数据要同步的情况下。
  回复  引用  查看    
#14楼  2007-10-29 13:35 daizhj      
呵呵,看来大家越来越关注我们的项目了,的确用关联文件的方式来同步已做出的修改不是什么太好的方法(无论是效率还是代码结构上),但这也是因为没有合适的"原生态"(用LZ的话说吧)支持造成的.
不过有一点请LZ放心,我一直没有放弃过寻找优秀方案的尝试,包括您所说的CacheBlock之类的东西(必定总在园子里逛所以会有新发现).一但时机成熟就会有所变动了,呵呵:)
  回复  引用  查看    
#15楼 [楼主] 2007-10-29 13:40 DarkAngle      
--引用--------------------------------------------------
wingoo: lz,我机子上缓存总丢失是怎么个回事呢??
--------------------------------------------------------
内存不够被垃圾回收了吧~~~
  回复  引用  查看    
#16楼  2007-10-29 19:25 flaigd [未注册用户]
呵呵,华而不实,重点的部分反而轻描淡写。
  回复  引用    
#17楼  2007-10-30 12:44 戏水      
惊闻此处有人提到memcached ,特来AD
请看 《分布式缓存系统Memcached简介与实践》
http://www.cnblogs.com/zjneter/archive/2007/07/19/822780.html

完毕闪人~~
  回复  引用  查看    

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: