CSDN因为访问量巨大,所以采用了文件缓存机制,即将帖子内容生成XML文件,再在客户端有XSLT解析,确实可以很大的减轻数据库压力。于是我也想了想,如果采用这种文件缓存的方式来增强论坛、Blog的性能,减轻数据库压力,要注意哪些问题呢?于是仔细想了想一些可能的问题和如何来解决:
在这之前我先对本文的术语作一个简单解释:第一篇帖子和它的所有回复都称之为一个主题(Thread),第一篇帖子和它后面的每篇回复都叫做帖子(Post)。另外一个前提是本文中,类似于CSDN的情况:即XML文件缓存是辅助的,在数据库中同时存有一份数据,访问时先访问数据库获取关键信息,然后在显示帖子内容时读取XML文件。
缓存的单位:CSDN的缓存是以一个Thread为单位,将Thread下所有的Posts都生成一个XML,这样一个好处就是生成的文件比较少,缺点就是文件更新较频繁(每有回帖就必须更新一下文件),不方便支持帖子修改/删除功能,客户端下载时需要整个XML下载完才能显示出HTML。我个人倾向于以Post为单位,每个Post生成一个XML,这样做的缺点是生成的文件较多,优点是调用更加灵活,不用频繁更新XML,在客户端解析时可以支持单个Post加载完就显示。
缓存的时机:生成缓存文件主要有两种时机——帖子创建时即生成XML、第一次访问帖子时生成XML。我个人倾向于第二种方案:有利于多台服务器负载均衡,如果缓存文件丢失,可以方便再次生成。CSDN好像是采用的在有新帖子创建时生成/修改XML,不确认。
支持帖子修改:CSDN是不支持帖子修改和删除的(版主应该可以吧),具体原因我不清楚,如果他们的缓存文件时在帖子创建时生成和更新,那么可能是不想频繁更新文件,如果是第一次访问时,那么需要有一个时间戳来记录帖子是不是修改了,如果修改了那么需要更新XML。基于前面两个问题我选择的方案,在第一次访问时生成缓存的XML文件,并且以Post为单位生成XML文件,那么当帖子修改后,我在下次访问时需要更新这个XML文件,而不是访问旧的XML文件。
那么怎么样才能在每次访问时知道帖子更新了呢?如果是帖子修改时就直接更新XML文件当然简单,但是现在不是这样,那么是不是每次先服务器端读取一遍XML,然后对比一下XML中的帖子最后更新时间和数据库中的帖子最后更新时间是不是一致的,如果不是就表示需要更新XML了,这样当然可行,但是效率太低了,不过这倒是给了我一点启发:为什么不直接以帖子最后更新时间(精确到秒)命名,这样在找缓存文件时,如果帖子更新了,而缓存文件没有更新,那么就会找不到缓存文件,再次生成就好了。这样有个问题是会导致过期的文件还存在,倒不见得是坏事情——相当于对每个帖子都有个历史存档了。
权限问题:如果XML路径是很有规律的,并且可以被直接访问到,那么就没有什么权限和隐私可言了,一种比较直接有效的方法,就是通过HttpHandler来隐藏缓存的XML文件的真实路径,并且判断用户是否有权限看到。如果你担心XML文件路径可能被猜出来,那么可以将缓存文件禁止直接外部访问,或者对上面提到的XML文件命名方案进行改进:对于Post增加一个属性——缓存文件名,第一次创建时生成一个随机数作为文件名,如果后来帖子有修改,同时更新这个属性。
文件保存路径:因为每个Post一个文件这种方案会让文件很多,如果一个目录下文件太多,检索效率会非常低的,那么应该对文件进行分区,分成四级目录:第一级将帖子文件按照所属板块(Forum)分类,例如按照板块的ID生成目录;第二级按照Thread的日期来分类,这个可以根据帖子量来决定日期划分,如果帖子量巨大甚至可以每天一个目录;第三级按照Thread来分类,例如按照Thread.ThreadId来生成目录,第四级按照Post来分类,因为每个Post每更新一次就会生成一个新的XML,参考命名方式:
string cacheFilePath = string.Format("Forum{0}\\{1}\\{2}\\Thread{3}\\Post{4}\\{5}.xml",?post.ForumId, post.ThreadDate.ToString("yyyy"), post.ThreadDate.ToString("MM"), post.ThreadId, post.PostId, post.CacheFileName);
换皮肤问题:XSLT本身就可以很方便的实现换肤功能——每种皮肤一个XSLT文件即可,不过这样的一个问题就是需要对XSLT比较熟悉。如果要在传统的论坛换皮肤基础上实现这个功能,可以考虑一个折中的方案:将每个缓存后的Post的XML作为XML数据岛嵌入在帖子显示的位置,本身IE对XML数据岛就支持非常好,即使是FireFox之类不支持XML数据岛的浏览器(貌似是不支持的),也可以结合XSLT来做,这个XSLT只要解析显示帖子内容这部分就好了,相对难度小很多。
实际上这也是我对文件缓存的一些不成熟想法。同时推荐一篇相关文章:Disk-based Output Caching Module
打印 | 张贴于 2005-12-18 01:46:00 | Tag:Web技术
目前致力于
留言反馈
------------------------------------------------
如果命名使用 唯一名称+最有生成时间 命名
那么就能得出该xml缓存是否为新的xml,是否需要到数据库中重新读取,
CSDN完全应该用html而不是xml,现在错得回不了头了。
每次提交,就打开文件,readToEnd,然后,把新加的那点内容replace进去。都不用存回复的数据。做全文本检索就可以了。
Sheva
对于开发人员来说只需要实现动态的叶面即可。用户前端访问的是cache server,由cache server根据需要去抓取后端的动态脚本来反馈给用户最新的内容。
如果内容发生了变化,只需要发送xml-rpc请求给cache server.
换皮肤问题:动xsl文件不一定要懂xsl呢~里面真正的实现部分还是html和css的。
csdn的问题不是xslt用的怎么样,是那哥们用的不是xslt的正式版本,而是xslt草案。firefox支持的是正式标准。不知道开发的人怎么想的。在正式版已经推出2年之后,竟然用一份草案来作开发。
另外就是,我看不出来csdn论坛有用xml+xslt的必要。xml是为了描述数据,他有什么可描述的?生成html足够了,适应范围还广。
总之,从开始就错了,所以越错越离谱。
但是 web服务器
不是一样的 多了 承受IO操作......
"项目成员有几个人会XML/XSL。这是个很现实的问题,要就你一个人会,那你完蛋了.. "
这个也不用吧 XML/XSL又不是啥高深的东西 是个人花一点点时间就能用了
活活
.net 1.1 CacheDependency
文件依賴項是通過使用 Cache.Insert 并提供引用 XML 文件的 CacheDependency 對象添加的。
Cache.Insert("MyData", Source,
new CacheDependency(Server.MapPath("authors.xml")));
最起码www.asp.net没有使用文件缓存,记得他们出过一个案例PPT,讲了怎么怎么实现的。反正他们的访问量应该比CSDN大吧
假设一个帖子占内存100k(目前的XML平均不会超过这个大小),一共100个版面
现在每个版面有25条帖子,假设需要缓存前两页的帖子(很少会有人往后面翻的)
100k * 100 * 25 * 2 = 500M
尚可接受吧。w3wp也是要到内存占用大于物理内存某个比率时才会回收内存的,程学设计良好的话不会是经常性的。
文件缓存需要很多额外的代码来实现,我就是觉得花人工成本来对付这种复杂性还不如直接买内存来的划算
缓存不是万能的:像帖子数据这种放在内存中是巨占内存的,你内存有多少可用?4G?.Net有800mb内存限制的。还有就是IIS的应用程序池会经常性回收,一回收缓存就没有了。
FF是支持XSLT但不支持XML DOM
FF对XSLT的支持和IE6还是有细微的差别,我没研究过W3C关于XSLT的标准,但w3school上面说IE6是第一个符合W3C XSL 1.0标准的浏览器 :-)
用XSLT前要先问自己这几个问题:
1、是否精通XSLT,因为它不像HTML那样有很好的容错性,少写一个关闭标签也会导致整个页面无法显示
2、是否要考虑浏览器兼容性。为IE6开发的、符合XSLT 1.0规范的XSL并不能在IE5上顺利运行。FF和Opera等就更不用说了
3、项目成员有几个人会XML/XSL。这是个很现实的问题,要就你一个人会,那你完蛋了..
4、对开发成本的影响有多少。据我所知,还没有一个特别好的XSL设计器来支持这种开发模式。相对于ASP.NET WebControl/WebPart的方式,成本怕是要高很多了
我觉得CSDN的这个论坛用ASP.NET页面缓存就足够了,可以买很多内存,比如说4G,也不会花多少钱。要还不够,可以做IIS Load Balance (Web Farm)