打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
如何分离个人信息,缓存动态页面

肖理达 (KrazyNio AT hotmail.com), 2005.06.07, 转载请注明出处

一直想写一篇关于动态页面 cache 的文章,但每次“提笔”却又放弃,因为总是觉得准备得还不够充分。今天埋头写下,只是希望对自己的工作做一些笔录。

1、问题起源

我们经常会在一个动态页面中加入很多个人信息,以 CMS 首页为例,用户登录之前显示登录框,登录之后显示其用户名,并根据权限显示其可用模块的链接。由于每个用户登录之后,显示出来的动态信息都是不一样的,所以这部分无法进行 cache,我们将这部分信息定义为“[u]个人信息[/u]”,它的特性是根据登录用户进行动态改变。

现在问题来了,就是一个 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 部分,直接运行相关代码,我把这种方法称为“[u]选择性 cache[/u]”。这种方法中,我们牺牲了一部分可 cache 的情况,但很大程度上提高了个人信息的可扩充性,也就是说个人信息的多少、显示的位置等等都不再受到限制,这是值得的,毕竟修改服务端代码要比修改大量的 JavaScript 代码要来得方便,而且 JavaScript 的调试也会比较麻烦。

分析一下这种 cache 方式,概要流程图如下:

 

 

 

代码片段如下:

<?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 则是由客户端往服务器发送的头,其工作原理图如下:

 

 


可以看到,再次请求本地存在的 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 源代码

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
使用浏览器缓存来加快站点的访问速度
apache PHP如何利用HTTP缓存协议原理解析及应用指南
基于资源的HTTP Cache的实现介绍
浏览器缓存机制 - Learning Correcting Improving - Jav...
php-- 避免表单的重复提交
介绍缓存的基本概念和常用的缓存技术
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服