Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

动态页也使用LastModified缓存

  LastModified和ETag的工作原来基本相同,不同之处是LastModified的标识符是一个时间,而ETag的标识符是任意的。其实ETag可以取代LastModified,不过杀鸡何必用牛刀呢?如果只是判断一个文件的最后修改时间,还是用LastModified比较标准,语义上也会比较通顺。在静态资源中使用LastModified是很容易的,只要把文件的最后修改时间取出就可以了。可是动态页的数据是从数据库加载的,如果使用程序文件的最后修改时间作为LastModified就无法在客户端同步最新的数据。如果是使用ETag缓存就不会有这个问题,因为ETag可以用程序将要输出到客户端的数据的MD5作为比对的标识符,这样数据改变时MD5也会改变。可是这就意味着程序必须执行一次,完全不能节省服务器资源,所以我才一直说ETag只能节省带宽。下面是一个ETag的例子 //连接数据库
$c=mysql_connect('127.0.0.1','******','******');

//查询(假如这里需要执行100次查询)
$s=mysql_query('select * from test.tab');
//记录集放入数组
$a=array();
while($i=mysql_fetch_array($s))$a[]=$i;
//数组转换成JSON
$output=json_encode($a);

//计算ETag
$ETag=md5($output);
//输出ETag
header('ETag:'.$ETag);
//判断页面是否改变
if($_SERVER['HTTP_IF_NONE_MATCH']==$ETag)
  //返回304
  header('HTTP/1.1 304 Not Modified');
else echo $output;//输出新数据

//关闭数据库连接
mysql_close($c);
  这个代码无论返回的是200还是304,每次请求都需要大量查询数据库,此时使用ETag就不会带来多大的好处。我们想要的效果是在页面程序全部运行之前就知道页面程序的输出结果,这就要从影响页面输出结果的条件入手。这个条件有两个,“数据”和“页面程序”。如果这两个条件都不变,输出结果一般也就不会改变。但是也有一些特殊情况,比如程序中有随机输出或动态时间的情况。这样即使程序和数据都不变也会输出不同的内容。不过这些情况都是可以避免的,比如随机内容可以使用AJAX加载、动态时间可以在前端计算。搞定这两种情况以后,剩下的就是只有数据或程序改变时候页面才会变化的情况。
  知道了这些,再来做LastModified缓存就简单了,我们只要获取数据库中与页面相关的表的更新时间和当前程序文件的更新时间,它们中最大的那个就是动态页面的LastModified了。而要在MYSQL中得到数据库的最后修改时间的方法很多,最简单的就是在information_schema这个系统数据库中的TABLES表中查询到。TABLES这张表是内存表,查询速度比MyISAM类型表的查询速度要快的多,不过它偶尔会缓存,所以查询结果有时会不准确。也许有些开发者对MYSQL的权限分配很忌讳,不愿意把系统表的查询权限交给程序。这也可以不必太担心,除了直接查询系统表之外还有很多安全的方法,比如使用存储过程封装查询、或者自己建一个表,使用触发器来更新最后修改的时间。总之,这个步骤就是八仙过海各显神通了。
  至于程序文件的修改时间就更容易了,PHP中有提供获取文件修改时间的函数filemtime。不过在PHP程序中通常会有大量的文件在互相引用着,比如一些头尾的独立文件header.php、footer.php。如果有必要,也可以加上这些文件的最后编辑时间判断。不过通常是没必要的,这些文件一般很少被修改。
  最后是一个简单的LastModified实例 //连接数据库
$c=mysql_connect('127.0.0.1','******','******');

//查询数据表的最后修改时间
$s=mysql_query("
  select max(UPDATE_TIME)
  from information_schema.TABLES
  where TABLE_SCHEMA='test' AND
        TABLE_NAME in ('tab1','tab2','tab3')
");
//取出记录集的第一行
$t=mysql_fetch_array($s);
//从数据库更新时间和程序文件更新时间中取最大值
$t=max(strtotime($t[0]),filemtime(__FILE__));
//输出Last-Modified头
header('Last-Modified:'.gmdate('M d Y H:i:s',$t).' GMT');
//判断客户端缓存的Last-Modified是否是最新的
if(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$t){
  //如果已经是最新的就直接返回304
  header('HTTP/1.1 304 Not Modified');
  mysql_close($c);//关闭数据库连接
  exit;//结束程序
};

//假设这个程序大量查询了tab1、tab2、tab3这三个表
//此处省略N行
echo 'OK';

//关闭数据库连接
mysql_close($c);
  这里为了方便直接使用了查询系统数据库的方法获取表的最后修改时间,由于它会缓存不能保证每次都能取到正确的数据,而且InnoDB类型的表是无法在information_schema.TABLES中查询到最后更新时间的。所以,在实际项目中不建议使用这个方法来获取最后更新时间。
网名:
34.203.213.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^