下面是一个用于php中的静态缓存加入动态个人信息的方案,我觉得他的思路是可以借鉴的,就转帖在这里了。
正文
****************************************************************************
转自:(http://nio.infor96.com/archives/283)
如何分离个人信息,缓存动态页面
肖理达 (KrazyNio AT hotmail.com), 2005.06.07, 转载请注明出处
一直想写一篇关于动态页面 cache 的文章,但每次?提笔?却又放弃,因为总是觉得准备得
还不够充分。今天埋头写下,只是希望对自己的工作做一些笔录。
1、问题起源
我们经常会在一个动态页面中加入很多个人信息,以 CMS 首页为例,用户登录之前显示登
录框,登录之后显示其用户名,并根据权限显示其可用模块的链接。由于每个用户登录之
后,显示出来的动态信息都是不一样的,所以这部分无法进行 cache,我们将这部分信息
定义为?个人信息?,它的特性是根据登录用户进行动态改变。
现在问题来了,就是一个 CMS 的首页,访问者的登录概率并不是百分百的,应该说有一大
部分人访问首页是没有登录的,这个时候的首页是一个公共的页面,没有任何个人信息,
或者说这时候首页的任何动态信息都是可以转换成静态的,也就是说这部分是可 cache 的
。
2、使用 JavaScript 分离个人信息
解决这个问题的方法有很多种,一种是将个人信息和其他信息进行分离,如在 CMS 首页中
加入一个外部的 JavaScript 文件,而这个文件的内容实际上是由 PHP 动态生成的。CMS
首页 index.php 代码片段:
<script language="JavaScript" src="/js/personal.php"></script>
?.
<script language=?JavaScript?>
document.write(sUser);
document.write(sLinks);
</script>
personal.php 代码片段:
<?php
session_start();
header(?Cache-Control: no-store, no-cache, must-revalidate?);
?>
sUser = ?<?php echo $_SESSION['USER']; ?>?;
sLinks = ?<?php echo addslashes($_SESSION['LINKS']); ?>?;
这种方法比较适合个人信息较少,易于集中显示的情况。通过 JavaScript 外挂代码实现
个人信息分离之后,personal.php 是永不 cache 的,这样也就可以放心地对 CMS 首页进
行 cache 了,具体的 cache 方法可采用 304 HTTP 头与 Cache_Lite 相结合的方式,这
在后边有详细代码示例。
3、选择性分离个人信息
一旦个人信息较多,很难对其进行分离的时候,上述方法实现起来就会比较麻烦了。接下
来介绍一种比较放宽的 cache 方式,就是只对用户未登录的 CMS 首页进行 cache,一旦
发现用户处于登录状态,则跳过 cache 部分,直接运行相关代码,我把这种方法称为?
选择性 cache?。这种方法中,我们牺牲了一部分可 cache 的情况,但很大程度上提
高了个人信息的可扩充性,也就是说个人信息的多少、显示的位置等等都不再受到限制,
这是值得的,毕竟修改服务端代码要比修改大量的 JavaScript 代码要来得方便,而且
JavaScript 的调试也会比较麻烦。
分析一下这种 cache 方式,概要流程图如下:
image
代码片段如下:
<?php
session_cache_limiter(?must-revalidate?);
session_start();
if (empty($_SESSION['USER'])) { //未登录时才使用 cache
?. //输出 cache
} else {
// 直接输出页面内容,此条件下与未使用 cache 时一样
$data =& get_data();
echo $data;
} //end if
?>
这里需要说明的一点是,由于 PHP 在使用 SESSION 时,php.ini 中默认设置的
session.cache_limiter 为 nocache,所以需要修改 cache_limiter,设置成
must-revalidate,使得客户端再次浏览当前页时必须发送相关 HTTP 头信息到服务器进行
验证,然后才决定是否加载客户端本地 cache。不要把客户端本地 cache 与服务器端
cache 搞混,之后的代码片段中会充分利用客户端 cache 和服务器端 cache 机制达到缓
存的目的。关于 must-revalidate,请参考 HTTP 规格说明书 RFC 2612 的 14.9.4 章节
。
上边的代码并不完整,接下来是更加深入的探讨客户端 cache 机制了,利用客户端 cache
,可以有效地减轻服务器端负载。首先了解一下 HTTP 头:Last-Modified 与
If-Modified-Since。简单的说,Last-Modified 与If-Modified-Since 都是用于记录页面
最后修改时间的 HTTP 头信息,只是 Last-Modified 是由服务器往客户端发送的 HTTP 头
,而 If-Modified-Since 则是由客户端往服务器发送的头,其工作原理图如下:
image
可以看到,再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 头将
先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进
行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的
内容,如果是最新的,则返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户
端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了
服务器的负担。想要详细查看 HTTP 头信息,可以在 Firefox 中安装 LiveHTTPHeaders
插件,安装完成之后按 Alt+L 就可以在 Sidebar 中看到了。
现在再来完善之前的 index.php 代码:
<?php
session_cache_limiter(?must-revalidate?);
session_start();
function &get_data()
{
//此函数用于获取本页面的输出内容
//?.
} //end function
if (empty($_SESSION['USER'])) { //未登录时才使用 cache
//====================================================
// 1. 检查 HTTP 头是否符合 304 的条件
//====================================================
//get_last_modified() 函数需要另外单独实现,此函数用于获取服务器端 cache 文件的最后修改时间,可将时间戳保存在数据库中。
$last_modified = get_last_modified();
$headers = getallheaders();
if (strtotime($headers['If-Modified-Since']) == $last_modified) {
// 返回 304 并结束程序运行
header('HTTP/1.1 304 Not Modified');
exit;
} //end if
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $last_modified) . ' GMT');
//====================================================
// 2. 检查 cache 文件是否存在
//====================================================
require_once 'Cache/Lite.php';
// 参数设置
$options = array(
'cacheDir' => './cache/',
'lifeTime' => 86400, //最大 cache 一天时间
'fileNameProtection' => false //使用 CMS 自身提供的 id 作为名字
);
$cache = new Cache_Lite($options); //创建 Cache_Lite 对象
$id = md5($_SERVER['REQUEST_URI']); //生成对应于 cache 文件的 ID
if ($data = $cache->get($id)) { //存在 cache 文件,获取内容,直接输出
echo $data;
} else {
$data =& get_data();
echo $data;
flush();
$cache->save($data); //保存 cache
} //end if
} else {
$data =& get_data();
echo $data;
} //end if
?>
测试时可以使用 LivHTTPHeaders 插件,你将会看到第一次访问时是返回 200,第二次到
第N次访问时则返回了 304,而登录之后,则一直都返回 200,因为我们选择性 cache 之
后,对登录之后一律运行程序输出,而不使用 cache,如果之后需要对输出的个人信息进
行修改,只需要改函数 get_data() 即可,也避免了 JavaScript 的调试。
总结
除此之外,还有其它方法可以实现分离个人信息,缓存动态页面的目的。而且为了提高服
务器运行效率,还可以使用数据库 cache、Squid 反向代理等,如 ADOdb 的 cache。目前
用的比较多的 Drupal 应用的就是本文中提到的第二种方法。
参考资料
HTTP Caching & Cache-Busting for Content Publishers
Hypertext Transfer Protocol ? HTTP/1.1
PHP Anthology, Volume 2: Applications. Chapter 5: Caching
Caching Tutorial for Web Authors and Webmasters
Drupal 源代码
Comments RSS | Trackback URL
4 Comments on ?关于 Cache(4)?
By zeal. June 13th, 2005 at 3:25 pm
我们目前采用的是oracle cache server以及squid。对于不能cache的内容采取分目录存放
。在代码设计的时候就考虑可cache性。对于大量需要用户交互的内容,似乎cache的作用
不大。
By Nio. June 13th, 2005 at 5:02 pm
嗯,如果内容本身全都是个人信息的话或者必须登录才能浏览的页面,就用不到这种cache
方法了,需要看登录与不登录的概率比而定 🙂
By nc. July 17th, 2006 at 12:43 pm
感谢你文章的提示,非常不错.
By ahu. October 8th, 2006 at 5:10 pm
zeal的公司貌似有些money~
**************************************************************************
转载完毕
另一段静态页面计数代码:
<script language="javascript" src="/data/include/count.php?id=$id"></
script>
|
|
转摘自:(http://www.mephp.com/view.php?tid=1&id=44)