技巧:用ETag产生更为有效的304 Not Modified

对于大流量的网站, 尽可能使用304 Not Modified, 这将大大节约带宽的使用,也节省你的资金投入.
通常,对于静态文件, 一般都无须配置, 多数的http server能够产生有效的last-modifed-time和Expires header,
同时下次请求的时候自动判断并输出304.
但是对于动态输出的页面, 依赖时间戳并不是一个有效的方式. 很多时候, 我们需要频繁更新文件,但是
文件内容并没有改变. 这是就要用到ETag了.

关于ETag即Entity Tag是W3C在HTTP 1.1中定义的. 对于动态输出,我们可以使用md5值作为etag的值, 这样
可以当内容没有变更,那么就可以直接返回304了.

结合MongoDB的GridFS,ETag更加有用. 当文件被存储到db中的时候, 计算文件的md5值,保存. 当输出gridfs中的文件时候,
同时输出日期和etag.
以下是一个简单的PHP类, 用于检查是否刷新, 这个类首先检查过期时间, 同时检查etag, 并且etag的优先级高于日期检查:

/**
* Cache Validator
*
* A helper class to mainipulate/validate cache.
*
* The code is ported from my oooold project(eps2004),but works!
* @author night
*/
class CZone_Core_Util_HttpCacheValidator {
/**
* Validate client headers and check wheather to send 304 header.
*
* @param int $lastModified timestamp to validate
* @param string $tag ETag to validate
*/
public static function is_expired($lastModified,$tag,$headers){
// $now = time();
// $headers = getallheaders();
$refresh=TRUE;
if(isset($headers["If-Modified-Since"])) {
$arraySince = explode(";", $headers["If-Modified-Since"]);
$since = strtotime($arraySince[0]);
if($since >= $lastModified) $refresh=FALSE;
}
/**
* Check Entity Tag(ETag)
*
* Entity tags are used for comparing two or more entities from the same requested resource.
* HTTP/1.1 uses entity tags in the ETag (section 14.19), If-Match (section 14.24),
* If-None-Match (section 14.26), and If-Range (section 14.27) header
* fields. The definition of how they are used and compared as cache
* validators is in section 13.3.3. An entity tag consists of an opaque
* quoted string, possibly prefixed by a weakness indicator.
*
* entity-tag = [ weak ]
* opaque-tag weak = “W/”
* opaque-tag =quoted-string
*
* A “strong entity tag” MAY be shared by two entities of
* a resource only if they are equivalent by octet equality. A “weak
* entity tag,” indicated by the “W/” prefix, MAY be shared by two
* entities of a resource only if the entities are equivalent and could
* be substituted for each other with no significant change in
* semantics. A weak entity tag can only be used for weak comparison.An
* entity tag MUST be unique across all versions of all entities
* associated with a particular resource. A given entity tag value MAY
* be used for entities obtained by requests on different URIs. The use
* of the same entity tag value in conjunction with entities obtained by
* requests on different URIs does not imply the equivalence of those
* entities.
*
* See HTTP/1.1(W3C)
*
*/
if(isset($headers["If-None-Match"])) { // check ETag
if(strcmp($headers["If-None-Match"], $tag) == 0 ){
$refresh=FALSE;
}
else {
$refresh=TRUE;
}

}
if(isset($headers["If-Match"])) { // check ETag
if(strcmp($headers["If-Match"], $tag) == 0 )
$refresh=FALSE;
else
$refresh=TRUE;
}
//firefox style,resume download
if(isset($headers["If-Range"])) { // check ETag
if(strcmp($headers["If-Range"], $tag) == 0 )
$refresh=FALSE;
else
$refresh=TRUE;
}
return $refresh;
}
?>

Comments

2 Responses to “技巧:用ETag产生更为有效的304 Not Modified”

  1. 巴别塔上的雇工 on January 12th, 2010 10:39 am

    应该尽量避免使用ETag,其实ETag真的没什么必须用的情况。
    为了更好的网站性能,应该有意地删除ETag Header
    请参考http://developer.yahoo.com/performance/rules.html

  2. nightsailer on January 13th, 2010 1:53 am

    所谓的YSlow的这些规则并非要全部照搬,某些rule也是有争议和使用场合的,如果你比较现在的版本和早期的版本也会发现有很多不同.reduce dns,Reduce Cookie Size,不理解很容易就背道而驰

    所以首先要明白为什么?你是否认真读懂原文的意思.

    ETag如果无法有效的产生,那么自然会出现问题. 所以所谓减少ETag其实是针对static文件, 因为某些服务器的ETag生成算法并无法保证
    文件的一致性. 像常用的文件的inode-size-timestamp的变化来计算etag,必然导致文中所说, 当文件在集群中,
    生成的etag值不一致的情况.

    这些和我所说的是完全不同的,我说的ETag的使用恰恰是针对动态内容的! 使用动态文件内容的HASH值作为ETag,只要
    文件不产生变化,那么所生成ETag会保持一致, 这反而比使用修改时间更为有效!

    因此,是否使用ETag, 首要条件就是能否选择一个合适的生成算法, 如果是,那么效果就是显而易见的.

Leave a Reply