Published on

HTTP协商缓存与资源标识符生成详解

Authors
  • Name
    Twitter

什么是协商缓存

协商缓存是HTTP缓存机制中的一种重要策略,它允许客户端和服务器之间通过某种标识符来判断资源是否发生了变化,从而决定是否需要重新下载资源。这种机制可以有效减少不必要的网络传输,提高Web应用的性能。

资源标识符的生成方法

1. 基于内容的ETag

这是最准确的资源标识符生成方式,通过对文件内容进行哈希运算来生成:

const crypto = require('crypto');
const fs = require('fs');

function generateETag(filePath) {
    // 读取文件内容
    const content = fs.readFileSync(filePath);
    
    // 使用 MD5 对文件内容进行哈希
    const hash = crypto.createHash('md5');
    hash.update(content);
    
    // 生成 ETag
    return `"${hash.digest('hex')}"`;
}

优点:

  • 准确反映内容变化
  • 不受文件修改时间影响
  • 适用于内容相同但修改时间不同的情况

缺点:

  • 需要读取整个文件内容
  • 计算哈希值有一定CPU开销

2. Last-Modified时间戳

基于文件的最后修改时间生成标识符:

function generateLastModified(filePath) {
    const stats = fs.statSync(filePath);
    return stats.mtime.toUTCString();
}

优点:

  • 生成简单,性能开销小
  • 不需要额外存储空间

缺点:

  • 精度只到秒级
  • 可能出现误判(时间变化但内容未变)

3. 弱ETag生成

结合文件大小和修改时间的混合策略:

function generateWeakETag(filePath) {
    const stats = fs.statSync(filePath);
    const size = stats.size;
    const mtime = stats.mtime.getTime();
    
    return `W/"${size}-${mtime}"`;
}

优点:

  • 性能开销适中
  • 比单纯使用Last-Modified更准确
  • 适合大文件场景

实践建议

  1. 选择合适的生成方式

    • 对于频繁变化的小文件,使用基于内容的ETag
    • 对于较大的静态资源,考虑使用弱ETag
    • 对于简单场景,Last-Modified可能就足够了
  2. HTTP响应头设置

const headers = {
    'ETag': generateETag(filePath),
    'Last-Modified': generateLastModified(filePath),
    'Cache-Control': 'no-cache' // 启用协商缓存
};
  1. 服务端验证逻辑
function validateCache(req, filePath) {
    const ifNoneMatch = req.headers['if-none-match'];
    const ifModifiedSince = req.headers['if-modified-since'];
    
    const currentETag = generateETag(filePath);
    const lastModified = generateLastModified(filePath);
    
    if (ifNoneMatch && ifNoneMatch === currentETag) {
        return 304; // Not Modified
    }
    
    if (ifModifiedSince && new Date(ifModifiedSince) >= new Date(lastModified)) {
        return 304; // Not Modified
    }
    
    return 200; // OK
}

总结

选择合适的资源标识符生成方法需要在性能、准确性和实现复杂度之间做权衡。在实际应用中,可以根据具体场景选择最适合的方案,甚至组合使用多种方法来获得最佳效果。

对于大多数Web应用来说,使用ETag或Last-Modified都可以显著提升性能。关键是要根据自己的业务场景选择合适的实现方式,并确保正确配置相关的HTTP头信息。

参考资源